v4: Valkey index drift — orphaned vectors not cleaned up on memory deletion #11
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The
healthendpoint reports index counts directly from Valkey's search indexes, but these counts can diverge significantly from the actual number of memory records in the system. Runningmemory_auditreveals the true picture.Observed discrepancy
health(Valkey index)memory_audit(actual records)That's ~921 orphaned index entries — vectors present in the Valkey search index with no corresponding memory record.
The web UI dashboard (which queries records directly) showed 235 knowledge entries, consistent with the audit, and much lower than the 754 reported by
health.Root cause (suspected)
When memories are deleted, the Valkey search index entries are not being removed. This compounds over time, especially after bulk deletes, restores, or expired entries being purged. The index grows without bound while the record store stays in sync.
Proposed fix
Add a
reindexcommand (or integrate intomemory_audit) that:idx:episodic,idx:project,idx:knowledge)This could be exposed as:
reindexMCP toolmemory_audit(e.g.--fixorauto_clean=True)The
healthendpoint should ideally report record counts, not raw index counts — or report both separately so the drift is visible.Impact
healthgives misleading counts, making it hard to trust reported index sizesClosing — this shipped, matching the proposed fix almost exactly, and was verified against the current
v5code during the 2026-07-08 audit:reindex(namespace?)MCP tool (v5.1.1): drops and recreates the search index for a namespace so valkey-search rebuilds from existing hashes only, clearing phantom entries. Data-safe — only the index is touched. Returns before/afternum_docs, the actual record count, andremoved_phantoms. (The valkey-py.dropindex()wrapper turned out to be unreliable, so it usesFT.DROPINDEXdirectly.)health()reports both sides plus the drift (v5.1.1, extended to the preference namespace): per-namespace indexnum_docs, the authoritative SCAN-based record count, and adriftentry whenever they diverge — so the discrepancy in this issue's table is now visible at a glance rather than misleading. As of v5.3.0 the record counts come from a single keyspace scan instead of one per namespace.The "orphaned vectors accumulate silently" failure mode is what
reindex+ the drift report were built for.