Summary
pebble/tool/lsm's runLSM reconstructs an L0 organizer from the engine's current state by calling manifest.NewVersionForTesting → (*manifest.L0Organizer).ResetForTesting → manifest.newL0Sublevels → addFileToSublevels. On certain production LSM shapes the reconstruction trips an assertion ("addFileToSublevels found existing newer file") and panics. The production-path L0 organizer (which is maintained incrementally rather than rebuilt from scratch) does not trip the same condition on the same LSM.
Where it surfaces
CockroachDB's /debug/lsm-viz/<store> endpoint routes through tool/lsm via pkg/server/debug/server.go:analyzeLSM and inherits the panic. Observed reliably on a post-online-restore CockroachDB cluster whose LSM was holding linked external SSTs at intermediate levels — the CRDB-side report is at cockroachdb/cockroach#170896.
The shape was non-pathological from a correctness standpoint: production reads, writes, and compactions ran against the same LSM without issue. Only the from-scratch reconstruction in the test helpers asserted.
Stack
addFileToSublevels found existing newer file
internal/manifest/l0_sublevels.go:679 (addFileToSublevels)
internal/manifest/l0_sublevels.go:309 (newL0Sublevels)
internal/manifest/l0_sublevels.go:2237 (L0Organizer.ResetForTesting)
internal/manifest/version.go:124 (NewVersionForTesting)
tool/lsm.go:326 (lsmT.buildEdits)
tool/lsm.go:160 (lsmT.runLSM)
Conjecture
*ForTesting helpers exist for unit tests with hand-crafted LSMs and may carry stricter invariants than the live organizer maintenance path. Either:
- The assertion in
addFileToSublevels is genuinely over-broad and rejects a file ordering that the production code emits and handles correctly, or
- The reconstruction's iteration order differs from how the live organizer would have produced the same final state, and that order trips an assertion that's only meaningful for hand-built test fixtures.
In either case, tool/lsm (and its callers) should not depend on *ForTesting reconstruction succeeding on arbitrary production LSMs.
Suggested directions
- Identify which file ordering trips the assertion. A minimal MANIFEST reproduction would pin it down — restoring
gs://cockroach-fixtures-us-east1/roachtest/master/tpcc-5k/20260522-090958.790 AS OF SYSTEM TIME '2026-05-22T10:56:00Z' with EXPERIMENTAL DEFERRED COPY and pausing the download job produces a triggering LSM.
- If the assertion is legitimate, route
tool/lsm around the test-helper path and have it build its view via production-path APIs only.
- If the assertion is over-broad for production LSMs, narrow it (or remove it) and check whether the live organizer maintenance path was relying on a related invariant.
Related: cockroachdb/cockroach#170896.
Jira issue: PEBBLE-1446
Summary
pebble/tool/lsm'srunLSMreconstructs an L0 organizer from the engine's current state by callingmanifest.NewVersionForTesting→(*manifest.L0Organizer).ResetForTesting→manifest.newL0Sublevels→addFileToSublevels. On certain production LSM shapes the reconstruction trips an assertion ("addFileToSublevels found existing newer file") and panics. The production-path L0 organizer (which is maintained incrementally rather than rebuilt from scratch) does not trip the same condition on the same LSM.Where it surfaces
CockroachDB's
/debug/lsm-viz/<store>endpoint routes throughtool/lsmviapkg/server/debug/server.go:analyzeLSMand inherits the panic. Observed reliably on a post-online-restore CockroachDB cluster whose LSM was holding linked external SSTs at intermediate levels — the CRDB-side report is at cockroachdb/cockroach#170896.The shape was non-pathological from a correctness standpoint: production reads, writes, and compactions ran against the same LSM without issue. Only the from-scratch reconstruction in the test helpers asserted.
Stack
Conjecture
*ForTestinghelpers exist for unit tests with hand-crafted LSMs and may carry stricter invariants than the live organizer maintenance path. Either:addFileToSublevelsis genuinely over-broad and rejects a file ordering that the production code emits and handles correctly, orIn either case,
tool/lsm(and its callers) should not depend on*ForTestingreconstruction succeeding on arbitrary production LSMs.Suggested directions
gs://cockroach-fixtures-us-east1/roachtest/master/tpcc-5k/20260522-090958.790AS OF SYSTEM TIME '2026-05-22T10:56:00Z'withEXPERIMENTAL DEFERRED COPYand pausing the download job produces a triggering LSM.tool/lsmaround the test-helper path and have it build its view via production-path APIs only.Related: cockroachdb/cockroach#170896.
Jira issue: PEBBLE-1446