Uh oh!
There was an error while loading. Please reload this page.
spans: drop the generated duration_ms column (schema v10) - #88
Conversation
… nothing duration_ms was a VIRTUAL generated column declared mid-table. It takes a logical slot but no storage slot, so every column after it had a logical index one ahead of its physical index, and `WHERE col = <constant>` on such a column probed an unrelated ART index and matched nothing - a silent wrong answer that reads correctly in review. That is why /api/v1/bash-commands shipped permanently empty. Schema version 10 drops the column and the four queries that read it compute epoch_ms(end_time) - epoch_ms(start_time) instead, aliased back to duration_ms so no response shape or sort key moves. The COALESCE(tool_name, '') workaround goes with it: DuckDB 1.4+ pushes COALESCE into the scan too, so it was on borrowed time. DuckDB refuses to ALTER a table an index depends on, so the migration drops the four secondary indexes first and the existing CREATE INDEX IF NOT EXISTS block rebuilds them. It moves no row data. Verified against a copy of production (108 MB, 34 706 spans): tool_name = 'Bash' counted 0 rows before and 6 440 after, service_name = 'claude-code' 0 before and 34 705 after, span count unchanged; storage.Open went 9.5s -> 9.8s against a cold start dominated by WAL replay, and a later re-apply of schema.sql costs 138 ms. The new upgrade test covers the same path on a seeded v9 database, and the pushdown guard now passes with an empty broken-column list. Co-Authored-By: Wayland <wayland@agents.flopbut.local> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Warning Review limit reached
Next review available in:103 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
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 |
…presence handleSession ignores the Scan error, so a projection that no longer scans would have returned zero-valued spans and still passed the len() > 0 check. Pin the computed value instead. Co-Authored-By: Wayland <wayland@agents.flopbut.local> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Root-cause fix for the DuckDB filter-pushdown wrong-results trap, per ADR-0013.
What changed
schema.sql→ version 10.duration_msis gone fromCREATE TABLE spans, and the migration section drops it from existing databases. DuckDB refuses toALTERa table an index depends on, so the four secondary indexes are dropped first and the existingCREATE INDEX IF NOT EXISTSblock rebuilds them. No backfill, noUPDATE, no row data moved.CAST(epoch_ms(end_time) - epoch_ms(start_time) AS DOUBLE), aliased back toduration_ms: the session spans projection, the tools stats CTE, the bash-commands CTE, and the retention roll-up. TheCASTkeeps the expressionDOUBLEexactly as the old column was, soSUMdoes not becomeHUGEINTand no scan target changes.COALESCE(tool_name, '') = 'Bash'workaround is removed along with the comment block explaining it. It was engine-fragile: DuckDB 1.4+ pushesCOALESCEinto the scan too.pushdownBrokenSpanColsis now empty andTestSpansEqualityUnderFilterPushdownpasses with it empty — every VARCHAR column ofspansagrees under bare equality. The guard test stays as the detector for a reintroduced generated column.TestUpgradeDropsGeneratedDurationColumnseeds a populated v9 database (generated column mid-table + the four indexes) and asserts the migration removes the column, keeps the rows, and makestool_name = 'Bash'correct. That test is what caught the index dependency — without theDROP INDEXstatements,storage.Openfails outright on every existing database withDependency Error: Cannot alter entry "spans".No response shape changes:
duration_ms/avg_duration_mskeep their JSON names and values, and the export ZIP is untouched (writeSpansCSValready derived the duration in Go, and the spans export query never selected the column), soformat_versiondoes not move. The importer reads by header name and never inserted the column.Verification
go test ./...andgo vet ./...green (Dockergolang:1.23-bookworm, arm64 — no native Go toolchain on this box). VitePress docs build green.Against a
docker cpcopy of the production database (108 MB + 0.4 MB WAL, 34 706 spans, DuckDB v1.1.3):schema_versionspansrowsduration_msdeclaredWHERE tool_name = 'Bash'WHERE service_name = 'claude-code'spansTimings on that copy: raw open (WAL replay only) 9.48 s →
storage.Openwith the migration 9.84 s, so the whole upgrade adds ~0.4 s to a cold start dominated by replay. A subsequent open with the hash already recorded is 9 ms, and a forced full re-apply ofschema.sql(what every future schema edit costs now, including the index rebuild) is 138 ms. The file shrank 108.3 MB → 98.3 MB as the WAL folded in on close.Docs
Fixedentry for the wrong results and oneChangedentry for the v10 migration, its measured cost, and the absence of a downgrade path.spanslayout, plus theALTER/index dependency finding.ADR-0013is referenced by name indocs/decisions/0001-storage.mdwithout a link, since the file lands in #87 and a relative link to a not-yet-merged page fails the docs build. The CHANGELOG entry does link it — worth merging #87 first.Closes FLO-574.