diff --git a/bugsink/transaction.py b/bugsink/transaction.py index 23fda45..0be1a88 100644 --- a/bugsink/transaction.py +++ b/bugsink/transaction.py @@ -8,6 +8,8 @@ import threading from django.db import transaction as django_db_transaction from django.db import DEFAULT_DB_ALIAS +from snappea.settings import get_settings as get_snappea_settings + performance_logger = logging.getLogger("bugsink.performance.db") local_storage = threading.local() @@ -153,7 +155,7 @@ class ImmediateAtomic(SuperDurableAtomic): connection = django_db_transaction.get_connection(self.using) if hasattr(connection, "_start_transaction_under_autocommit"): - connection._start_transaction_under_autocommit_original = connection._start_transaction_under_autocommit + self._start_transaction_under_autocommit_original = connection._start_transaction_under_autocommit connection._start_transaction_under_autocommit = types.MethodType( _start_transaction_under_autocommit_patched, connection) @@ -183,9 +185,9 @@ class ImmediateAtomic(SuperDurableAtomic): performance_logger.info(f"{took * 1000:6.2f}ms IMMEDIATE transaction{using_clause}") connection = django_db_transaction.get_connection(self.using) - if hasattr(connection, "_start_transaction_under_autocommit"): - connection._start_transaction_under_autocommit = connection._start_transaction_under_autocommit_original - del connection._start_transaction_under_autocommit_original + if hasattr(self, "_start_transaction_under_autocommit_original"): + connection._start_transaction_under_autocommit = self._start_transaction_under_autocommit_original + del self._start_transaction_under_autocommit_original @contextmanager @@ -206,10 +208,21 @@ def immediate_atomic(using=None, savepoint=True, durable=True): else: immediate_atomic = ImmediateAtomic(using, savepoint, durable) - # https://stackoverflow.com/a/45681273/339144 provides some context on nesting context managers; and how to proceed - # if you want to do this with an arbitrary number of context managers. - with SemaphoreContext(using), immediate_atomic: - yield + if get_snappea_settings().TASK_ALWAYS_EAGER: + # In ALWAYS_EAGER mode we cannot use SemaphoreContext as the outermost context, because any delay_on_commit + # tasks that are triggered on __exit__ of the (in that case, inner) immediate_atomic, when themselves initiating + # a new task-with-transaction, will not be able to acquire the semaphore (it's not been released yet). + # Fundamentally the solution would be to push the "on commit" logic onto the outermost context, but that seems + # fragile (monkeypatching/heavy overriding) and since the whole SemaphoreContext is only needed as an extra + # guard against WAL growth (not something we care about in the non-production setup), we just simplify for that + # case. + with immediate_atomic: + yield + else: + # https://stackoverflow.com/a/45681273/339144 provides some context on nesting context managers; and how to + # proceed if you want to do this with an arbitrary number of context managers. + with SemaphoreContext(using), immediate_atomic: + yield def delay_on_commit(function, *args, **kwargs):