Python was far and away my favorite, since we could take advantage of decorators to make everything relatively seamless.
Consider a function something like this:
def do_something_transactionally(db, *args, **kwargs):
tr = db.create_transaction()
# do some things with the transaction object
tr.commit().get()
This code was broken in a number of ways -- there's no retry, or handling of errors from commit, etc.So we exposed a decorator in Python to handle all of these issues for the common case:
@transactional
def do_something_transactionally(db, *args, **kwargs):
# do some things with the transaction object
This was more or less equivalent to: def do_something_transactionally(db, *args, **kwargs):
tr = db.create_transaction()
while True:
try:
# do some things with the transaction object
tr.commit().wait()
break
except Error as e:
tr.on_error(e).get()
It was actually even niftier than that, though, because the decorator allowed you to pass in either a transaction object or a database object. If the caller provided a transaction rather than a database, then the decorator did not create a new transaction or commit it; this allowed composition of more complicated transactional calls of decorated functions.So I will definitely grant you that if you encountered this code, you would need to understand what @transactional meant, but I would definitely argue that this was cleaner and easier to deal with than asking every function to explicitly implement the retry logic.