Repository navigation
GigaMap/jvector: invalidate stale graphs and derived vectors after external updates - #832
hrstoyanov wants to merge 14 commits into
Conversation
…ndex.invalidateGraph() API
…ndex.invalidateGraph() API
|
Hi @hrstoyanov, thank you for this contribution and for reporting the issue in #847. Before we can merge this PR, the legal requirements have to be fulfilled on your side. This is required by the Eclipse Foundation, and we unfortunately can't make workarounds for it any more. Once that's done, we'll be happy to continue with |
…hile holding the GigaMap monitor (internal#155) A monitor holder only tries the index group's write locks and waits on the monitor in between, as removeAll()/reindex() do since eclipse-store#848; searches from the same index are rejected instead of self-deadlocking. Adds VectorIndexInvalidateGraphLockOrderTest.
|
Update after #848 and #855: Why. #848 established that a thread holding the GigaMap monitor must never wait for What changed (
Tests. New
Without the fix, the first, third and fourth fail (deadlock, self-deadlock and starvation respectively); with it all four pass. |
|
Hi, and thanks a lot for this PR. The problem description is excellent: the replication/binary-merge scenario is clearly explained, and it’s a valid point that the only current workaround is Unsafe-level field manipulation, which is definitely not something anyone should have to rely on. The proposal is also nicely scoped. The fail-closed behaviour for incremental on-disk mode and the test coverage show you thought this through carefully. That said, we’ll have to postpone this one for now. The core team is currently working on larger changes in GigaMap and the vector index, and those will very likely touch the same areas this PR changes (builder lifecycle, graphRebuilt handling, deferred builder ops). Merging it now would probably lead to conflicts, or we’d end up with an API whose semantics would need to change again shortly afterwards. We definitely want to keep the use case in mind, though. Being able to safely invalidate a stale in-memory graph after the underlying data changes outside of add/update/set is a legitimate requirement, and we’ll take it into account in the ongoing work. Once those changes have landed, we’ll get back to this and check whether the PR can be rebased, whether it’s still needed, or whether the new design already covers it. Thanks again for the effort |
Catch up with the segmented on-disk index (eclipse-store#861) and the vectorizer result copy (eclipse-store#862). invalidateGraph() is retargeted to the new architecture: on-disk indices are rejected because their graph is served from the directory's segments, the removed deferred-op queue is dropped, and the graph rebuild reads the vector file. The lock-order tests pause a search through the searchTestHook seam, since searches no longer call the vectorizer while scoring. Co-Authored-By: Kimi K3 <noreply@moonshot.ai>
Keep the fork's publishing branch on the latest upstream (eclipse-store#861 segmented on-disk index, eclipse-store#862 vectorizer result copy) plus the PR eclipse-store#832 patch only: VectorIndex.invalidateGraph() and its lock-order protocol. The superseded rebuild-scoping delta is dropped, since eclipse-store#861 removed the Store-backed rebuild it optimized, and the upstream tree is taken for the rest. The result is byte-identical to the PR eclipse-store#832 branch, so the artifacts published from here carry exactly the patch under review.
tree and the PR eclipse-store#832 patch, drop the superseded PR eclipse-store#844
|
Update after #863, in case it helps the ongoing GigaMap/vector work. #863 lists a race this PR already fixes. #863's "Known, not addressed" says: "After a failed initialization, a search re-initializes the index without the map's monitor, so two threads can both initialize it." On For the redesign: an invalidation must not move Rebase. #863 touches the regions next to this PR's changes ( |
External updates to a live GigaMap bypass its mutation hooks, leaving a VectorIndex's transient search state stale. Since #861, retiring only the graph also leaves a temporary vector file with old vectors: the rebuilt graph can miss newly imported entities or return IDs that were deleted.
This PR adds
VectorIndex.invalidateGraph()for replication and live binary-import integrations. It retires the in-memory graph and marks a derived temporary vector file stale. The next access clears and recovers that file from current entities before rebuilding the graph. Clearing and vectorization stay off the import path, and repeated invalidations coalesce until access.Recovery preserves the imported vector count. Zeroing it would make recovery advance
structuralModCountlocally, allowing the next writer update to compare equal and skip its invalidation. Persistent vector files are left unchanged, and on-disk graph mode rejects invalidation. Lock handling supports callers holding the parent-map monitor, rejects calls from inside a search, and serializes concurrent first initialization.Regression coverage is on this PR branch in
VectorIndexInvalidateGraphTest: mutate entity vectors outside the map API, invalidate twice, verify fresh vectors and search results, verify one recovery per access, and verify the persisted witness stays unchanged. This test fails before the fix. Existing lock-order, concurrent-search, and on-disk refusal tests remain.Compatibility with upstream #863: its
discardGraph()continues to serve reindex/reset, while external invalidation preserves the imported witness.invalidationAfterReindexPreservesTheNewWitnessexercises reindex followed by invalidation and verifies fresh vectors without a local counter increment.Validation against upstream
4029cb01:main(359fb050, this PR plus GigaMap/jvector: reuse vectors during exact graph construction #864): clean Maven install, 2,082 tests, zero failures, 11 skipped; JVector accounts for 623 tests, zero failures, 10 skipped.4f7f058fin an isolated source snapshot: unit 1,176 tests (2 skipped), integration 230, crash matrix 27, chaos soak 1; zero failures. Ongoing uncommitted cluster changes were excluded.Lucene keeps using its existing reusable
close()seam. No binary layout or replication protocol changes are introduced.Fixes #847.