fix(memory-vector): seed JVector index from SQLite on VectorMemorySystem.open()

После рестарта in-RAM граф JVector создавался пустым (seedEntries не
проходились из metaStore), поэтому search возвращал [], пока не
появлялись новые upsert'ы — память терялась после каждого рестарта.

- VectorMemorySystem.open(): JVectorMemoryIndex(dimension, metaStore.allEntries())
- SqliteMemoryMetaStore.open(): убрал случайное двойное конструирование
- регресс-тест openSeedsIndexFromSqliteAfterRestart (save → close → open → search)
This commit is contained in:
2026-09-15 08:17:42 +03:00
parent 18ae6e0619
commit 135c6a419d
3 changed files with 21 additions and 2 deletions
@@ -194,7 +194,6 @@ class SqliteMemoryMetaStore(
*/ */
fun open(dbPath: String, dimension: Int): SqliteMemoryMetaStore { fun open(dbPath: String, dimension: Int): SqliteMemoryMetaStore {
val conn = DriverManager.getConnection("jdbc:sqlite:$dbPath") val conn = DriverManager.getConnection("jdbc:sqlite:$dbPath")
SqliteMemoryMetaStore(conn, dimension)
return SqliteMemoryMetaStore(conn, dimension) return SqliteMemoryMetaStore(conn, dimension)
} }
} }
@@ -45,7 +45,10 @@ class VectorMemorySystem(
topK: Int = 10, topK: Int = 10,
): VectorMemorySystem { ): VectorMemorySystem {
val metaStore = SqliteMemoryMetaStore.open(dbPath, embedding.dimension) val metaStore = SqliteMemoryMetaStore.open(dbPath, embedding.dimension)
val index = JVectorMemoryIndex(embedding.dimension) // Граф пересобирается из SQLite (источник правды): без seed'ов
// после рестарта in-RAM индекс пуст и search возвращал бы [],
// пока не появятся новые upsert'ы.
val index = JVectorMemoryIndex(embedding.dimension, metaStore.allEntries())
val store = VectorMemoryStore(index, metaStore, embedding) val store = VectorMemoryStore(index, metaStore, embedding)
val prefetcher = VectorPrefetcher(store, topK) val prefetcher = VectorPrefetcher(store, topK)
val reviewer = VectorMemoryReviewer(store) val reviewer = VectorMemoryReviewer(store)
@@ -136,4 +136,21 @@ class VectorMemoryStoreTest {
store2.close() store2.close()
} }
} }
@Test
fun openSeedsIndexFromSqliteAfterRestart() = runTest {
// Регрессия: VectorMemorySystem.open() обязан пересадить in-RAM граф
// из SQLite — иначе после рестарта search возвращает [] до первого upsert.
val first = VectorMemorySystem.open(file.absolutePath, FakeEmbeddingProvider(dimension = dim))
first.store.upsert(makeNote("r", "restarted fact: dog rex poodle"))
first.close()
val second = VectorMemorySystem.open(file.absolutePath, FakeEmbeddingProvider(dimension = dim))
try {
val results = second.store.search(MemorySearchQuery(query = "restarted fact", topK = 5))
assertTrue(results.any { it.note.id == "r" })
} finally {
second.close()
}
}
} }