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:
5
.changeset/agentstore-db-cache.md
Normal file
5
.changeset/agentstore-db-cache.md
Normal 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.
|
||||||
@@ -187,6 +187,20 @@ function resolveCreationRuntimeConfig(
|
|||||||
return rc;
|
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.
|
* AgentStore manages agent lifecycle with SQLite-backed persistence.
|
||||||
* Follows the same patterns as TaskStore for consistency.
|
* Follows the same patterns as TaskStore for consistency.
|
||||||
@@ -215,11 +229,25 @@ export class AgentStore extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get db(): Database {
|
private get db(): Database {
|
||||||
if (!this._db) {
|
if (this._db) return this._db;
|
||||||
this._db = new Database(this.rootDir, { inMemory: this.inMemoryDb });
|
|
||||||
|
if (this.inMemoryDb) {
|
||||||
|
this._db = new Database(this.rootDir, { inMemory: true });
|
||||||
this._db.init();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user