Skip to content

fix(writer): commit loop reads the connection through the non-throwing path - #929

Open
markaddleman wants to merge 1 commit into
replikativ:mainfrom
markaddleman:fix/commit-loop-derefs-released-connection
Open

fix(writer): commit loop reads the connection through the non-throwing path#929
markaddleman wants to merge 1 commit into
replikativ:mainfrom
markaddleman:fix/commit-loop-derefs-released-connection

Conversation

@markaddleman

Copy link
Copy Markdown

Fixes #928.

Releasing a connection that has transacted raises Connection has been released. inside the commit go-loop. It never reaches the caller — go-try catches it and hands it to the global supervisor — so it appears only as delayed Supervisor: output on the 10 s stale timeout. That is why it reads as an intermittent flake rather than the deterministic thing it is; we had it filed downstream as a flake for months before measuring @(:pending-exceptions superv.async/S) directly.

connector/release marks the connection released (delete-connection!) before w/shutdown. Closing the queue unparks (<?- commit-queue), and the second recur argument then derefs an already-released connection.

@connection at that line was the only bare deref of the connection in the namespace — lines 58, 60, 66, 67, 276, 301, 315 and 328 all read through @(:wrapped-atom connection). This makes it consistent with them.

Measurement

Reproducer is in #928 — public API only, 20 iterations of create → connect → transact → snapshot → release → delete. Run on 0.8.1760, same JVM, patched source shadowing the released jar on the classpath so that only this token differs:

pending supervisor exceptions reads correct
as released 20 / 20 yes
patched 0 / 20 yes

With zero transactions it never fires either way, which is what identifies the commit loop specifically rather than the release path in general.

What I have not done

I have not run datahike's own test suite against this. The Java sources need the project's compile-java step and it did not build standalone in my environment, so I would rather say so than imply a green suite. The change is one token plus a comment; CI should be the judge.

The comment is there because the non-obvious spelling is the point — a later simplification back to @connection would silently reintroduce this. Happy to drop it if you would prefer the bare change.

Impact for consumers

Reads are unaffected: d/q runs over the db value and never re-enters deref-conn, so a consumer holding a snapshot sees correct results throughout. The cost is one tracked exception per released connection accumulating in a global supervisor, and stdout flooding on the stale timeout — which for a long-running process that opens and releases connections is a steady leak.

…g path

Releasing a connection that has transacted raises `Connection has been
released.` inside the commit go-loop. It never reaches the caller — `go-try`
catches it and hands it to the global supervisor — so it surfaces only as
delayed `Supervisor:` output on the stale timeout, which is why it reads as an
intermittent flake rather than the deterministic one it is.

`release` marks the connection released (`delete-connection!`) before
`w/shutdown`; closing the queue unparks `(<?- commit-queue)` and the second
recur argument then derefs an already-released connection.

`@connection` here was the only bare deref of the connection in this namespace
— every other read already goes through `@(:wrapped-atom connection)`.

Measured on 0.8.1760, same JVM, only this token differing, over 20 iterations
of create -> connect -> transact -> snapshot -> release -> delete:
pending supervisor exceptions 20/20 before, 0/20 after, reads correct in both.
With zero transactions it never fires either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant