fix: flush graph LSM-trees on close to prevent data loss across restarts

GraphAdjacencyIndex.flush() was a no-op — LSM MemTables were never
written to SSTables for datasets under the 100K auto-flush threshold.
This caused readdir, getRelations, and getDescendants to return empty
results after close + reopen.

Three fixes:
- LSMTree.get(): merge MemTable + SSTable results (data spans both
  after flush, old early-return missed SSTable data)
- GraphAdjacencyIndex.flush(): actually flush all 4 LSM-trees
- GraphAdjacencyIndex.close(): close all 4 trees (was only closing 2)

Also: brain.close() and shutdown hooks now call close() on graphIndex,
HNSW index, and metadataIndex to release timers and file handles.
This commit is contained in:
David Snelling 2026-02-01 17:55:40 -08:00
parent 0add0af45a
commit ab2493af02
4 changed files with 213 additions and 22 deletions

View file

@ -800,10 +800,21 @@ export class GraphAdjacencyIndex {
const startTime = Date.now()
// Flush both LSM-trees
// Note: LSMTree.close() will handle flushing MemTable
// For now, we don't have an explicit flush method in LSMTree
// The MemTable will be flushed automatically when threshold is reached
// Flush all 4 LSM-trees in parallel (MemTables → SSTables on disk)
await Promise.all([
this.lsmTreeSource.flush().then(() => {
prodLog.debug(`GraphAdjacencyIndex: Flushed source tree`)
}),
this.lsmTreeTarget.flush().then(() => {
prodLog.debug(`GraphAdjacencyIndex: Flushed target tree`)
}),
this.lsmTreeVerbsBySource.flush().then(() => {
prodLog.debug(`GraphAdjacencyIndex: Flushed verbs-by-source tree`)
}),
this.lsmTreeVerbsByTarget.flush().then(() => {
prodLog.debug(`GraphAdjacencyIndex: Flushed verbs-by-target tree`)
}),
])
const elapsed = Date.now() - startTime
@ -819,10 +830,14 @@ export class GraphAdjacencyIndex {
this.flushTimer = undefined
}
// Close LSM-trees (will flush MemTables)
// Close all 4 LSM-trees (will flush MemTables to SSTables)
if (this.initialized) {
await this.lsmTreeSource.close()
await this.lsmTreeTarget.close()
await Promise.all([
this.lsmTreeSource.close(),
this.lsmTreeTarget.close(),
this.lsmTreeVerbsBySource.close(),
this.lsmTreeVerbsByTarget.close(),
])
}
prodLog.info('GraphAdjacencyIndex: Shutdown complete')