From 11cb807d058e53cf525af99c06d19b4e01b09b91 Mon Sep 17 00:00:00 2001 From: Kyle MacFarlane Date: Thu, 17 May 2012 12:56:29 +0100 Subject: [PATCH 1/3] Removed extra whitespace --- xact.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/xact.py b/xact.py index c8b5a49..b5d0a10 100644 --- a/xact.py +++ b/xact.py @@ -1,14 +1,14 @@ """ This code provides a decorator / context manager for transaction management in Django on PostgreSQL. It is intended as a replacement for the existing Django commit_on_success() function, and provides some nice features: - + * Nested transactions: The top-level transaction will be a BEGIN/COMMIT/ROLLBACK block; inner "transactions" are implemented as savepoints. * Commits even if is_dirty is False, eliminating the mistake of forgetting to set the dirty flag when doing database-modifying raw SQL. * Better interaction with pgPool II, if you're using it. * A workaround for a subtle but nasty bug in Django's transaction management. - + As currently implemented, it is NOT thread-safe as a decorator (it IS thread-safe as a context manager). Fix coming. @@ -25,7 +25,7 @@ class _Transaction(object): def __init__(self, using): self.using = using self.sid = None - + def __enter__(self): if connections[self.using].features.uses_savepoints: # We're already in a transaction; create a savepoint. @@ -33,7 +33,7 @@ def __enter__(self): else: transaction.enter_transaction_management(using=self.using) transaction.managed(True, using=self.using) - + def __exit__(self, exc_type, exc_value, traceback): if exc_value is None: # commit operation @@ -62,7 +62,7 @@ def __exit__(self, exc_type, exc_value, traceback): else: # Inner savepoint transaction.savepoint_rollback(self.sid, self.using) - + return False def _leave_transaction_management(self): @@ -71,10 +71,10 @@ def _leave_transaction_management(self): connections[self.using]._set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) # Patch for bug in Django's psycopg2 backend; see: # https://code.djangoproject.com/ticket/16047 - + # This is a great recipe for allowing a single object to handle both @ decorators # and with contexts. - + def __call__(self, func): @wraps(func) def inner(*args, **kwargs): From b4b1c612825742a3e056f61689c24441ca0d6d8a Mon Sep 17 00:00:00 2001 From: Kyle MacFarlane Date: Thu, 17 May 2012 12:59:04 +0100 Subject: [PATCH 2/3] Now thread safe --- xact.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/xact.py b/xact.py index b5d0a10..1bde994 100644 --- a/xact.py +++ b/xact.py @@ -19,25 +19,28 @@ from django.db import transaction, DEFAULT_DB_ALIAS, connections +from threading import local + import psycopg2.extensions class _Transaction(object): def __init__(self, using): self.using = using - self.sid = None + self._local = local() def __enter__(self): if connections[self.using].features.uses_savepoints: # We're already in a transaction; create a savepoint. - self.sid = transaction.savepoint(self.using) + self._local.sid = transaction.savepoint(self.using) else: + self._local.sid = None transaction.enter_transaction_management(using=self.using) transaction.managed(True, using=self.using) def __exit__(self, exc_type, exc_value, traceback): if exc_value is None: # commit operation - if self.sid is None: + if self._local.sid is None: # Outer transaction try: transaction.commit(self.using) @@ -49,19 +52,19 @@ def __exit__(self, exc_type, exc_value, traceback): else: # Inner savepoint try: - transaction.savepoint_commit(self.sid, self.using) + transaction.savepoint_commit(self._local.sid, self.using) except: - transaction.savepoint_rollback(self.sid, self.using) + transaction.savepoint_rollback(self._local.sid, self.using) raise else: # rollback operation - if self.sid is None: + if self._local.sid is None: # Outer transaction transaction.rollback(self.using) self._leave_transaction_management() else: # Inner savepoint - transaction.savepoint_rollback(self.sid, self.using) + transaction.savepoint_rollback(self._local.sid, self.using) return False From e3334dcb2cfe4d70129795c7b8ab5dfc7ab64b32 Mon Sep 17 00:00:00 2001 From: Kyle MacFarlane Date: Thu, 17 May 2012 13:00:28 +0100 Subject: [PATCH 3/3] Prevent chaos if autocommit is actually off --- xact.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xact.py b/xact.py index 1bde994..cc59175 100644 --- a/xact.py +++ b/xact.py @@ -17,10 +17,10 @@ from functools import wraps -from django.db import transaction, DEFAULT_DB_ALIAS, connections - from threading import local +from django.db import transaction, DEFAULT_DB_ALIAS, connections + import psycopg2.extensions class _Transaction(object): @@ -29,7 +29,7 @@ def __init__(self, using): self._local = local() def __enter__(self): - if connections[self.using].features.uses_savepoints: + if transaction.is_managed(self.using): # We're already in a transaction; create a savepoint. self._local.sid = transaction.savepoint(self.using) else: