feat(FN-3173): add SQLite WAL tuning, integrity checks, and agent log batch
This merge brings FN-3173's SQLite stability improvements: WAL tuning pragmas for better concurrency, periodic integrity checks with self-healing recovery, and batched agent log writes to reduce I/O overhead. It also includes a new cron-runner for scheduled maintenance tasks, TUI mouse wheel scrolli Fusion-Task-Id: FN-3173
This commit is contained in:
@@ -51,6 +51,29 @@ describe("Database", () => {
|
||||
expect(row.foreign_keys).toBe(1);
|
||||
});
|
||||
|
||||
it("sets WAL tuning pragmas for disk-backed databases", () => {
|
||||
const synchronous = db.prepare("PRAGMA synchronous").get() as { synchronous: number };
|
||||
const autoCheckpoint = db.prepare("PRAGMA wal_autocheckpoint").get() as { wal_autocheckpoint: number };
|
||||
const journalSizeLimit = db.prepare("PRAGMA journal_size_limit").get() as { journal_size_limit: number };
|
||||
|
||||
expect(synchronous.synchronous).toBe(1); // NORMAL
|
||||
expect(autoCheckpoint.wal_autocheckpoint).toBe(100);
|
||||
expect(journalSizeLimit.journal_size_limit).toBe(4_194_304);
|
||||
});
|
||||
|
||||
it("does not force WAL tuning pragmas for in-memory databases", () => {
|
||||
const memDb = new Database(fusionDir, { inMemory: true });
|
||||
memDb.init();
|
||||
|
||||
const autoCheckpoint = memDb.prepare("PRAGMA wal_autocheckpoint").get() as { wal_autocheckpoint: number };
|
||||
const journalSizeLimit = memDb.prepare("PRAGMA journal_size_limit").get() as { journal_size_limit: number };
|
||||
|
||||
expect(autoCheckpoint.wal_autocheckpoint).toBe(1000);
|
||||
expect(journalSizeLimit.journal_size_limit).toBe(-1);
|
||||
|
||||
memDb.close();
|
||||
});
|
||||
|
||||
it("creates all expected tables", () => {
|
||||
const tables = db.prepare(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
|
||||
@@ -409,6 +432,28 @@ describe("Database", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("integrity check", () => {
|
||||
it("returns ok for healthy databases and leaves corruption flag false", () => {
|
||||
expect(db.corruptionDetected).toBe(false);
|
||||
expect(db.integrityCheck()).toEqual({ ok: true });
|
||||
});
|
||||
|
||||
it("keeps corruptionDetected false after init for healthy database", () => {
|
||||
const diskDb = new Database(fusionDir);
|
||||
diskDb.init();
|
||||
expect(diskDb.corruptionDetected).toBe(false);
|
||||
diskDb.close();
|
||||
});
|
||||
|
||||
it("skips integrity check side effects for in-memory databases", () => {
|
||||
const memDb = new Database(fusionDir, { inMemory: true });
|
||||
memDb.init();
|
||||
expect(memDb.integrityCheck()).toEqual({ ok: true });
|
||||
expect(memDb.corruptionDetected).toBe(false);
|
||||
memDb.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe("foreign key cascade across reopen", () => {
|
||||
it("cascade delete works after closing and reopening the database", () => {
|
||||
const now = new Date().toISOString();
|
||||
|
||||
@@ -4141,6 +4141,31 @@ Task with acceptance criteria
|
||||
expect(events[0].taskId).toBe(task.id);
|
||||
});
|
||||
|
||||
it("appendAgentLogBatch inserts all entries and emits per-entry events", async () => {
|
||||
const task = await createTestTask();
|
||||
const events: any[] = [];
|
||||
store.on("agent:log", (entry) => events.push(entry));
|
||||
|
||||
await store.appendAgentLogBatch([
|
||||
{ taskId: task.id, text: "batch 1", type: "text" },
|
||||
{ taskId: task.id, text: "tool", type: "tool", detail: "read file", agent: "executor" },
|
||||
]);
|
||||
|
||||
const logs = await store.getAgentLogs(task.id);
|
||||
expect(logs).toHaveLength(2);
|
||||
expect(logs.map((entry) => entry.text)).toEqual(["batch 1", "tool"]);
|
||||
expect(events).toHaveLength(2);
|
||||
expect(events[1]).toMatchObject({ text: "tool", type: "tool", detail: "read file", agent: "executor" });
|
||||
});
|
||||
|
||||
it("appendAgentLogBatch with empty entries is a no-op", async () => {
|
||||
const task = await createTestTask();
|
||||
|
||||
await store.appendAgentLogBatch([]);
|
||||
|
||||
expect(await store.getAgentLogCount(task.id)).toBe(0);
|
||||
});
|
||||
|
||||
it("appendAgentLog writes detail when provided", async () => {
|
||||
const task = await createTestTask();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user