You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TestOutboxBroker dispatches publishes from rolled-back transactions #218
TestOutboxBroker ignores the caller's transaction. Every fake publish path, OutboxResponse included, stores and dispatches the row immediately, so a publish inside a transaction that later rolls back still runs the downstream handler. A test can't check "rollback means no message", and a chained row runs inside the parent handler before the parent acks.
One way: when the session is a real AsyncSession, hook after_commit and after_soft_rollback on its sync_session and defer the store and dispatch until commit. Keep immediate dispatch for mock sessions. This changes when handlers run in sync mode, and existing user tests may depend on the current timing.
Until then, docs/usage/testing.md should state the limitation and point to the integration tests.
Closing as won't fix. The behaviour is now documented, and making the fake transaction-aware costs more than it buys.
Confirmed:broker.publish / publisher.publish inside a transaction that rolls back still run their handlers under TestOutboxBroker, because the fake store ignores the session.
Why not fix it:
Few tests would benefit. Transaction awareness only matters for tests that pass a real session to TestOutboxBroker. Since 0.16.0 (feat!: TestOutboxBroker publishes through the real producer #238) the documented setup is fake_session(), a mock with no real transaction.
The handler has to run before commit() returns. That's what sync mode promises: assert straight after the commit. Doing it from an after_commit hook needs SQLAlchemy's semi-private sqlalchemy.util.await_only. I verified that it works:
on SQLAlchemy 2.0.0, 2.0.54 and 2.1.5, under every join_transaction_mode;
with begin and with autobegin;
with hook exceptions propagating to the commit() caller.
2.1 renames it await_ and keeps await_only only as a deprecated alias.
Savepoints add more work. A savepoint rollback (after_soft_rollback with nested) and a close without commit (after_transaction_end only) each need their own handling.
What's documented instead:docs/usage/testing.md (0.16.0) says the fake store ignores transactions, so a row published in a transaction that rolls back is still stored and delivered, and that rollback behaviour should be tested against a real database.
If this comes back: after #236 the change is confined to FakeOutboxClient.insert. It would buffer rows per transaction, deliver them on commit, and drop them on rollback or close, savepoints included.
Prior art:
Django's captureOnCommitCallbacks(execute=True) and Rails 5+'s after_commit in transactional tests both run commit callbacks in tests.
FastStream's own test brokers have no transaction concept.
Activity
lesnik512 commentedon Oct 17, 2026
Closing as won't fix. The behaviour is now documented, and making the fake transaction-aware costs more than it buys.
Confirmed:
broker.publish/publisher.publishinside a transaction that rolls back still run their handlers underTestOutboxBroker, because the fake store ignores the session.Why not fix it:
Few tests would benefit. Transaction awareness only matters for tests that pass a real session to
TestOutboxBroker. Since 0.16.0 (feat!: TestOutboxBroker publishes through the real producer #238) the documented setup isfake_session(), a mock with no real transaction.The handler has to run before
commit()returns. That's what sync mode promises: assert straight after the commit. Doing it from anafter_commithook needs SQLAlchemy's semi-privatesqlalchemy.util.await_only. I verified that it works:join_transaction_mode;beginand with autobegin;commit()caller.2.1 renames it
await_and keepsawait_onlyonly as a deprecated alias.Savepoints add more work. A savepoint rollback (
after_soft_rollbackwithnested) and a close without commit (after_transaction_endonly) each need their own handling.What's documented instead:
docs/usage/testing.md(0.16.0) says the fake store ignores transactions, so a row published in a transaction that rolls back is still stored and delivered, and that rollback behaviour should be tested against a real database.If this comes back: after #236 the change is confined to
FakeOutboxClient.insert. It would buffer rows per transaction, deliver them on commit, and drop them on rollback or close, savepoints included.Prior art:
captureOnCommitCallbacks(execute=True)and Rails 5+'safter_commitin transactional tests both run commit callbacks in tests.