Repository navigation
test(storage): synthetic reproducer for the ART-index corruption - #149
Merged
Merged
Conversation
Rebuilds the trigger from the schema, with no production bytes: spans as it stood at schema version 2 with that era's three ART indexes, filled across the retention boundary; storage.Open then runs the real schema.sql, which adds user_id VARCHAR to the filled indexed table and indexes that added column; a bulk UPDATE writes it and the retention roll-up supplies the large replace transaction; then checkpoint, close, reopen, checkpoint, ingest more, repeat. The repetition and the restart are the test, not scaffolding around it: the damage accumulates and is only ever reported by the checkpoint that folds the index or the open that reads it back. The fourth assertion - an indexed equality agreeing with the same predicate the index cannot serve - covers the silent form, a wrong answer rather than an error. It stays in the ordinary test set rather than behind a build tag because the moment it most needs to run is a driver or engine bump, which is exactly when an optional test is forgotten. Defaults cost 4.7 s; COTEL_ART_REPRO_SPANS and COTEL_ART_REPRO_CYCLES scale it up. Against the linked engine (duckdb-go/v2 v2.10506.0, v1.5.6) it does not reproduce, at the defaults or at 200 000 spans over 6 cycles. The recovery page records that as a measurement, not an exoneration: it settles the half of the forensic volume's release condition that was unevaluable, in the weaker direction. Co-Authored-By: Wayland <wayland@agents.flopbut.local> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 34 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
What
A synthetic reproducer for the ART-index corruption that took production down on 2026-09-28, plus the recovery page's verdict on the half of the forensic volume's release condition that nobody could evaluate.
internal/storage/art_repro_test.gorebuilds the trigger from the schema, with no production bytes — every value is derived from the row number, so it carries no user id, session id or span attribute that belonged to anyone:spansas it stood at schema version 2 (nouser_id) with that era's three ART indexes, filled with spans spread over 60 days, i.e. across the 30-day retention boundary;storage.Open, so the realschema.sqlruns the realALTER TABLE spans ADD COLUMN user_id VARCHARagainst a filled, indexed table and then buildsidx_spans_user_idon that added column;UPDATEwrites the added column, then cotel's own retention roll-up supplies the large replace transaction — theINSERT INTO daily_usageand theDELETE FROM spansare one transaction, so it is the real mutation and not an imitation;The repetition and the restart are the test, not scaffolding: the damage accumulates invisibly, and this class of fault is only ever reported by the checkpoint that folds the index or the open that reads it back. Each pass asserts the reopen succeeds, both checkpoints fold, all four indexes survive, and an indexed
user_id = ?agrees with the same predicate written so the index cannot serve it — the last one covering the silent form of this damage, a wrong answer rather than an error.Result
Against the engine the tree links today (
duckdb-go/v2 v2.10506.0,SELECT version()→v1.5.6), on linux/arm64 undergolang:1.24:The docs state this as a measurement, not an exoneration: two negative runs at one scale on one architecture, against a production failure that took weeks of ingest and an upstream report of the same class on 1.5.5 at ~19 GB. The release condition now reads not shown to reproduce at our scale on 1.5.6, which is strictly weaker than fixed, and a clean run does not release the volume.
Decision worth a second opinion
The ticket allowed a long/optional build tag "if it is slow". At the defaults it costs 4.7 s, so it is not tagged — it runs in
go test ./..., which means a driver or engine bump exercises it automatically.COTEL_ART_REPRO_SPANS/COTEL_ART_REPRO_CYCLESscale it up for a deliberate hunt. If you would rather it never ran in CI, a//go:build artreprotag is a one-line change.Not done, deliberately
Verified
CGO_ENABLED=1 go vet ./...— cleanCGO_ENABLED=1 go test ./...— all packages green,internal/storage11.0 snpm run buildindocs/— VitePress site builds (the Docs workflow runs on this PR)🤖 Generated with Claude Code