Skip to content

Conversation

@meshy
Copy link
Collaborator

@meshy meshy commented Oct 9, 2025

Before now, exiting transaction would run all after-commit callbacks, on the assumption that they were enqueued inside the transaction. In tests this sometimes hid order-of-execution bugs, where pre-existing unhandled after-commit callbacks would get called, but at the wrong time.

When opening a transaction, we will now check for pre-existing unhandled after-commit callbacks, and raise an error when they're found.

Fixes #31

Alternative to #87

Copy link

@HamaBarhamou HamaBarhamou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks—this looks great! Two quick points:

  1. CHANGELOG / naming — Since this is tests-only, I’d either explicitly note “(tests only)” in the CHANGELOG entry or rename the flag to SUBATOMIC_TEST_CATCH_UNHANDLED_AFTER_COMMIT_CALLBACKS (and similarly SUBATOMIC_TEST_RUN_AFTER_COMMIT_CALLBACKS) to avoid ambiguity.

  2. Exception DX — Exposing the callables via callbacks is handy but can get noisy (closures, long reprs). Maybe keep the attribute for debugging, but tweak the exception message/__str__ to show just a count + actionable hint, e.g.
    Found 2 unhandled on_commit callbacks. Ensure they’re run before opening a transaction, or set SUBATOMIC_TEST_CATCH_UNHANDLED_AFTER_COMMIT_CALLBACKS=False to opt out in tests.

(Nit) A brief comment in _execute_on_commit_callbacks_in_tests explaining why we check both the entry state (only_in_testcase_transaction) and the exit state (_innermost_atomic_block_wraps_testcase) would help clarify the intent (avoid false positives if context changes between enter/exit).

@meshy meshy force-pushed the meshy/prevent-lingering-commits branch from 263f581 to 2382836 Compare December 5, 2025 14:41
@meshy
Copy link
Collaborator Author

meshy commented Dec 5, 2025

  1. CHANGELOG / naming — Since this is tests-only, I’d either explicitly note “(tests only)” in the CHANGELOG entry or rename the flag to SUBATOMIC_TEST_CATCH_UNHANDLED_AFTER_COMMIT_CALLBACKS (and similarly SUBATOMIC_TEST_RUN_AFTER_COMMIT_CALLBACKS) to avoid ambiguity.

Thanks for this -- I've renamed the flag to append _IN_TESTS (which is in line with the other similar setting), and make the "in tests" nature of the change more prominent in the changelog.

2. **Exception DX** — Exposing the callables via `callbacks` is handy but can get noisy (closures, long reprs). Maybe keep the attribute for debugging, but tweak the exception message/`__str__` to show just a count + actionable hint, e.g.
   `Found 2 unhandled on_commit callbacks. Ensure they’re run before opening a transaction, or set SUBATOMIC_TEST_CATCH_UNHANDLED_AFTER_COMMIT_CALLBACKS=False to opt out in tests.`

I'm going to keep it as-is for now. While explaining the meaning of the error is likely to be useful the first time it's encountered, I think the benefit of seeing the callbacks will be most useful long-term.

(Nit) A brief comment in _execute_on_commit_callbacks_in_tests explaining why we check both the entry state (only_in_testcase_transaction) and the exit state (_innermost_atomic_block_wraps_testcase) would help clarify the intent (avoid false positives if context changes between enter/exit).

You're right, but the comments here need further improvement anyway, so I'm going to leave that for a later refinement.

@meshy meshy marked this pull request as ready for review December 5, 2025 16:37
@meshy meshy requested a review from a team as a code owner December 5, 2025 16:37
Copy link
Collaborator

@samueljsb samueljsb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One nitpick about the changelog, but otherwise this looks like it's ready now!

Comment on lines +209 to +211
# Django's `atomic` leaves unhandled after-commit actions on exit.
with django_transaction.atomic():
db.run_after_commit(counter.increment)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice demonstration of the cause of this problem.

Before now, exiting `transaction` would run all after-commit callbacks,
on the assumption that they were enqueued inside the transaction.
In tests this sometimes hid order-of-execution bugs, where pre-existing
unhandled after-commit callbacks would get called, but at the wrong
time.

When opening a transaction, we will now check for pre-existing unhandled
after-commit callbacks, and raise an error when they're found.

Co-authored-by: Samuel Searles-Bryant <[email protected]>
@meshy meshy force-pushed the meshy/prevent-lingering-commits branch from e96eaf7 to 810aeb0 Compare December 10, 2025 14:06
@meshy meshy merged commit 8852067 into main Dec 10, 2025
7 checks passed
@meshy meshy deleted the meshy/prevent-lingering-commits branch December 10, 2025 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Raise error in tests if entering transaction and the testcase-transaction already has on-commit callbacks

5 participants