feat(FN-4048): add FusionDB vacuum helper

Added a `FusionDB.vacuum()` helper to compact the SQLite database, with accompanying regression tests covering VACUUM behavior and error handling in `packages/core/src/db.ts` and its test file.

Fusion-Task-Id: FN-4048
This commit is contained in:
Fusion
2026-05-11 22:25:34 -07:00
committed by gsxdsm
parent afa2240fd6
commit e3deba3681
3 changed files with 112 additions and 3 deletions

View File

@@ -10,7 +10,7 @@
import { DatabaseSync } from "./sqlite-adapter.js";
import { isAbsolute, join } from "node:path";
import { mkdirSync, existsSync } from "node:fs";
import { mkdirSync, existsSync, statSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { randomUUID } from "node:crypto";
import { DEFAULT_PROJECT_SETTINGS } from "./types.js";
@@ -22,6 +22,13 @@ import type { SteeringComment, TaskComment } from "./types.js";
/** A prepared SQL statement wrapping the node:sqlite StatementSync type. */
export type Statement = ReturnType<DatabaseSync["prepare"]>;
/** Result payload for explicit database compaction via `VACUUM`. */
export interface VacuumResult {
beforeBytes: number;
afterBytes: number;
durationMs: number;
}
// ── JSON Helpers ─────────────────────────────────────────────────────
/**
@@ -1298,6 +1305,50 @@ export class Database {
return rebuilt.status === 0;
}
/**
* Run WAL truncation + VACUUM and report compaction stats.
*
* In-memory databases no-op and return zeroed stats. Disk-backed databases
* sample file size before/after compaction, run `wal_checkpoint(TRUNCATE)`,
* and then run `VACUUM` while the connection is in EXCLUSIVE locking mode to
* prevent concurrent writes from other connections during maintenance.
*/
vacuum(): VacuumResult {
if (this.inMemory) {
return { beforeBytes: 0, afterBytes: 0, durationMs: 0 };
}
const beforeBytes = existsSync(this.dbPath) ? statSync(this.dbPath).size : 0;
const startedAt = Date.now();
this.db.exec("PRAGMA locking_mode=EXCLUSIVE");
try {
try {
this.walCheckpoint("TRUNCATE");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Database vacuum maintenance failed during WAL checkpoint (dbPath=${this.dbPath}): ${message}`);
}
try {
this.db.exec("VACUUM");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Database vacuum maintenance failed during VACUUM (dbPath=${this.dbPath}): ${message}`);
}
const afterBytes = existsSync(this.dbPath) ? statSync(this.dbPath).size : 0;
return {
beforeBytes,
afterBytes,
durationMs: Date.now() - startedAt,
};
} finally {
this.db.exec("PRAGMA locking_mode=NORMAL");
}
}
/**
* Initialize the database: create tables if they don't exist
* and seed meta values.