Repository navigation
Prevent lost settings updates and partial configuration writes - #17
Merged
Merged
Conversation
Serialize the full read-merge-persist with an asyncio.Lock held across asyncio.to_thread, and persist via a temp file in the same directory then os.replace, so concurrent subset POSTs cannot overwrite each other and a failed write preserves the last valid config. Add an overlapping-request test (verified to lose updates in 20/20 runs without the lock) and a failed-write test covering config preservation and temp-file cleanup, and document the single-process guarantee and multi-process limitation in the README and AGENTS.md. Closes #14
5 tasks
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (6 files)
Reviewed by efficient · Input: 32.3K · Output: 10.1K · Cached: 140.7K |
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.
Fixes #14.
What
my-plugin/routes.pyperformed an unlocked read–merge–write inasyncio.to_threadand wrote straight to<config_dir>/my-plugin.json. Concurrent subsetPOSTs could overwrite each other's keys, and an interrupted write could leave invalid JSON.asyncio.Lockis held across the entire read–merge–persist, so overlapping subset updates observe each other's changes instead of clobbering them. Body streaming and schema validation still happen before the lock is taken, and filesystem work still runs off the event loop viaasyncio.to_thread._write_settings_atomically()writes to atempfile.mkstemptemporary file in the same directory, flushes +fsyncs it, thenos.replaces it onto the destination. A failure at any point unlinks the temp file and leaves the previous configuration byte-identical (the request returns500).POSTs to different keys and asserts all three persist; a failed-write test breaksos.replaceand asserts the previous config survives unchanged with no leftover temp file. A sanity harness confirmed the overlapping test loses updates in 20/20 runs when the lock is disabled and passes with it.my-plugin/README.mdgains a "Concurrency and durability" section covering the guarantee, the multi-process limitation (per-process lock; cross-process locking needed for multiple workers sharing aconfig_dir), and the0600file mode thatmkstempimplies;AGENTS.mdandCHANGELOG.mdupdated to match.The test fixture now enters
TestClientas a context manager so every request shares one event loop — without that, the lock bound to the first request's per-request loop would be rejected by the second.Verification
pytest -q— 21 passed (includes the two new tests)node --check my-plugin/screen.js, print-call grep — all passbig-pickle(free) | 𝕏