Skip to content

test(integration): catch on-disk storage format breaks pre-merge - #4003

Merged
hubcio merged 5 commits into
masterfrom
storage-compat-test
Sep 2, 2026
Merged

test(integration): catch on-disk storage format breaks pre-merge#4003
hubcio merged 5 commits into
masterfrom
storage-compat-test

Conversation

@hubcio

@hubcio hubcio commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

A change to the on-disk format could ship undetected, and every
way it fails is silent. A partition whose durable state cannot be
read is tombstoned while boot still exits 0. Topic options
degrade per key with no log line at all. Segment files carry no
version stamp, so a layout change is a misread rather than an
error. The breaking:storage label already promised this check and
was wired to nothing.

A server built from origin/master seeds a data directory holding
sealed and active segments, a forced metadata checkpoint,
consumer and group offsets, a purged topic, users and
permissions. The binary is then swapped to the build under test
and restarted against that same directory, and everything must
read back.

One process and one test rather than a two-phase handoff:
restart() re-reads executable_path on every call and stop() never
touches the data directory, so no staging area or cross-process
manifest is needed. Both binaries are built explicitly, because
the integration crate has no dependency on the server and nextest
alone would run whatever stale binary sits in target, comparing
master against master and reporting green forever.

The assertions check data, never that the server booted. Topic
options are seeded non-default so a lost key cannot masquerade as
a re-derived default, and the segment set is compared byte for
byte to catch a truncating misparse that a readback would miss.
breaking:storage skips the check, resolved from live labels
rather than the webhook payload so it still works on a re-run.

@github-actions github-actions Bot added the S-waiting-on-review PR is waiting on a reviewer label Aug 31, 2026
@hubcio
hubcio force-pushed the storage-compat-test branch from 7b6facc to ddfb920 Compare September 1, 2026 06:58
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.02%. Comparing base (bbfad59) to head (e1edec8).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #4003      +/-   ##
============================================
+ Coverage     84.97%   85.02%   +0.04%     
  Complexity     1402     1402              
============================================
  Files          1230     1236       +6     
  Lines        181408   181465      +57     
  Branches     147703   147758      +55     
============================================
+ Hits         154157   154286     +129     
+ Misses        23199    23105      -94     
- Partials       4052     4074      +22     
Components Coverage Δ
Rust Core 86.06% <81.09%> (+0.05%) ⬆️
Java SDK 67.29% <ø> (ø)
C# SDK 75.37% <ø> (-0.10%) ⬇️
Python SDK 90.06% <ø> (ø)
PHP SDK 85.65% <ø> (ø)
Node SDK 94.50% <ø> (+0.11%) ⬆️
Go SDK 69.35% <ø> (-0.02%) ⬇️
see 39 files with indirect coverage changes
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

spetz
spetz previously approved these changes Sep 2, 2026
A change to the on-disk format could ship undetected, and every
way it fails is silent. A partition whose durable state cannot be
read is tombstoned while boot still exits 0. Topic options
degrade per key with no log line at all. Segment files carry no
version stamp, so a layout change is a misread rather than an
error. The breaking:storage label already promised this check and
was wired to nothing.

A server built from origin/master seeds a data directory holding
sealed and active segments, a forced metadata checkpoint,
consumer and group offsets, a purged topic, users and
permissions. The binary is then swapped to the build under test
and restarted against that same directory, and everything must
read back.

One process and one test rather than a two-phase handoff:
restart() re-reads executable_path on every call and stop() never
touches the data directory, so no staging area or cross-process
manifest is needed. Both binaries are built explicitly, because
the integration crate has no dependency on the server and nextest
alone would run whatever stale binary sits in target, comparing
master against master and reporting green forever.

The assertions check data, never that the server booted. Topic
options are seeded non-default so a lost key cannot masquerade as
a re-derived default, and the segment set is compared byte for
byte to catch a truncating misparse that a readback would miss.
breaking:storage skips the check, resolved from live labels
rather than the webhook payload so it still works on a re-run.
Review of the compat lane found three ways it could pass with the
on-disk format broken. The purged topic was empty on both sides of
the swap: a purge.gen that fails to decode reads as 0 by design, the
reconciler re-purges, and messages_count == 0 held either way. Users
were seeded with every permission bit true, so a dropped or
reordered field read back identical, and nothing authenticated with
the seeded password or token after the swap. Stream recovery was a
bare count, which a corrupted name survives because later reads
resolve by id.

Messages appended after the purge now have to survive the swap at
offset 0, permissions alternate bits at every level and compare
whole, fresh clients log in with the password and the raw token,
and streams compare as an id-to-name map.

The lane itself could also be skipped or misdirected. rust-configs
owns the partition and metadata schema knobs but was not a
dependency, and the module wiring files were not watched. The
baseline was live origin/master, which can be newer than the master
a pull_request merge ref was cut against; the script now takes the
merge commit's first parent, read from the raw object because HEAD^1
does not resolve in a depth-1 checkout. A relative CARGO_TARGET_DIR
is made absolute before the worktree build, and test-storage-compat
no longer runs the DAG and coverage setup it never reads.
The baseline fetch passed --depth=1 unconditionally. On the CI
checkout that is a no-op, but on a developer's full clone git does
not skip anything: it writes the fetched tip into .git/shallow and
grafts every older commit off the history, for every worktree that
shares the repository. A local run left master cut at its tip and
git status counting the whole old history as remote-only commits.

Pass --depth=1 only when the repository is already shallow.
The checkpoint fired on 250 empty streams before any topic, group,
user or token existed, so every rich type reached the second boot
through WAL replay alone and a broken snapshot encoding passed.
Messages had id 0, no user headers, one payload length and partition
0, and the readback compared a payload prefix, so a misread id,
timestamp, header or batch partition id passed too. A purge
generation decoding above the committed one kept the messages the
test checks while silently suppressing every later purge.

Seed the rich metadata before the checkpoint drive and one more of
each type after it, so both encodings are exercised. Give messages
explicit ids, typed headers, varying lengths and a non-zero
partition, capture the baseline's own readback and compare every
stored field after the swap. Purge again under the build under test
and require it to take effect; that topic is excluded from the byte
comparison by design.

The script now passes --retries 0 and --ignore-default-filter
instead of relying on the absence of --profile ci, and
.config/nextest.toml requires nextest 0.9.77, the floor for those
flags. The install step replaces a cache-restored binary that fails
the version check instead of aborting the lane later.
@hubcio
hubcio merged commit ad9e5dd into master Sep 2, 2026
166 of 176 checks passed
@hubcio
hubcio deleted the storage-compat-test branch September 2, 2026 09:46
@github-actions github-actions Bot removed the S-waiting-on-review PR is waiting on a reviewer label Sep 2, 2026
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.

3 participants