Repository navigation
feat(cli): build a branch's declared indexes with omnigraph index build - #1150
Conversation
`omnigraph index build [--branch B]` brings one branch's declared indexes up to its rows: it builds every declared index a table lacks and rebuilds whole every BTREE or vector index that leaves a current fragment uncovered, in one graph commit on that branch. Full-text coverage and vector properties without vectors are reported as pending with their remedy. Optimize does the same on main with compaction; both use the same index builders. `omnigraph index rebuild --kind full-text` replaces `rebuild-full-text-indexes`, which stays one release as a hidden alias that warns. `--kind` is required. Engine: `Omnigraph::ensure_indices_on_as` enforces Change on the branch before any effect and records the actor; `ensure_indices` and `ensure_indices_on` delegate to it with no actor, so with a policy installed they refuse instead of bypassing the gate. One table's work stages as one detached CreateIndex (`TableStore::stage_index_reconciliation`). The full-text refusals and optimize's pending reason named commands with a positional address the CLI rejects; they now name parseable commands, held as constants that a CLI test parses.
…e-840-98af0f # Conflicts: # crates/omnigraph-cli/src/planes.rs # docs/user/operations/upgrade.md
An index build now rebuilds an index whose coverage lags, so in hunt cell seed 2 the first EnsureIndices has work after rows reach an indexed table, and a setup crash at its pre-manifest window strands staging. Seed 5's first EnsureIndices has nothing to build, which is what the test needs.
WhyA branch can hold rows whose declared indexes are missing or no longer cover those rows. Queries still return the right rows. Filters and search then scan more data than the schema declares. Operators need one command that closes that cost gap on the branch they name. What
How
The new segment's fragment bitmap is the dataset bitmap. Lance 13 That whole rebuild is required on this Lance version. On stable row ids, a delta scalar or full-text segment does not own its rows. Older segments can still match. Vector search already has per-segment ownership. The Lance alignment guide says to drop the whole-rebuild rule at the bump whose scalar and full-text search apply segment ownership. Unknown fragment coverage stays untouched, which is the existing fold rule. ImpactAfter the commit, the branch can probe the new indexes. The CLI test shows VerdictApprove. Blocking findings: 0. Optional findings: 4. Optional findings
Tradeoff and liabilityA separate The new surface is the Five similar commands fit this group: status, rebuild of one kind, and drop. This change spends efficiency to keep coverage correct on Lance 13. A whole rebuild writes more index data than a delta segment. That cost removes the stale-match hazard. The pinned search behavior makes the delta unsafe for scalar and full-text indexes. A refresh keeps the recorded partition count and trains L2, which is the metric OmniGraph uses for the vector indexes it creates. That choice already exists on the optimize fold. The deprecated spelling Other open pull requests#1151 fixes RUSTSEC-2026-0387 and does not change this command. #1139 ( #1130 overlaps #951 and #963 are direction for permissions and schema apply with live branches. This command adds no scheduler and no second catalog. It stays compatible with that direction. ValidationReviewed head: Isolated checkout: Local runs, debug profile,
An earlier engine filter omitted the CI on this head: Test Workspace, GQ Logic Tests, GQT ordinary, GQT dst, GQT served, DST pinned suite, rustfmt, clippy, S3, Azurite, and Storage Upgrade succeeded. Dependency Guard failed on Regression proofNo GQT before/after pair is required. The new contract is a maintenance publication and the plan that follows it. The GQT runner calls The LimitationsI did not merge a branch after an index build in this run. The pure-insert conclusion is from Inline comments
The sentence says
Add the read-set sentence from the Optimize section. A write that arrives on a table while its index batch is staged fails the run with a read-set conflict. The next run plans again from the new pin. |
State the full-text contract change, add the mixed fresh-and-moved BTREE case, name the identity and endpoint BTREEs, record lance#9785 and lance#9872 and mark the moved-row half as the Lance 13.0.0 fence, state the moved-row costs, and block on PR ModernRelay#1150.
An index build or fold commits a Lance CreateIndex with removals, which the pure-insert merge proof treated as a row change, so the next merge of that interval fell back to the ordered row diff. Any CreateIndex read at the expected version keeps every fragment, so the proof now crosses it as an index-only commit. Pending index work on edge tables is now reported after the node types, and the maintenance guide states that index build also runs on main and that a write landing during the batch fails the run with a read-set conflict.
WhyA fast-forward merge can adopt a source branch when every commit after the base only inserts rows. An index build commits a Lance What
HowLance 13 applies OmniGraph commits every index effect as that operation, through
The check sits on the operation, in The proof then requires the source fragments that the base lacks to hold the same physical row count as the certified inserts. A ImpactA fast-forward after an index build on the source stays a pointer switch. VerdictReady. New blocking findings: 0. New optional findings: 1. Earlier optional findingsAll four notes on
Optional finding
Inline on Tradeoff and liabilityThe old predicate named one safe The change feed still rejects The new surface is unchanged from the previous review: the This spends index bytes to keep coverage correct on Lance 13. A whole rebuild writes more than a delta segment. That cost removes the stale-match hazard the alignment guide pins for scalar and full-text indexes. #1156 plans incremental segments later and says the #1175 will move maintenance from ValidationReviewed head: Local runs used an isolated checkout of that head and Parent behavior, files
Failed. 0 passed, 1 failed, 642 filtered out, 0.14s. The assertion at the history-read count was Same test at the head, after restoring those two files:
Passed. 2 passed, 0 failed, 641 filtered out, 0.09s. CI on this head (run 40276555402) passed Test Workspace, GQ Logic Tests, GQT ordinary, GQT dst, GQT served, DST pinned suite, rustfmt, clippy, RustFS, Azurite, Storage Upgrade, and the CI Gate. That is CI evidence, not a local rerun. LimitationsI did not rerun the workspace suite, the GQT corpus, or the CLI integration test locally. I did not run a separate fast-forward after |
…e-840-98af0f # Conflicts: # crates/omnigraph/src/storage_layer.rs
…erging main Main's csv overwrite sink and the index reconciliation each forward one pinned dataset through TableStorage, so storage_layer.rs now holds 35.
The pure-insert merge proof crosses any CreateIndex link as moving no row. A surface guard now commits a first build and a whole rebuild detached, as the engine does, and requires every fragment of the read version unchanged: ids, physical rows, data files and deletions. It goes red if a Lance apply rewrites fragments inside CreateIndex. The proof's comment and the Lance compatibility table point at it.
WhyThe pure-insert merge proof treats every WhatHead
The merge How
The guard builds a BTREE with The sealed-forward count is an AST count of zero-argument
Impact
VerdictReady. New blocking findings: 0. New optional findings: 0. Earlier findingsThe review on
Tradeoff and liabilityThe proof depends on one Lance apply rule. The guard is that dependency, in the suite that already pins detached commits. A Lance bump that changes the apply fails this test. The engine gains no new type, no new state, and no second catalog. The registry edit is the existing chokepoint list. It records the forward #1178 added beside the forward this branch added. A second unregistered The whole-rebuild cost is unchanged from the review on Five more pins of this kind belong in Other open pull requests
#1156 (updated after this head) keeps the BTREE and vector #1175, #1177, and #1181 also edit ValidationReviewed head: Local runs used At the head:
Passed. CI on this head, run 40444314162, passed Test Workspace, GQ Logic Tests, GQT ordinary, GQT dst, GQT served, DST pinned suite, rustfmt, clippy, RustFS, Azurite, Storage Upgrade, and the CI Gate. The workspace log shows the same four tests passed. That is CI evidence. Regression proofThe failure starts at the merge commit At
Failed. 0 passed, 1 failed, 35 filtered, 1.14s. The panic text is The same command at the head passed, as listed above. The Lance guard records behavior that already holds. With only No GQT case was added. This delta does not change query rows, counts, column types, or error text. The maintenance publication remains owned by the engine and CLI tests from the earlier reviews. LimitationsThe fixture has no deletion file.
I did not rerun the workspace suite, the GQT corpus, or the CLI integration test locally. CI on this head passed those jobs. I did not measure rebuild time or index bytes. Local tests use small graphs in the debug profile. |
The merge of ModernRelay#1150 made `rebuild-full-text-indexes` a hidden spelling of `index rebuild --kind full-text` and added `index build`, both direct-only on a cluster root. The S3 allow-list still named only the old variant, so the current commands refused an S3 root and the skill pointed agents at the deprecated one. Admit `Command::Index` and test both leaves. Say in the skill, command reference and guide that each direct writer keeps its admission lock, which must be force-unlocked before the next writer or the server starts. Move the S3 section to its own page: it put the maintenance guide over the 350-line docs limit.
…ry and maintenance (#1189) * feat(cli): accept an inline s3:// cluster root for deployment, recovery and maintenance Since #1115 an inline target is local-only, so the 0.14 CLI cannot address an S3 cluster root. With the server stopped there is then no way to run optimize or cleanup, force-unlock a retained lock, or reconcile an outstanding deployment on S3 (#1163, #1082). This admits an inline s3:// root for cluster plan, apply, status, force-unlock and upgrade-ledger, and for optimize, cleanup, repair and rebuild-full-text-indexes with --graph. The destination is captured once from the AWS environment through StorageProvider::capture_from. Every other command, az://, and an s3:// graph URI still refuse before any I/O. Claude-Session: https://claude.ai/code/session_01FGZebF1Mp287NfGK4u4qbc * fix(cli): admit index build and rebuild on an inline s3:// root The merge of #1150 made `rebuild-full-text-indexes` a hidden spelling of `index rebuild --kind full-text` and added `index build`, both direct-only on a cluster root. The S3 allow-list still named only the old variant, so the current commands refused an S3 root and the skill pointed agents at the deprecated one. Admit `Command::Index` and test both leaves. Say in the skill, command reference and guide that each direct writer keeps its admission lock, which must be force-unlocked before the next writer or the server starts. Move the S3 section to its own page: it put the maintenance guide over the 350-line docs limit. --------- Co-authored-by: Ragnor Comerford <ragnor.comerford@gmail.com> Co-authored-by: Ragnor Comerford <hello@ragnor.co>
Closes #840. No command could build a branch's indexes:
optimizereconciles them onmainonly, and the full-text rebuild covers full text alone. A type whose first rows land on a branch had no identity, BTREE or vector index there, and rows added on a branch to an indexed type stayed outside its BTREE and vector indexes until the branch merged andmainwas optimized. Answers stayed correct; branch reads scanned instead.omnigraph index build [--branch B]brings one branch's declared indexes up to its rows: it builds every declared index a table lacks and rebuilds whole every BTREE or vector index that leaves a current fragment uncovered, in one graph commit on that branch, reporting full-text coverage and vector properties without vectors as pending with their remedy. The engine operation,Omnigraph::ensure_indices_on_as, enforcesChangeon the branch before any effect; each table's work stages as one detachedCreateIndexon its pin from the two builders optimize already chains after compaction, and optimize's own links are unchanged.omnigraph index rebuild --kind full-textreplacesrebuild-full-text-indexes, which stays hidden for one release and warns. The full-text refusals namedomnigraph … <URI>, which the CLI rejects; they now name constants a test parses through clap.The engine test builds a first-rows type and refreshes a lagging identity index on a branch, leaves
mainunchanged, and publishes nothing on a second run; the CLI test shows the branch's explain move fromsequentialtoindex_probeonrank_idx, and fails on the missing refreshedPerson.__idrow when the refresh is disabled. The policy test refuses both operations and both no-actor wrappers before any effect. A fast-forward merge afterindex buildon the source keeps the pure-insert proof: Lance applies anyCreateIndexwith every fragment of the version it read unchanged, so the proof now crosses one as an index-only commit instead of falling back to the ordered row diff (merge_fast_forward::fast_forward_merge_proof_crosses_an_index_build_on_the_source_issue_840reads two links and scans nothing; before the fix it stopped after one).lance_surface_guards.rs::rfc_0067_create_index_keeps_every_fragment_of_its_read_versionpins the Lance fact this relies on: a detached first build and a whole rebuild keep every fragment of the read version, ids, physical rows, data files and deletions. Pending work on edge tables is reported after the node types. The DST storage-cost golden is unchanged.For review:
ensure_indiceschanges meaning. It now also rebuilds lagging BTREE and vector indexes, whole, since OmniGraph adds no delta segments (lance#9817), and with a policy installed it refuses without an actor. Three seeded pins moved with it, each because an earlierensure_indicesnow has refresh work: a branching test now builds the branch's indexes before forking so its no-work premise holds;dst_maintenance_commit_uses_execution_branchseed 228317 moves from write 84 to 95 (with the refresh disabled, 84 lands in op 9 again; 95 is the same kind of write, a nodes-table transaction put, as the other seed's pin); anddst_setup_crash_needs_its_aftermathmoves from hunt cell seed 2 to 5, the first of four scanned seeds whose firstensure_indicesstill has nothing to build. Full-text coverage stays explicit as RFC 0043 decided, though its original reason no longer applies since RFC 0067; that is a separate decision.openapi.jsonchanges one description. Not run locally: the GQT corpus, whose harness builds indexes once on freshly seeded tables.