feat: add distributed REFRESH INDEX for incremental index maintenance - #822
ivscheianu wants to merge 1 commit into
Conversation
fbe1182 to
985fe59
Compare
985fe59 to
399d0a0
Compare
399d0a0 to
617b97f
Compare
617b97f to
bd7c94c
Compare
bd7c94c to
d18ef41
Compare
ALTER TABLE ... REFRESH INDEX indexes only the fragments an existing index does not cover, distributed across Spark executors the same way CREATE INDEX is. The driver re-resolves the index before committing to catch a concurrent DROP, and rejects FTS segments whose configuration differs from the ones they would join.
d18ef41 to
3ef6f26
Compare
There was a problem hiding this comment.
The distributed refresh path matches the incremental-maintenance contract: executor work is pinned to the planned snapshot, current coverage and full-text configuration are checked before the logical segment commit, existing coverage is preserved, and the operational limits are documented.
Until lance#6806 is released and pinned, avoid running DROP INDEX concurrently with a refresh. A drop in the narrow check-to-commit window can be undone and leave a partially covered index; SHOW INDEXES makes that state detectable and another drop recovers it, so this remains a non-blocking operational risk.
|
hello, @geruh! Here's the final one. Looks big, but a significant part of it are tests and also a bit of documentation, hopefully it's manageable. Thanks a lot! |
Part of #789 (2/2). Depends on #821 — review that first; the diff here includes it until it merges.
Design: #788. Supersedes #784 (rebased and split).
Summary
CREATE INDEXrebuilds every fragment, so keeping an index current on an append-heavy table means re-indexing the whole table. This adds an incremental counterpart:It indexes only the fragments the index does not cover, distributed the same way
CREATE INDEXis: the driver diffs coverage against the table and balances the remainder by row count, executors build one uncommitted segment per batch, and the driver commits them as one logical index. Lance core keeps existing segments whose fragments are disjoint from the incoming ones, so prior coverage survives the commit.A deferred index (
train = false) covers nothing, so refreshing one builds the whole table through the same distributed path — replacing the guidance to callDataset.optimizeIndices, which runs on a single node.Guards
DROP INDEXduring the build is caught rather than silently undone.Known gap
A concurrent
DROP INDEXbetween re-resolution and the commit can be silently undone. This needslance-format/lance#6806on the core side. Pinned by a disabled regression test.Not in scope
CREATE INDEX.WITHclause with type defaults as fallback.Changes (this PR only, excluding #821)
LanceSqlExtensions.g4(+2 lines:refreshIndexrule,REFRESHkeyword)visitRefreshIndexaddedRefreshIndex.scala(new)LanceDataSourceV2Strategy.scala(+3 lines)RefreshIndexExec.scala(227 lines, new)BaseRefreshIndexTest.java(766 lines, 26 tests), parser tests, per-version stubsrefresh-index.md(214 lines)