You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Soft deleted HNSW nodes are never removed, so the graph and hnsw.bin grow with every deleteDocument() and updateDocument().
Changes
VectorDatabase::compact(): int rebuilds the HNSW graph from active documents using their stored vectors (no re-embedding) and returns the number of reclaimed nodes. Persistent databases are saved under the folder lock. No deletions means no rebuild and no write.
VectorDatabase::deletedCount(): int, to compact above a threshold.
HNSW\Index::compact(), HNSW\Index::deletedCount(), and an optional explicit node ID on HNSW\Index::insert(). VectorDatabase now passes its own node ID instead of relying on both counters staying in lockstep.
Notes
Active nodes keep their node IDs. Renumbering would mean renaming docs/{n}.bin files, which cannot be done atomically with the index files. The serializer already stores node IDs explicitly, so there is no format change.
BM25 is not rebuilt: removeDocument() already removes documents fully.
The rebuild costs about as much as a fresh build, so it belongs in a maintenance job.
Not included
Reusing deleted slots on insert. It would conflict with tombstone recovery in open(), and compact() covers the need.
📊 marks 11 throughput metrics that moved within the run to run variance of a shared runner. Only a drop beyond 50% counts as a regression there. Memory and disk figures repeat exactly between runs, so they gate at 5%.
The flagged row is a 2 MB shift between phases, not extra memory. In the small scenario save (memory delta) dropped from 2 MB to 0 MB while open (memory delta) rose from 2 MB to 4 MB, so the total is unchanged. PHP's allocator grows in 2 MB chunks, which is why a single chunk moving from one phase to the next reads as +100%.
#29 and #30 show exactly the same pattern while touching unrelated code, and neither changes the open() path, so the baseline most likely predates a change already on main.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #27.
Soft deleted HNSW nodes are never removed, so the graph and
hnsw.bingrow with everydeleteDocument()andupdateDocument().Changes
VectorDatabase::compact(): intrebuilds the HNSW graph from active documents using their stored vectors (no re-embedding) and returns the number of reclaimed nodes. Persistent databases are saved under the folder lock. No deletions means no rebuild and no write.VectorDatabase::deletedCount(): int, to compact above a threshold.HNSW\Index::compact(),HNSW\Index::deletedCount(), and an optional explicit node ID onHNSW\Index::insert().VectorDatabasenow passes its own node ID instead of relying on both counters staying in lockstep.Notes
docs/{n}.binfiles, which cannot be done atomically with the index files. The serializer already stores node IDs explicitly, so there is no format change.removeDocument()already removes documents fully.Not included
open(), andcompact()covers the need.save()writesmeta.jsonbeforehnsw.bin, so a crash in between can make deleted nodes searchable again. The window predates this PR and is tracked in save() is not atomic across meta.json, hnsw.bin and bm25.bin #28.