Repository navigation
fix: preserve knowledge base document IDs during backup restore - #10530
dafyy321-pixel wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Hey - I've reviewed your changes and they look great!
Sourcery assessment
Needs a human reviewer. If the restored integer IDs are wrong or collide, the copied FAISS index can retrieve the wrong knowledge-base documents and the resulting database state survives a code revert. The damage is bounded and can be repaired by restoring the backup again after correcting the import.
|
The pull-request workflows are marked Local verification for this backup fix is complete: the restore, FTS, and backup tests pass (82 tests), along with Ruff formatting and lint checks. The PR description includes the exact commands and the checks for sparse IDs, reverse-order restoration, a subsequent insert, and retrieval after reopening the stores. |
Fixes #10529.
This preserves the integer chunk IDs when restoring a knowledge-base backup, so the restored SQLite rows continue to match the IDs in the copied FAISS index.
A deleted chunk leaves a gap in SQLite's primary-key sequence. For example, a valid knowledge base can contain IDs
1and3. The backup exports those IDs along with the existing index, but the importer currently inserts the text rows with automatic allocation. The restored rows become1and2, while FAISS still contains vectors labelled1and3. The restore can finish normally even though dense retrieval can no longer resolve one of its vectors to the original text.The fix uses the identity already present in the backup. It requires no archive-format change, index rebuild, or additional embedding request.
Modifications
Preserve the integer primary key during restore
astrbot/core/db/vec_db/faiss_impl/document_storage.pyadds one optional, keyword-only argument toinsert_document():The supplied value is used as
Document.id. With the defaultNone, SQLite still allocates the integer ID automatically, as it does for ordinary inserts today. The existing positional arguments and return value remain the same.astrbot/core/backup/importer.pypassesinteger_id=doc.get("id")from the exported document entry. The comment beside that argument explains why it is needed: the FAISS file being copied into the restored knowledge base still refers to those integers.The string
doc_id, text, and metadata retain their existing import paths. The distinction betweendoc_idandidis important here. Preserving the string identifier does not repair a vector that points to a different SQLite primary key.Keep normal allocation and older document entries working
The explicit ID is limited to callers that provide it. New documents continue to use automatic allocation, and an imported entry without an
idfollows the same path becausedoc.get("id")returnsNone.For a restored database containing IDs
1and3, the next new document receives ID4. That keeps new inserts above the restored IDs instead of reusing an integer that the copied index already associates with an older chunk.Add a restore regression using real storage
tests/unit/test_kb_backup_restore.pyadds two cases. The main case creates real SQLite and FAISS stores and uses a small, deterministic embedding provider. It insertsalpha,bravo, andcharlie, deletesbravo, then restores the surviving document entries in reverse order and copies the original FAISS index into the restore destination.Reversing the entries is deliberate. It verifies that restoration preserves the supplied identity even when allocation order would otherwise assign a different integer to the same text.
The assertions cover:
alpha: 1,charlie: 3alpha: 1,charlie: 3charliecharlierowcharlie3with the matching string ID4charliechunkcharliechunk and newly inserteddeltaremain retrievableThe second case imports a document entry without
id, checks automatic allocation of ID1, then verifies that an ordinary insert receives ID2.Behavior after the change
In the sparse-ID example, both stores retain the same relationship:
Dense lookup no longer loses the row for ID
3, and inserting a new document does not cause the old vector to resolve to that document's text. FTS and dense retrieval agree on the restored chunk's identity.Verification Steps and Test Results
Validated locally on Windows with Python
3.12.14and Ruff0.15.22.The relevant test selection was:
This includes the two new restore cases, the existing FTS document-storage coverage, and the backup suite.
Formatting and static analysis also passed:
The explicit check of the new test file ensures that it is covered even when repository ignore rules affect the full-directory format command.
git diff --checkis clean.Compatibility and review scope
This changes two production files and adds one test file. The optional argument preserves existing calls to
insert_document(). The archive schema, database schema, backup routes, response envelopes, and frontend remain the same.The regression verifies the restore relationship at the document-store/index boundary rather than relying on a successful import message or matching entry counts. There is no UI navigation or label change, so the WebUI instructions and screenshots do not need an update for this fix.
The change can be reviewed independently of selective-backup work such as #10151. If the import loop is later converted to batch insertion, the same requirement still applies: the integer IDs supplied by the backup must be retained when the existing FAISS index is restored alongside them.
Checklist
Summary by Sourcery
Preserve knowledge-base document identities during backup restoration to keep SQLite records aligned with their existing FAISS vector IDs.
Bug Fixes:
Enhancements:
Tests: