Sequence setMetadata writes per key to fix flaky session config test#311989
Merged
roblourens merged 1 commit intomainfrom Apr 22, 2026
Merged
Sequence setMetadata writes per key to fix flaky session config test#311989roblourens merged 1 commit intomainfrom
roblourens merged 1 commit intomainfrom
Conversation
@vscode/sqlite3 runs in parallelized mode and dispatches db.run() calls to libuv's thread pool, so two setMetadata calls for the same key submitted close together can complete in the opposite order they were the later (and intended-final) value silently loses.submitted Surfaced by the 'Session Config persistence across restarts' integration test, which dispatches SessionConfigChanged shortly after createSession, then asserts the new branch is restored after a server restart. Repros in roughly 1-2 / 100 local runs under load; was a recurring CI flake. Fix: route setMetadata through a SequencerByKey<string> so writes for the same key run in submission order while writes for different keys still run concurrently. Same pattern already used for storeFileEdit. (Written by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses an intermittent CI failure (and a real production risk) where rapid consecutive setMetadata calls could be persisted out-of-order due to @vscode/sqlite3 running db.run() operations in parallelized mode.
Changes:
- Add a per-metadata-key
SequencerByKey<string>to serializesetMetadatawrites for the same key. - Route
setMetadatathrough the new sequencer while preserving concurrency across different metadata keys.
Show a summary per file
| File | Description |
|---|---|
| src/vs/platform/agentHost/node/sessionDatabase.ts | Adds a per-key sequencer for session metadata writes and applies it in setMetadata to guarantee submission-order persistence for “last-writer-wins” keys. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 0
pwang347
approved these changes
Apr 22, 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.
Symptom
The integration test
Protocol WebSocket - Session Config persistence across restarts > persisted config values are restored on subscribe after server restarthas been intermittently failing in CI:The test creates a session, dispatches
SessionConfigChangedto switch the branch frommaintorelease, restarts the server, and then asserts thatreleaseis what comes back. Sometimesmaincame back instead.Root cause
@vscode/sqlite3runs in parallelized mode by default —db.run()calls on the same connection are dispatched to libuv's thread pool and can complete out of submission order.In this scenario, two
setMetadata('configValues', …)writes happen back-to-back on the same connection:AgentService._persistConfigValueswrites the initial config when the session is created.AgentSideEffects._persistSessionFlagwrites the updated config afterSessionConfigChanged.Both writes are correctly awaited via the existing
whenIdle()flush, but the seconddb.runcan finish before the first one inside SQLite's worker pool, so the older value ends up as the final on-disk row. Captured timing during a reproduction confirmed this:release) completed at dt=7msmain) completed at dt=13ms →mainwas the last writer to diskThis is not just a test artefact: any rapid sequence of
SessionConfigChangedactions on the same key (e.g. fast UI toggles) could lose the latest value in production.Fix
Route
setMetadatathrough aSequencerByKey<string>keyed by metadata key, so writes for the same key run in submission order while writes for different keys still run concurrently. This is the same pattern already used forstoreFileEdit(keyed by file path) for the same reason.The fix is intentionally narrow:
createTurn,deleteTurn,truncateFromTurn, etc.) are not "last-writer-wins on a key" — they're inserts/deletes/transactions where out-of-order completion doesn't have the same hidden corruption mode.Validation
Stress-tested locally by running 12 parallel test processes in batches:
(Written by Copilot)