fix(loops): lock the count+insert and the list read - #840
Merged
Conversation
Two pre-existing races in the loops routes, flagged by CodeRabbit on #839 (the verbatim extraction moved them unchanged from server.py, so they correctly weren't fixed there): 1. save_loop computed `COUNT(*)` OUTSIDE meta_db._lock, then inserted inside it. Two simultaneous unnamed POSTs read the same count and both mint "Loop N". Fix: one lock scope around COUNT + INSERT. 2. list_loops read the shared single connection (check_same_thread=False) with no lock, so it could overlap a POST/DELETE commit. Fix: read under the lock, like every writer. Low severity in context — FeedBack is single-user (Principle I), so concurrent unnamed-loop POSTs essentially can't happen — but each fix is one lock scope. tests/test_loops_concurrency.py pins both with a threading.Barrier that releases 16 workers into save_loop at once. Negative-checked: reverting the COUNT back outside the lock fails the uniqueness assertion 5/5 runs; the fix passes 3/3. pytest 2400 passed; on-device two unnamed POSTs -> ['Loop 1','Loop 2']. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughLoop database reads and writes now use the metadata database lock. Auto-generated names are counted and inserted within one locked transaction, with concurrency tests covering simultaneous saves and interleaved reads and writes. ChangesLoop concurrency handling
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This was referenced Jul 10, 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.
Follow-up to #839. Fixes two pre-existing races CodeRabbit flagged on the loops routes there — the verbatim extraction moved them unchanged from
server.py, so they correctly weren't fixed in the move-only PR (both are byte-identical to6c98aba:server.py).save_loopauto-naming was not atomic.COUNT(*)ran outsidemeta_db._lock, then the INSERT ran inside it. Two simultaneous unnamed POSTs read the same count and both mintLoop N. → COUNT + INSERT now share one lock scope.list_loopsread the shared connection unlocked. The singlecheck_same_thread=Falseconnection is serialized through_lockby every writer, so an unlocked SELECT can overlap a POST/DELETE commit. → read under the lock.Low severity in context — FeedBack is single-user (Principle I), so concurrent unnamed-loop POSTs essentially can't happen — but each fix is one lock scope, so it's worth doing.
Test
tests/test_loops_concurrency.pyreleases 16 workers intosave_loopat once via athreading.Barrierand asserts no two auto-named loops collide. Negative-checked: reverting theCOUNTback outside the lock fails the uniqueness assertion 5/5 runs; the fix passes 3/3. A second test hammerslist_loopsagainst 40 concurrent writes and asserts no error.pytest2400 passed; on-device, two unnamed POSTs →['Loop 1', 'Loop 2']. Codex 0 findings.Closes the two review threads on #839.
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests