fix(8.0): restore() reloads a native entity-id mapper before graphIndex.rebuild()
A logical snapshot restore could silently lose graph edges on a brain backed by a native provider: the graph adjacency keys on the shared id-mapper's interned ints (sourceInt/targetInt), which are derived + never persisted, and restore() never reloaded the mapper — so a native graph rebuild resolved verb endpoints against a stale/empty mapper and dropped edges. restore() now calls entityIdMapper.rebuild() — a new OPTIONAL method on the EntityIdMapperProvider contract — BEFORE rebuilding the indexes, so a native mapper reloads its int<->uuid from the restored binary KV first and the graph resolves endpoints correctly. The JS mapper deliberately has NO rebuild() and needs none: MetadataIndex.rebuild() re-derives it from the restored entities via append-only getOrAssign, consistently with the bitmaps it builds — forcing a reload there would blank a still-referenced mapping and break find() after a same-instance restore. Contract: graphIndex.rebuild() resolves sourceId/targetId -> ints through the shared mapper itself (brainy does not re-feed resolved endpoints); brainy's only job is to ensure the mapper is reloaded first. Test: relationships survive a snapshot round-trip (db-mvcc.test.ts). 84 generation/temporal/visibility tests green; tsc clean.
This commit is contained in:
parent
3783e61b30
commit
4d0b64f455
4 changed files with 72 additions and 2 deletions
|
|
@ -6408,8 +6408,23 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
await this.storage.restoreFromDirectory(path)
|
||||
await this.generationStore.reopenAfterRestore(floorGeneration)
|
||||
|
||||
// Every in-memory index is now stale — rebuild all three from the
|
||||
// restored canonical records (each rebuild clears its own state first).
|
||||
// If the entity-id mapper is a NATIVE provider with a `rebuild()`, reload it
|
||||
// from the restored snapshot FIRST. The graph index resolves every verb
|
||||
// endpoint (sourceId/targetId → stable int) through this mapper, so a native
|
||||
// adjacency (keyed on those ints) needs the mapper to reflect the snapshot's
|
||||
// assignments BEFORE graphIndex.rebuild() — otherwise edges resolve to
|
||||
// stale/missing ints and are silently dropped (CTX-BR-RESTORE-REBUILD). The
|
||||
// JS mapper has no `rebuild()` and needs no reload: MetadataIndex.rebuild()
|
||||
// re-derives it from the restored entities via append-only getOrAssign,
|
||||
// consistently with the bitmaps it builds.
|
||||
const idMapper = this.metadataIndex.getIdMapper() as { rebuild?: () => Promise<void> }
|
||||
if (typeof idMapper.rebuild === 'function') {
|
||||
await idMapper.rebuild()
|
||||
}
|
||||
|
||||
// Every in-memory index is now stale — rebuild all three from the restored
|
||||
// canonical records (each rebuild clears its own state first). The graph
|
||||
// rebuild resolves endpoints via the (now-reloaded, for native) mapper.
|
||||
await Promise.all([
|
||||
this.metadataIndex.rebuild(),
|
||||
this.index.rebuild(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue