connect before dirtying a transaction with a timeout - #45
Merged
maths22 merged 1 commit intoAug 11, 2026
Merged
Conversation
Setting an explicit timeout calls dirty!, which makes the transaction unrestorable. That disables Rails' lazy connect -- with_raw_connection only connects when reconnect_can_restore_state? is true -- so on a connection that had never been used, the first statement (BEGIN, issued by RealTransaction#materialize!) ran against a nil raw connection: NoMethodError: undefined method 'async_exec' for nil Connect before dirty! rather than after. reset_transaction only carries the transaction manager across a reconnect while it is still restorable, so connecting once the transaction is dirty silently discards it along with the timeout just set -- no BEGIN, no SET LOCAL, and the block runs with no timeout at all. The second spec covers that, since the crash alone is fixed by either ordering.
maths22
approved these changes
Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Setting an explicit timeout calls
dirty!, which makes the transaction unrestorable. That disables Rails' lazy connect —with_raw_connectiononly connects whenreconnect_can_restore_state?is true — so on a connection that has never been used, the first statement isBEGIN(issued byRealTransaction#materialize!) and it runs against a nil raw connection:Hit in production in Canvas, where
canvas_partmanwraps on-demand partition creation inwith_statement_timeoutand can land on a GuardRail role connection that the request has not otherwise touched. Applies to all threeTIMEOUTS, not juststatement_timeout.The ordering is load-bearing
Connecting after
dirty!also stops the crash, but is wrong.reset_transactiononly carries the transaction manager across a reconnect while it is stillrestorable?:Once the transaction is dirty that is false, so
connect!→verify!→reconnect!(restore_transactions: true)installs a fresh empty manager and never swaps back. The transaction is silently discarded along with the timeout just set — noBEGIN, noSET LOCAL, and the block runs with no timeout at all. That is why there are two specs: the first passes under either ordering, the second only under this one.Verification
mainwith the production error above.bin/rubocopclean.Follow-up
dirty! unless ::Rails.version < '7.1'is now dead — 93c38f5 raised the floor to Rails 7.2 but left that check and two more inspec_helper.rbbehind. Kept out of this PR so the fix stays reviewable; follow-up to come.🤖 Generated with Claude Code