refactor(8.0): delete DataAPI — superseded by Db persist/restore + import API + stats
The legacy backup/import/export/stats facade (src/api/DataAPI.ts) drifted from the modern entity shape and every job it did now has a first-class surface. Delete it and brain.data(), and rewire the CLI: - data-stats → brain.stats() (full BrainyStats report: per-type breakdowns, indexed fields, index health, storage backend, writer lock, version) - clean → brain.clear() - export → alias of snapshot; a db.persist() snapshot is the full-fidelity export format (open with Brainy.load, load wholesale with brainy restore); external data ingestion remains brainy import (UniversalImportAPI) Rewiring clean onto brain.clear() exposed two real bugs, both fixed: - clear() left this.graphIndex undefined forever — any graph-touching call afterwards (relate, getNeighbors, stats) crashed. clear() now re-resolves the graph index exactly as init() does and re-wires the shared UUID↔int resolver, and re-resolves the metadata index with the same provider fallback as init(). - storage.clear() reset the legacy totals but not the per-type/subtype count rollups or id→type caches, so stats() reported phantom counts for deleted entities. Both adapters now delegate derived-state reset to reloadDerivedState(), the same path restore-from-snapshot uses. One-shot CLI commands (data-stats, clean, snapshot/export, restore, history, generation) now close the brain and exit explicitly — global cache timers otherwise keep the process alive holding the writer lock. Verified: build clean, 1383/1383 unit tests, 24/24 db-mvcc integration, plus an end-to-end CLI smoke (add → data-stats → export → clean → data-stats).
This commit is contained in:
parent
cc8037db10
commit
478fa176f2
12 changed files with 262 additions and 639 deletions
150
src/brainy.ts
150
src/brainy.ts
|
|
@ -4298,68 +4298,95 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Clear all data from the database
|
||||
* @description Clear ALL data from the database — entities, relationships,
|
||||
* blobs, and every derived index. The instance remains fully usable
|
||||
* afterwards: the vector, metadata, and graph indexes are re-resolved
|
||||
* exactly as on `init()`, and when the VFS was active a fresh VFS root
|
||||
* entity is re-created (so a thresholdless vector search against a cleared
|
||||
* store can still surface that one root entity).
|
||||
* @example
|
||||
* await brain.clear()
|
||||
*/
|
||||
async clear(): Promise<void> {
|
||||
await this.ensureInitialized()
|
||||
|
||||
// Clear storage
|
||||
await this.storage.clear()
|
||||
await this.storage.clear()
|
||||
|
||||
// Invalidate GraphAdjacencyIndex to prevent stale in-memory data
|
||||
// The index has LSMTree data and verbIdSet pointing to deleted entities.
|
||||
// Without this, relate()'s duplicate check uses stale data, potentially
|
||||
// allowing duplicate relationships or missing valid duplicates.
|
||||
if (typeof (this.storage as any).invalidateGraphIndex === 'function') {
|
||||
;(this.storage as any).invalidateGraphIndex()
|
||||
}
|
||||
this.graphIndex = undefined as any
|
||||
|
||||
// Reset index
|
||||
if ('clear' in this.index && typeof this.index.clear === 'function') {
|
||||
await this.index.clear()
|
||||
} else {
|
||||
// Recreate index using plugin factory when available
|
||||
this.index = this.createIndex()
|
||||
}
|
||||
|
||||
// Recreate metadata index to clear cached data
|
||||
const clearMetadataFactory = this.pluginRegistry.getProvider<(storage: StorageAdapter) => any>('metadataIndex')
|
||||
this.metadataIndex = clearMetadataFactory
|
||||
? clearMetadataFactory(this.storage)
|
||||
: new MetadataIndexManager(this.storage)
|
||||
await this.metadataIndex.init()
|
||||
|
||||
// Reset dimensions
|
||||
this.dimensions = undefined
|
||||
|
||||
// Clear any cached sub-APIs
|
||||
this._neural = undefined
|
||||
this._nlp = undefined
|
||||
this._tripleIntelligence = undefined
|
||||
|
||||
// Re-initialize the content-addressed blob store after storage.clear()
|
||||
// (clear() drops the BlobStorage instance). The VFS stores all file
|
||||
// content through it, so this must happen BEFORE VFS reinitialization.
|
||||
if (typeof (this.storage as any).initializeBlobStorage === 'function') {
|
||||
await (this.storage as any).initializeBlobStorage()
|
||||
}
|
||||
|
||||
// Reset VFS state - root entity was deleted by storage.clear()
|
||||
// Bug: VFS instance remained in memory pointing to deleted root entity
|
||||
if (this._vfs) {
|
||||
// Clear PathResolver caches (including UnifiedCache VFS entries)
|
||||
if ((this._vfs as any).pathResolver?.invalidateAllCaches) {
|
||||
(this._vfs as any).pathResolver.invalidateAllCaches()
|
||||
}
|
||||
// Recreate and reinitialize VFS so it's ready for use
|
||||
this._vfs = new VirtualFileSystem(this)
|
||||
await this._vfs.init()
|
||||
// _vfsInitialized remains true since we just initialized
|
||||
} else {
|
||||
// VFS was never used, reset flag for clean state
|
||||
this._vfsInitialized = false
|
||||
// Invalidate GraphAdjacencyIndex to prevent stale in-memory data
|
||||
// The index has LSMTree data and verbIdSet pointing to deleted entities.
|
||||
// Without this, relate()'s duplicate check uses stale data, potentially
|
||||
// allowing duplicate relationships or missing valid duplicates.
|
||||
if (typeof (this.storage as any).invalidateGraphIndex === 'function') {
|
||||
;(this.storage as any).invalidateGraphIndex()
|
||||
}
|
||||
|
||||
// Reset index
|
||||
if ('clear' in this.index && typeof this.index.clear === 'function') {
|
||||
await this.index.clear()
|
||||
} else {
|
||||
// Recreate index using plugin factory when available
|
||||
this.index = this.createIndex()
|
||||
}
|
||||
|
||||
// Recreate metadata index to clear cached data — mirror init()'s
|
||||
// resolution: provider factory first, then the JS implementation (with a
|
||||
// plugin-provided native entityIdMapper when registered).
|
||||
const clearMetadataFactory = this.pluginRegistry.getProvider<(storage: StorageAdapter) => any>('metadataIndex')
|
||||
if (clearMetadataFactory) {
|
||||
this.metadataIndex = clearMetadataFactory(this.storage)
|
||||
} else {
|
||||
const entityIdMapperFactory = this.pluginRegistry.getProvider<(storage: StorageAdapter) => any>('entityIdMapper')
|
||||
this.metadataIndex = new MetadataIndexManager(this.storage, {}, {
|
||||
entityIdMapper: entityIdMapperFactory ? entityIdMapperFactory(this.storage) : undefined,
|
||||
})
|
||||
}
|
||||
await this.metadataIndex.init()
|
||||
|
||||
// Re-resolve the graph index the same way init() does (provider factory,
|
||||
// else the storage layer's lazy singleton) and re-wire the shared
|
||||
// UUID ↔ int resolver. Without this, every graph-touching call after
|
||||
// clear() — relate(), getNeighbors(), stats() — would hit an undefined
|
||||
// index.
|
||||
const clearGraphFactory = this.pluginRegistry.getProvider<(storage: StorageAdapter) => any>('graphIndex')
|
||||
if (clearGraphFactory) {
|
||||
this.graphIndex = clearGraphFactory(this.storage)
|
||||
this.storage.setGraphIndex(this.graphIndex)
|
||||
} else {
|
||||
this.graphIndex = await (this.storage as any).getGraphIndex()
|
||||
}
|
||||
this.wireGraphIdResolver()
|
||||
|
||||
// Reset dimensions
|
||||
this.dimensions = undefined
|
||||
|
||||
// Clear any cached sub-APIs
|
||||
this._neural = undefined
|
||||
this._nlp = undefined
|
||||
this._tripleIntelligence = undefined
|
||||
|
||||
// Re-initialize the content-addressed blob store after storage.clear()
|
||||
// (clear() drops the BlobStorage instance). The VFS stores all file
|
||||
// content through it, so this must happen BEFORE VFS reinitialization.
|
||||
if (typeof (this.storage as any).initializeBlobStorage === 'function') {
|
||||
await (this.storage as any).initializeBlobStorage()
|
||||
}
|
||||
|
||||
// Reset VFS state - root entity was deleted by storage.clear()
|
||||
// Bug: VFS instance remained in memory pointing to deleted root entity
|
||||
if (this._vfs) {
|
||||
// Clear PathResolver caches (including UnifiedCache VFS entries)
|
||||
if ((this._vfs as any).pathResolver?.invalidateAllCaches) {
|
||||
(this._vfs as any).pathResolver.invalidateAllCaches()
|
||||
}
|
||||
// Recreate and reinitialize VFS so it's ready for use
|
||||
this._vfs = new VirtualFileSystem(this)
|
||||
await this._vfs.init()
|
||||
// _vfsInitialized remains true since we just initialized
|
||||
} else {
|
||||
// VFS was never used, reset flag for clean state
|
||||
this._vfsInitialized = false
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Migration API ───────────────────────────────────────────────
|
||||
|
|
@ -6231,19 +6258,6 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
return this._hub
|
||||
}
|
||||
|
||||
/**
|
||||
* Data Management API - backup, restore, import, export
|
||||
*/
|
||||
async data() {
|
||||
const { DataAPI } = await import('./api/DataAPI.js')
|
||||
return new DataAPI(
|
||||
this.storage,
|
||||
(id: string) => this.get(id),
|
||||
undefined, // No getRelation method yet
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Triple Intelligence System
|
||||
* Advanced pattern recognition and relationship analysis
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue