Repository navigation
fix(vlog): recover the newest surviving file when a 0-byte .vlog is skipped - #1
Merged
adityeah8969 merged 1 commit intoOct 18, 2026
Conversation
…kipped populateFilesMap sets vlog.maxFid from the directory listing before the 0-byte skip runs, so when the crash artifact holds the highest fid the skip drops it from filesMap while maxFid keeps naming it. The filesMap[maxFid] lookup guarding the tail truncation then finds nothing, so iterate and Truncate never run and a partially written entry stays on the newest surviving .vlog. That is the same crash that dropped the 0-byte file, so recovery is skipped exactly when it is needed. The stale value also defeats the `fid != maxFid` guard that keeps a header-only newest file from being deleted. Track newestFid, the highest fid that survived the stat pass, and use it for both. Leave maxFid alone: createVlogFile takes maxFid+1 with O_EXCL, so lowering it would collide with the artifact still sitting on disk. Also log skipped files, wrap the stat error with errFile to match openMemTables, and lower ValueThreshold in TestOpenWithEmptyVlogFile so its values actually reach the value log and the read-only step exercises the vlog read path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Owner
|
@matthewmcneely thanks for the PR, I have merged this into my fork, and I have asked for final review in my original PR |
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.
Description
Follow-up to my review on dgraph-io/badger#2331. This targets
fix/empty-mem-file-openin your fork rather than upstream, so you can fold it in however you like before the next push. The.memand.vlogstat-and-skip work is yours and unchanged; this fixes one thing the vlog half missed, plus a few small items from the review.Why
populateFilesMapsetsvlog.maxFidfrom the directory listing, before the 0-byte skip runs. When the crash artifact holds the highest fid, and it normally does, the skip drops it fromfilesMapwhilevlog.maxFidkeeps naming it. Two things then read a fid that is no longer there:filesMap[vlog.maxFid]lookup guarding the tail truncation finds nothing, soiterateandTruncatenever run. The newest surviving.vlogkeeps whatever partial entry was left on it. The crash that leaves a 0-byte.vlogbehind is exactly the crash that can tear the tail of the file before it, so recovery gets skipped precisely when it is needed.fid != vlog.maxFidguard stops protecting the newest real file, so a header-only newest.vloggets deleted. Benign on its own, since a header-only file holds no entries, but it is the same root cause.Reproduced on your branch by appending a torn record to the last
.vlogand reopening:OpenNot a regression, since
Openfailed outright before your PR. It is a hole in the recovery path the PR is meant to repair.Changes
value.gonewestFid, the highest fid that actually survived. Both the truncation and the header-only guard use it.vlog.maxFidis deliberately left alone.createVlogFiletakesmaxFid+1withO_EXCL, so lowering it to the newest surviving fid would collide with the artifact still sitting on disk and fail withEEXIST. That one is worth a comment because it looks like the obvious fix.vlog.maxFidis safe there: read-only never reachescreateVlogFile, so the invariant stays broken for the life of the handle, butgetFileRLockedkeys onvp.Fidand skips itsmaxFidbound check whenReadOnlyis set.value.goandmemtable.goInfofwhen a 0-byte file is skipped, matching the existingDeleting empty file:line just below it. Both artifacts now stay on disk permanently, so the log line is the only way an operator learns the directory carries crash residue.errFilefor the stat error invlog.open, so it wraps the same wayopenMemTablesalready does.db_test.goTestEmptyVlogFileDoesNotSkipTailRecovery, run with and without the artifact present. It fails on the current branch withexpected: 52570, actual: 52607and passes with thevalue.gochange.TestOpenWithEmptyVlogFilenow setsValueThresholdto 32. At the 1MB default every value was inlined in the LSM tree, so the test never touched the value log read path at all. Step 6 now reads all 100 values back through the vlog in read-only mode, which is the mode wherevlog.maxFidstays dangling.Testing
go test ./...passes.TestEmptyVlogFileDoesNotSkipTailRecoveryfails without thevalue.gochange and passes with it.gofmtclean,go vetclean apart from the pre-existingpb.Matchlock-copy warnings inpublisher.go.🤖 Generated with Claude Code