Repository navigation
Conversation
EnRaiha
marked this pull request as draft
October 6, 2026 14:19
EnRaiha
force-pushed
the
drill/issue354
branch
from
October 7, 2026 07:55
7ed16c1 to
b0f1c8f
Compare
EnRaiha
marked this pull request as ready for review
October 7, 2026 09:29
The metadata stall bound and the data-group recovery bound were constants in the bootstrap code, so an operator with a long WAL tail could not raise them. Both are newtypes now, read from [tuning.startup]. The server config path already rejected such a key through `deny_unknown_fields`, but that message lists every valid field instead of the path that is read. A guard names the replacement path, so a misplaced key says where to put it. The one-node harness passes its own tighter bound rather than inheriting the production default, which keeps the harness fast without changing what production does. The single-node harness keeps the shipped defaults on purpose, and its comment says so.
A store written by another build failed every boot with a message calling its segments corrupt. The root cause was in the reader: WalError::UnsupportedVersion already existed and the header returned it, but the reader flattened it, and every other validation failure, into StopReason::Corruption. The gate then saw an empty tail and reported damage that was not there. The reader returns that error now, and the startup gate turns it into Error::VersionCompat carrying the segment path, the version found, the version required, and advice that depends on the direction of the gap. A zeroed version stays a corruption stop, because the writer paths share this reader and a torn tail must not stop a store from opening. The gate also runs before the writer opens. That open recovers the newest segment, so without the ordering the composed message was unreachable on the store that motivated the work.
The planner's null check becomes is_none_or, and the reply path's negated conjunction is written as one condition. Both are the lints the CI profile enforces.
A review found the reader-level behaviour had no reader-level test: the gate and the writer open were covered, but nothing in reader.rs exercised the version match itself. This adds it in both directions, an unknown version is a typed error and a zeroed version is a corruption stop. The getting-started paragraph also described what the metadata bound does without saying that the data-group bound is a hard deadline, which is the difference an operator needs when a boot is slow.
A review found the zeroed-version case proved nothing: an empty record list is also what a silent skip or a clean end returns, so a fail-open regression would pass it. The reason is public, so the test asserts it is Corruption.
EnRaiha
force-pushed
the
drill/issue354
branch
from
October 7, 2026 11:10
b0f1c8f to
c620167
Compare
This branch has not been deployed
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.
A store written by another build failed every boot with a message that called its WAL segments corrupt. The reader already knew the format version and threw it away, which is the root cause. This branch surfaces that version, so startup names what it found and what it requires, and it makes the two startup bounds configurable with a test pinning each one.
What changed
WalError::UnsupportedVersioninstead of flattening every header validation failure intoStopReason::Corruptionnodedb-wal/src/reader.rsnodedb/src/wal/manager/replay.rsraft_ready_timeout_msanddata_group_recovery_timeout_msfrom[tuning.startup]as newtypes, and refuse them on the[server]path with the key namednodedb-types/src/config/tuning/startup.rs,nodedb/src/config/server/config.rs,nodedb/src/bootstrap/cluster_ready.rs,nodedb/src/bootstrap/data_group_recovery.rs,nodedb/src/control/security/auth_lease/status.rsnodedb/src/bootstrap/wal_init.rsnodedb/src/bootstrap/wal_init.rs,nodedb/src/wal/manager/replay.rsnodedb/src/wal/manager/replay.rs,nodedb/src/bootstrap/cluster_ready.rs,nodedb/tests/inproc/Why
A 20,000-row store booted in 83.4 s, and the phase that dominates the boot runs after both startup gates, so neither bound covers it. Both bounds were hard-coded and unreachable from the server config.
Separately, the production store could not boot at all, and the message sent its reader looking for damage that was not there. The header carried the answer.
Checks
cargo fmt --all -- --checkpasses.version compatibility: WAL segment '…' holds records in WAL format version 1; this build requires 3. The store needs a migration, not a repair., the word "corrupted" appears zero times in its log, and a real version-3 store boots to serving.cargo nextest run -p nodedb --lib -E 'test(/wal::manager::replay/) or test(/class_parity/)'Evidence, logs, the mutation diff and the end-to-end script: https://github.com/EnRaiha/nodedb/blob/pr412-c620167c/docs/review/README.md
Scope
Two units share this pull request, one commit each: the startup bounds under
[tuning.startup], and the WAL format diagnosis. They are independent, and a reviewer who wants only one can take the commit that carries it. The third commit is two clippy fixes that CI's-D warningsprofile requires.Review 2: PASS, 0 blockers, bound to this head. The evidence branch, its logs and its tags are linked above.
A rebase merge keeps the unit commits separate. A squash merge would erase them, and the two commits that cover the reader test can be folded into one on request.
Closes #354