perf(core): opt-in in-memory SQLite for *-store tests

Adds an opt-in `inMemory` flag to `Database`/`ArchiveDatabase` (and
`{ inMemoryDb }` to TaskStore, AgentStore, RoutineStore,
AutomationStore, PluginStore) that swaps the on-disk fusion.db /
archive.db for SQLite's `:memory:` connection. Production callers
never set the flag, so behavior is unchanged.

Test files for each store now flip the flag in `beforeEach`. The
handful of tests that exercise cross-instance persistence (open store
A, close, open store B on same dir, expect data) construct disk-backed
stores explicitly inside the test body, marked with a comment at each
site.

Wall-clock impact:
- core:      69.4s → 18.5s  (3.7× faster, 3038 tests)
- dashboard: 156.6s → 30.0s (5.2× faster — improvement ripples through
                              any test that constructs a TaskStore)

The refactor eliminates the per-test SQLite open + WAL fsync + tmp
dir cleanup loop that dominated setup cost: ~50ms/test → ~5ms/test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-26 18:45:14 -07:00
parent e2bc644fd5
commit 6e4797ff14
17 changed files with 179 additions and 34 deletions

View File

@@ -82,6 +82,12 @@ export interface AgentStoreOptions {
rootDir?: string;
/** Optional TaskStore for checkout/release operations */
taskStore?: TaskStore;
/**
* Test-only: open the underlying SQLite DB as `:memory:` instead of a
* disk-backed file. Skips per-test fsync and WAL setup; mirrors the
* pattern in TaskStore. Production callers must leave this unset.
*/
inMemoryDb?: boolean;
}
/** Agent data as stored in SQLite JSON columns */
@@ -174,6 +180,7 @@ export class AgentStore extends EventEmitter {
private locks: Map<string, AgentLock> = new Map();
private _db: Database | null = null;
private taskStore?: TaskStore;
private readonly inMemoryDb: boolean;
constructor(options: AgentStoreOptions = {}) {
super();
@@ -187,11 +194,12 @@ export class AgentStore extends EventEmitter {
this.rootDir = options.rootDir ?? resolve(".fusion");
this.agentsDir = join(this.rootDir, "agents");
this.taskStore = options.taskStore;
this.inMemoryDb = options.inMemoryDb === true;
}
private get db(): Database {
if (!this._db) {
this._db = new Database(this.rootDir);
this._db = new Database(this.rootDir, { inMemory: this.inMemoryDb });
this._db.init();
}
return this._db;