openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
75 lines (74 loc) • 3.07 kB
JavaScript
import { s as asFiniteNumber } from "./number-coercion-CLj0HTDM.js";
import { t as openNodeSqliteDatabase } from "./node-sqlite-BpQX3W0e.js";
import { s as resolveOpenClawStateSqlitePath } from "./openclaw-state-db-schema-version-c1ZL6JGz.js";
import { y as listOpenClawRegisteredAgentDatabases } from "./openclaw-agent-db-maintenance-wTIy-jt-.js";
import "./openclaw-agent-db-CWtDoRbC.js";
import { t as note } from "./note-DqHk3fA1.js";
import { n as formatBytes } from "./doctor-disk-space-qV5PlSCz.js";
import fs from "node:fs";
//#region src/commands/doctor-db-bloat.ts
const BLOAT_MIN_FILE_BYTES = 134217728;
const BLOAT_MIN_FREE_BYTES = 33554432;
const BLOAT_FREE_RATIO = .25;
const LARGE_DB_WARN_BYTES = 1073741824;
function readSqliteBloatStats(pathname) {
let fileBytes;
try {
fileBytes = fs.statSync(pathname, { throwIfNoEntry: false })?.size ?? 0;
} catch {
return null;
}
if (fileBytes <= 0) return null;
let db;
try {
db = openNodeSqliteDatabase(pathname, { readOnly: true });
const pageSize = readPragmaNumber(db, "page_size") ?? 4096;
const freelistCount = readPragmaNumber(db, "freelist_count") ?? 0;
const autoVacuum = readPragmaNumber(db, "auto_vacuum") ?? 0;
return {
fileBytes,
freeBytes: freelistCount * pageSize,
incrementalAutoVacuum: autoVacuum === 2
};
} catch {
return null;
} finally {
db?.close();
}
}
function readPragmaNumber(db, pragma) {
const row = db.prepare(`PRAGMA ${pragma}`).get();
return asFiniteNumber(row?.[pragma]) ?? null;
}
function describeBloat(label, stats) {
const freeRatio = stats.fileBytes > 0 ? stats.freeBytes / stats.fileBytes : 0;
if (stats.fileBytes >= BLOAT_MIN_FILE_BYTES && stats.freeBytes >= BLOAT_MIN_FREE_BYTES && freeRatio >= BLOAT_FREE_RATIO) {
const remedy = stats.incrementalAutoVacuum ? "incremental vacuum will release it gradually" : "run `VACUUM` offline (gateway stopped) to reclaim it";
return `${label}: ${formatBytes(stats.fileBytes)} on disk with ${formatBytes(stats.freeBytes)} reclaimable free pages; ${remedy}.`;
}
if (stats.fileBytes >= LARGE_DB_WARN_BYTES) return `${label}: ${formatBytes(stats.fileBytes)} on disk; review session/transcript retention settings if growth is unexpected.`;
return null;
}
function collectSqliteBloatWarnings(deps) {
const env = deps?.env ?? process.env;
const warnings = [];
const stateStats = readSqliteBloatStats(resolveOpenClawStateSqlitePath(env));
if (stateStats) {
const warning = describeBloat("state DB", stateStats);
if (warning) warnings.push(warning);
}
for (const registered of listOpenClawRegisteredAgentDatabases({ env })) {
const stats = readSqliteBloatStats(registered.path);
if (!stats) continue;
const warning = describeBloat(`agent DB (${registered.agentId})`, stats);
if (warning) warnings.push(warning);
}
return warnings;
}
function noteSqliteDatabaseBloat(_cfg, deps) {
const warnings = collectSqliteBloatWarnings(deps);
if (warnings.length === 0) return;
note(warnings.join("\n"), "SQLite database size");
}
//#endregion
export { noteSqliteDatabaseBloat };