perf(core): cache AgentStore SQLite connection per project

The dashboard re-instantiates AgentStore on every /api/agents request, and
each instance was opening a fresh Database — re-running schema migrations
and PRAGMA integrity_check on the full file. On a multi-hundred-MB DB that
was 3-7s per request and leaked file handles. Sharing a cached Database
keyed by rootDir reduces it to a one-time process cost.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-03 02:02:09 -07:00
parent 85ec574c25
commit 41bb6be0f8
2 changed files with 36 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Cache the AgentStore SQLite connection per project so the dashboard no longer reopens the database, re-runs migrations, and re-executes `PRAGMA integrity_check` on every `/api/agents` request. On large project databases this turned a sub-100ms call into multi-second latency that bled into every dashboard view fetching the agent list.

View File

@@ -187,6 +187,20 @@ function resolveCreationRuntimeConfig(
return rc;
}
/**
* Process-wide cache of initialized Database connections, keyed by absolute
* rootDir. Without this, callers that re-instantiate AgentStore per request
* (the dashboard does this in ~65 places) reopen the SQLite file, re-run the
* full schema migration, and re-execute `PRAGMA integrity_check` (a full table
* scan) every time — on a multi-hundred-MB DB that's seconds per request and
* leaks file handles. Sharing one Database object reduces all of that to a
* one-time cost per process.
*
* In-memory DBs are intentionally *not* cached: they're test-only and each
* test wants its own isolated `:memory:` connection.
*/
const agentStoreDbCache = new Map<string, Database>();
/**
* AgentStore manages agent lifecycle with SQLite-backed persistence.
* Follows the same patterns as TaskStore for consistency.
@@ -215,11 +229,25 @@ export class AgentStore extends EventEmitter {
}
private get db(): Database {
if (!this._db) {
this._db = new Database(this.rootDir, { inMemory: this.inMemoryDb });
if (this._db) return this._db;
if (this.inMemoryDb) {
this._db = new Database(this.rootDir, { inMemory: true });
this._db.init();
return this._db;
}
return this._db;
const cached = agentStoreDbCache.get(this.rootDir);
if (cached) {
this._db = cached;
return cached;
}
const fresh = new Database(this.rootDir, { inMemory: false });
fresh.init();
agentStoreDbCache.set(this.rootDir, fresh);
this._db = fresh;
return fresh;
}
/**