Skip to content

Instantly share code, notes, and snippets.

@risicle
Created March 17, 2016 18:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save risicle/c09295bce42b173226d9 to your computer and use it in GitHub Desktop.
Save risicle/c09295bce42b173226d9 to your computer and use it in GitHub Desktop.
Django @retry_transaction decorator
from django.db import transaction, IntegrityError, DatabaseError
from functools import wraps
import logging
_transaction_logger = logging.getLogger("db.transactions")
def retry_transaction(attempts=2, on_failure=None, logger=_transaction_logger):
"""
Decorator for catching db errors and retrying the operation.
There are a few reasons a transaction may fail:
- Serialization errors or the like, which should of course be retried
- Many db errors have equivalent checks in django code - as db errors raised may well be caused by
something changing _after_ the django checks have been made but before the transaction has been
committed. Trying it again should at least give us the proper error.
Being careful to also enforce a maximum number of attempts lest something else is causing the DatabaseErrors
and we go into an infinite loop. django < 1.7 is stupid and doesn't give us access to the pgcode of
DatabaseErrors which would allow us to actually distinguish the serialization errors we're on the lookout
for from other DatabaseErrors so this is a possibility.
"""
def decorator(func):
def wrapping_function(*args, **kwargs):
attempts_left = attempts
while True:
try:
return func(*args, **kwargs)
except (transaction.TransactionManagementError, DatabaseError), e:
attempts_left -= 1
if callable(on_failure):
ret = on_failure(e, attempts_left)
try:
# allow an integer-convertable return value to override our attempts_left
attempts_left = int(ret)
except:
pass
if attempts_left > 0:
logger.warning("""Caught %r in retry_transaction decorator, retrying...""", e)
continue
else:
raise e
return wraps(func)(wrapping_function)
return decorator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment