Skip to content

Release 2.6.0 — secondary index blobs: non-key lookups prune#24

Merged
pango07 merged 2 commits into
mainfrom
feat/secondary-indexes-2.6
Jul 11, 2026
Merged

Release 2.6.0 — secondary index blobs: non-key lookups prune#24
pango07 merged 2 commits into
mainfrom
feat/secondary-indexes-2.6

Conversation

@pango07

@pango07 pango07 commented Jul 11, 2026

Copy link
Copy Markdown
Owner

The last SQL-dialect roadmap item (LARVA-DESIGN.md §14) — the roadmap is now complete.

What an index is

Exactly what §14 promised: just another immutable blob committed in the same atomic swap. Per indexed column, one ULID-named blob maps chunkId → sorted distinct non-NULL values. The manifest's table entry carries the current ref; every commit that changes the table's chunk set re-stages the blob copy-on-write (drop retired ids, add entries for new chunks) and re-points the ref in the same commit. Plans, transactions, group-commit batches, ordered-intent leader batching, and tier-A folds all route through one maintenance hook. Queries with =, IN, or range predicates on an indexed column then keep only the chunks whose entry can satisfy the predicate (binary search over the sorted values).

Why no format bump

The load-bearing decision: entries are keyed by immutable chunk id, so an index entry can only ever be wrong by absence — and a chunk id missing from the map is simply always fetched. Absence is exactly what every failure mode produces:

  • a pre-2.6 client writing chunks without maintaining the index (their apply functions preserve the refs they don't understand),
  • an index update lost to a rebase against a concurrent winner,
  • a blob deleted by an older client's vacuum (readIndex treats 404 as "no index").

Each degrades pruning; none can change results. Verified offline: a chaos suite compares indexed lookups against full scans under two-writer contention, and a planted proto-level "old client" write proves the stale-safety path.

Using it

  • Code-first: t.text().index() — flags sync both ways at connect (performance metadata, never drift); creating one backfills from existing chunks in a single commit that re-executes if the chunk set moves underneath it.
  • SQL: CREATE INDEX [IF NOT EXISTS] [name] ON t (col) / DROP INDEX ON t (col) — one index per column, addressed by column (a name parses and is ignored). CREATE UNIQUE INDEX and composite indexes rejected by name with the reason.
  • Vacuum retains index blobs by the same reachability rule as chunks; time-travel reads use the indexes their manifest references.

Measured in the smoke suite: WHERE total = 220 on a 5-chunk table fetches 1 chunk; the offline suite shows equality/IN/range each pruning to exactly the matching chunks.

Tests — 297 checks (was 275)

  • group-commit: 75 → 88 — the new section 16 covers equality/IN/range pruning, UPDATE/DELETE maintenance, stale-chunk safety (proto-level insert), missing-blob degradation, connect auto-drop/auto-create with backfill, tier-A fold indexing, and the chaos + contention ground-truth comparison.
  • sql-smoke: 134 → 143 — CREATE/DROP INDEX catalog (incl. UNIQUE/composite/name-based-DROP rejections) and a live pruning walkthrough.
  • Full gauntlet green locally: stress (zero lost updates), property (model holds), api-smoke 30, s3-adapter 17, cli-smoke 19.

Docs synced: design doc §5/§8/§14 (roadmap marked complete), content/sql.mdx + api.mdx, docs/larva-for-agents.md (→ /llms.txt), quickstart agent prompt, READMEs, CLAUDE.md.

Self-review plus an independent gpt-5.5 (codex) pass focused on the absence-only safety argument. The review confirmed no path makes a present index entry wrong for its chunk id, and found one real bug, fixed with a regression test: the internal MAX sentinel for open-ended ranges was compared as a value, so WHERE indexed_col > 'z' could wrongly prune a chunk whose values sort above the sentinel (the pre-existing zone-map check had the same latent hole). The marker is now identity-checked as "no upper bound".

🤖 Generated with Claude Code

pango07 and others added 2 commits July 10, 2026 22:56
An index is one immutable blob per (table, column) mapping chunkId ->
sorted distinct values, referenced from the manifest's table entry and
re-staged copy-on-write in the same atomic commit that changes the
table's chunks (plans, transactions, group-commit batches, tier-A folds
all route through one maintenance hook). Queries with =/IN/range
predicates on an indexed column keep only chunks whose entry can
satisfy the predicate.

Keying entries by immutable chunk id means an entry can only be wrong
by ABSENCE, and absent chunks are always fetched — so a pre-index
writer, a rebase-lost update, or a vacuumed blob degrade pruning, never
results. No format bump needed; verified by an offline chaos suite
comparing indexed lookups against full scans under contention, plus a
planted proto-level 'old client' write.

- .index() in defineSchema (auto-synced both ways at connect; create
  backfills from existing chunks in one commit)
- CREATE INDEX [IF NOT EXISTS] [name] ON t (col) / DROP INDEX ON t (col)
  (one index per column, addressed by column; UNIQUE/composite rejected
  by name)
- vacuum retains index blobs by the same reachability rule as chunks
- 296 checks (group-commit 75 -> 87, sql-smoke 134 -> 143)
- design doc §5/§8/§14 (roadmap complete) + all agent-facing docs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gpt-5.5 finding: the MAX sentinel string was compared as a value, so an
open-ended range (WHERE col > 'z') could wrongly prune a chunk whose
values sort above the sentinel — in both the new index pruning and the
pre-existing zone-map check. The marker is now identity-checked (===)
as 'no upper bound'; a real value equal to it merely over-fetches.
+1 regression check (297 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
larva-db Ready Ready Preview, Comment Jul 11, 2026 5:58am

Request Review

@pango07 pango07 merged commit 9232563 into main Jul 11, 2026
4 checks passed
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.

1 participant