Repository navigation
fix(db): make the compaction lock token unique independent of the clock - #1217
Conversation
acquireCompactLock stamped its lock file with pid + time.Now(), and release compared that string to decide whether the lock was still ours. On Linux and macOS time.Now() has nanosecond resolution, so two acquisitions in one process always differ. Windows' system clock is coarse (~0.5-15ms): two back-to-back acquisitions land in the same tick and produce a byte-identical token, so A's release deletes B's lock and a third compaction can start on the same BBolt database. This surfaced as TestCompactLockReleaseDoesNotRemoveSomeoneElsesLock failing on every windows-latest "Build Binaries" job since #1214, blocking unrelated PRs (#1215 touched only a markdown file and failed identically), but the collision is real outside the test too — the token is the only thing standing between a stale-lock cleanup and two concurrent compactions. Add a 16-byte crypto/rand nonce to the token. pid and started stay: they are what a human reads out of a stale lock file. The lock protocol and the compaction flow are otherwise untouched. The new test strips the clock-derived line before comparing, so it bites on a nanosecond clock too rather than passing vacuously everywhere but Windows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying mcpproxy-docs with
|
| Latest commit: |
9feb4ec
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b819c6b4.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://claude-kind-hugle-6fa6d4.mcpproxy-docs.pages.dev |
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 34095478154 --repo smart-mcp-proxy/mcpproxy-go
|
TestCompactLockReleaseDoesNotRemoveSomeoneElsesLock is the test that exposed this bug, and it stayed clock-dependent: on a nanosecond-resolution platform the two acquisitions get different timestamps, so it passed for the wrong reason and only failed on windows-latest, intermittently. Add compactLockNow as a seam and freeze it in both lock tests. A frozen clock is precisely the Windows condition — coarse granularity with two acquisitions back-to-back — so the collision is now exercised on every platform rather than by luck. With the nonce reverted, the pre-existing test reproduces the exact windows-latest failure on macOS. That also lets the new uniqueness test compare whole tokens instead of stripping the started= line to guess what Windows would collide on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed 9feb4ec, porting the test seam from #1221 (opened as a duplicate of this PR, now closed). The fix itself is unchanged. What this adds is that
Mutation-checked on macOS — with the nonce reverted, both tests fail, the pre-existing one with the exact windows-latest message: Also verified: |
Problem
TestCompactLockReleaseDoesNotRemoveSomeoneElsesLockfails on thewindows-latestBuild Binaries job of PR Build, blocking every PR. It is pre-existing and unrelated to any branch: introduced by f95fc14 (#1214), and #1215 — which touches only.github/RELEASE_NOTICE.md— failed with the byte-identical message (run 33946755580, job 101259930963).Root cause
acquireCompactLockstamped the lock file withand release compares
string(current) != tokento decide whether the lock is still ours.On Linux/macOS
time.Now()has nanosecond resolution, so two acquisitions in one process always differ. Windows' system clock is coarse (~0.5–15 ms); the test acquires A, removes the file, and acquires B back-to-back with no work between them, so both land in the same tick. Same PID + same timestamp = identical token, A's release believes B's lock is its own, and deletes it. The test running in 0.00s matches.This is a real bug, not just a flaky test: the token is the only thing standing between a stale-lock cleanup and two concurrent compactions on the same BBolt database, and on Windows it can genuinely collide.
Fix
Add a 16-byte
crypto/randnonce to the token.pidandstartedstay — they are what a human reads out of a stale lock file. The lock protocol and the compaction flow are untouched.Test
TestCompactLockTokenIsUniqueWithinOneClockTickacquires, releases, re-acquires, and compares the two lock files with the clock-derivedstarted=line stripped — so it reproduces what Windows sees and bites on a nanosecond clock too, instead of passing vacuously everywhere but Windows.Verified failing before the fix:
and passing after, alongside the original test.
go test ./cmd/mcpproxy/green,-racegreen,GOOS=windows go test -cbuilds,golangci-lint run --config .github/.golangci.yml ./cmd/mcpproxy/...reports 0 issues.🤖 Generated with Claude Code