feat(FN-4083): add WAL enforcement, immediate write transactions, and concu
Hardened SQLite concurrent-write safety in `@fusion/core` by auditing WAL enforcement and adding immediate write transactions to prevent stale reads under load, with executor semaphore limit tests in `@fusion/engine` validating the concurrency bounds; added a new `store-concurrent-writes.test.ts` in Fusion-Task-Id: FN-4083 Fusion-Task-Lineage: adabe51a-a9c4-481a-850d-985c373df51a
This commit is contained in:
@@ -199,6 +199,32 @@ describe("AgentSemaphore", () => {
|
||||
expect(sem.activeCount).toBe(0);
|
||||
});
|
||||
|
||||
it("keeps executor-style write sections within the configured execute concurrency", async () => {
|
||||
const sem = new AgentSemaphore(2);
|
||||
let concurrentWrites = 0;
|
||||
let maxConcurrentWrites = 0;
|
||||
|
||||
const performWrite = (taskId: string) =>
|
||||
sem.run(async () => {
|
||||
concurrentWrites += 1;
|
||||
maxConcurrentWrites = Math.max(maxConcurrentWrites, concurrentWrites);
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
concurrentWrites -= 1;
|
||||
return taskId;
|
||||
}, PRIORITY_EXECUTE);
|
||||
|
||||
const completed = await Promise.all([
|
||||
performWrite("FN-1"),
|
||||
performWrite("FN-2"),
|
||||
performWrite("FN-3"),
|
||||
performWrite("FN-4"),
|
||||
]);
|
||||
|
||||
expect(completed).toEqual(["FN-1", "FN-2", "FN-3", "FN-4"]);
|
||||
expect(maxConcurrentWrites).toBe(2);
|
||||
expect(sem.activeCount).toBe(0);
|
||||
});
|
||||
|
||||
it("integration: shared semaphore limits triage + execution + merge together", async () => {
|
||||
const sem = new AgentSemaphore(2);
|
||||
let concurrent = 0;
|
||||
|
||||
@@ -65,9 +65,11 @@ describe("hydrateWorktreeDb", () => {
|
||||
const db = new DatabaseSync(join(worktree, ".fusion", "fusion.db"));
|
||||
const tasks = (db.prepare("SELECT COUNT(*) as c FROM tasks WHERE id IN ('FN-A','FN-B','FN-C')").get() as any).c;
|
||||
const docs = (db.prepare("SELECT COUNT(*) as c FROM task_documents WHERE taskId='FN-B'").get() as any).c;
|
||||
const journalMode = db.prepare("PRAGMA journal_mode").get() as { journal_mode: string };
|
||||
db.close();
|
||||
expect(tasks).toBe(3);
|
||||
expect(docs).toBe(1);
|
||||
expect(journalMode.journal_mode).toBe("wal");
|
||||
});
|
||||
|
||||
it("no-op when rootDir === worktreePath", async () => {
|
||||
|
||||
@@ -113,8 +113,10 @@ export async function hydrateWorktreeDb({
|
||||
}
|
||||
|
||||
srcDb = new DatabaseSync(srcDbPath);
|
||||
srcDb.exec("PRAGMA busy_timeout = 5000");
|
||||
dstDb = openWorktreeDbWithRecovery(dstDbPath, worktreePath);
|
||||
|
||||
dstDb.exec("PRAGMA busy_timeout = 5000");
|
||||
dstDb.exec("PRAGMA journal_mode = WAL");
|
||||
|
||||
const srcTaskCols = getColumns(srcDb, "tasks");
|
||||
|
||||
Reference in New Issue
Block a user