Skip to content

debug: tag reload() breadcrumbs with lock state and NextUID (#647)#648

Merged
0kaba0hub merged 1 commit into
mainfrom
debug/reload-lock-state-breadcrumbs
Jul 19, 2026
Merged

debug: tag reload() breadcrumbs with lock state and NextUID (#647)#648
0kaba0hub merged 1 commit into
mainfrom
debug/reload-lock-state-breadcrumbs

Conversation

@0kaba0hub

Copy link
Copy Markdown
Owner

Summary

#645 fixed one trigger of the #644 NextUID-regression class (stale logFD surviving a rename), but the same symptom reproduced again on 2.1.11 under concurrent imaptest+smoketest load, with #645's ".log replaced" detector never firing — pointing at a second, distinct trigger (filed as #647).

withFolderRO (the read path — most importantly OpenFolder's dedup snapshot, hit on every internal/lmtp/deliver.go delivery) calls reload() under only the in-process fs.mu, never acquiring the cross-process distributed lock (u.b.locker) that withFolderLock (the write path) always takes first. An unlocked read can interleave with another process's locked compaction and load a torn view into the shared in-memory folderState, which every subsequent correctly-locked write then trusts as a baseline.

This PR

reload() now takes a locked bool (true from withFolder/withFolderLock, false from withFolderRO) and logs it plus NextUID before/after on every "fileindex: reload fast-path" / "reload full" / "reload applied" breadcrumb, so the next live reproduction can directly show an unlocked reload's NextUID diverging from a concurrent locked writer's, instead of inferring the race indirectly from the absence of #645's detector firing.

Purely additive logging behind LOG_LEVEL=debug — no behavior change. go test ./... green, existing #645 regression test (TestConcurrentCompactionNoUIDRegression) updated for the new reload() signature and still passing. appVersion → 2.1.13.

#645 fixed one trigger of the #644 NextUID-regression class (stale logFD
surviving a rename), but the same symptom reproduced again on 2.1.11 under
concurrent load with #645's ".log replaced" detector never firing — a
second, distinct trigger.

withFolderRO (the read path — most importantly OpenFolder's dedup snapshot,
hit on every internal/lmtp/deliver.go delivery) calls reload() under only
the in-process fs.mu, never acquiring the cross-process distributed lock
(u.b.locker) that withFolderLock (the write path) always takes first. An
unlocked read can interleave with another process's locked compaction and
load a torn view into the shared in-memory folderState, which every
subsequent correctly-locked write then trusts as a baseline.

reload() now takes a locked bool (true from withFolder/withFolderLock,
false from withFolderRO) and logs it plus NextUID before/after on every
"fileindex: reload fast-path"/"reload full"/"reload applied" breadcrumb, so
the next live reproduction can show an unlocked reload's NextUID diverging
from a concurrent locked writer's instead of inferring the race indirectly.

Purely additive logging behind LOG_LEVEL=debug — no behavior change.
@0kaba0hub
0kaba0hub merged commit e60afde into main Jul 19, 2026
1 of 2 checks passed
@0kaba0hub
0kaba0hub deleted the debug/reload-lock-state-breadcrumbs branch July 19, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants