fix: move-codex-session should wait out transient locks on its read-only connections #148
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/move-codex-session-read-only-busy-timeout"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Every read-only
DatabaseSyncinmove-codex-sessionhadnode:sqlite's default busy timeout of 0. A read that coincided with another connection rebuilding the WAL index failed at once withdatabase is locked(SQLITE_BUSY_RECOVERY, errcode 261) instead of letting SQLite retry. Another connection rebuilds the index when it is the first to open a WAL database, or checkpoints when it is the last to close one.Two consequences:
I found it through "keeps a destination writer's history rows when the history copy fails", which flaked with
database is lockedinstead ofUNIQUE constraint failed. All 12 captured stack traces failed on the first read of the read-only destination state connection.An
openReadOnly(path)helper now sets a 10 s busy timeout, matching the writers' 10 s and 30 s, and all 17 read-only opens use it. I rejected rewriting the test's poll to keep one connection open, because that would hide the bug.Under stress (16 CPU hogs, 24 parallel), 4 of 320 runs failed before and 0 of 320 after. In the diagnosis, the failure rate dropped from 1.25% to 0% on macOS and from 3.1% to 0% on Linux (Debian VM). A deterministic reproduction, where a parent holds the index-rebuild locks, failed 3 of 3 before and passed 3 of 3 after.
The new test "waits for the invoker's brief lock on a destination database" fails 3 of 3 without the fix. It holds the lock for 1 s, so it would also pass if the script took more than 1 s between opening the file and first reading it. The measured gap is about 10 ms.
This is not the cause of the older Linux-only "tolerates the invoker…" failure, which did not reproduce in 480 stressed runs.
🤖 Generated with Claude Code
move-codex-sessionshould wait out transient locks on its read-only connectionsReview
01M4JF6P98SPMK1YBAA6MX36PW— heada60b58af9a5828eecaa681d27bd4c4aeb1a2868dReview — j4k-oss/agent-skills @
cc41f460d4Scope: diff against base tree
5b1aa6fb3b4cStatus: dispatched — coverage complete (5/5 slots terminal)
Facts: current review-wide projection
Computed under:
Findings (0)
No findings survived.
Reviewed:
Other claims
01M4JF9YDB9G0XPDMAGG9GRY3Jlow — The lock test can pass without exercising the read timeout01M4JFA7GBQF8B21RD6Z4V4RXFlow — The lock test can pass without exercising the read timeoutCoverage
Coverage pass: 01M4JF728FKJW6AYS34417SF32
Accounting: complete
Slot health: healthy
5cb7dbe214a60b58af9aReview
01M4JF6P98SPMK1YBAA6MX36PWona60b58aleft two unadjudicated low claims, both titled "The lock test can pass without exercising the read timeout":01M4JF9YDB9G0XPDMAGG9GRY3Jand01M4JFA7GBQF8B21RD6Z4V4RXF. The service posted only titles, so I checked the one way I can see it happening.The test holds the destination state database's lock for 1 s after it first sees the script holding the file. It passes without the timeout only if the script takes more than 1 s between opening the database and first reading it. The test's comment and this PR's body already state that limit, and the gap measured about 10 ms.
With the timeout removed in a scratch copy, the test fails 3 of 3 on
a60b58a. With it, the test passes 3 of 3. I found no other way for it to pass without the timeout. A longer hold would only move the margin, and nothing outside SQLite shows when its busy handler starts waiting, so I'm leaving the test as it is and opening no follow-up.