fix db storage tuning settings (#4834)

Integrated into release/v3.8.36
This commit is contained in:
Randi
2026-06-23 21:57:26 -04:00
committed by GitHub
parent e99e2887bf
commit 459ffcf2c0
13 changed files with 420 additions and 75 deletions

View File

@@ -25,6 +25,7 @@ _In development — bullets added per PR; finalized at release._
- **Dashboard**: remove the dead, unconditional `useLiveRequests()` call from `HomePageClient.tsx` — it crashed the `/home` page in production builds with `ReferenceError: useLiveRequests is not defined` (#4759, #4745) and opened the live-dashboard WebSocket even when Provider Topology was hidden (#4596). The live feed remains owned by the settings-gated `HomeProviderTopologySection` ([#4761](https://github.com/diegosouzapw/OmniRoute/pull/4761) — thanks @diegosouzapw).
- **Providers dashboard**: dedupe provider nodes by id when adding a compatible provider (`upsertProviderNodeById`) so the same provider can no longer appear twice and no-op adds don't invalidate the compatible-provider memo ([#4768](https://github.com/diegosouzapw/OmniRoute/pull/4768) — closes #4746, thanks @diegosouzapw).
- **Storage VACUUM**: the scheduled VACUUM job now follows the Storage page settings (`scheduledVacuum` / `vacuumHour`) as the single source of truth; the legacy env-flag control path was removed ([#4726](https://github.com/diegosouzapw/OmniRoute/pull/4726) — thanks @rdself).
- **Storage SQLite tuning**: `Cache Size` is now a positive KiB setting (for example, `16384`) that applies to SQLite as `PRAGMA cache_size = -16384`; Page Size and Cache Size changes are applied to the live database instead of being persisted only in the settings table.
- **Tiers**: no-auth providers are now counted as free, and the free-tier filter returns an empty set instead of falling through to every provider ([#4753](https://github.com/diegosouzapw/OmniRoute/pull/4753) — thanks @megamen32 / @diegosouzapw).
- **Combos**: auto-promote `zeroLatencyOptimizationsEnabled` so legacy configs (pre-3.8.33 `fallbackCompressionMode="lite"`) round-trip cleanly on the first GUI edit ([#4774](https://github.com/diegosouzapw/OmniRoute/pull/4774) — thanks @KooshaPari / @diegosouzapw).

View File

@@ -40,9 +40,10 @@ For **single-user, single-instance** deployments (the primary OmniRoute use case
```ts
// src/lib/db/core.ts
db.pragma("journal_mode = WAL");
db.pragma("busy_timeout = 5000");
db.pragma("busy_timeout = 2000");
db.pragma("synchronous = NORMAL");
db.pragma("cache_size = -2048");
// Settings > System & Storage > Cache Size is applied as KiB.
db.pragma("cache_size = -16384");
```
WAL allows **concurrent reads** during writes — important for the dashboard, which queries while requests are being recorded.

View File

@@ -57,6 +57,7 @@ export const INTENTIONALLY_INTERNAL = new Set([
"migrationRunner", // db-internal: importado por db/core.ts (runMigrations ao inicializar o DB)
"notion", // intentionally-internal: settings/notion API route + open-sse/mcp-server/tools/notionTools.ts
"obsidian", // intentionally-internal: src/lib/obsidianSync.ts + settings/obsidian route + MCP obsidianTools.ts
"optimizationSettings", // db-internal: imported by db/core.ts for SQLite PRAGMA application helpers that require the live adapter
"pluginMetrics", // DEAD? (production): write path não foi conectado ainda (documentado no cabeçalho do módulo); testado por tests/unit/plugins-metrics.test.ts
"prompts", // DEAD? (production): zero callers de produção encontrados; domínio domain/prompts.ts é independente; testado por tests/integration/proxy-pipeline.test.ts
"providerNodeSelect", // db-internal: importado só por db/providers.ts (selectProviderNodeForConnection — lógica pura de seleção de provider node split do providers.ts, #4421)

View File

@@ -1030,18 +1030,18 @@ export default function SystemStorageTab() {
/>
</div>
<div>
<label className="block text-xs text-text-muted mb-1">
Cache Size (KB, negative = % of RAM)
</label>
<label className="block text-xs text-text-muted mb-1">Cache Size (KB)</label>
<input
type="number"
min="1"
step="1024"
value={dbSettings.optimization.cacheSize}
onChange={(e) =>
setDbSettings({
...dbSettings,
optimization: {
...dbSettings.optimization,
cacheSize: parseInt(e.target.value) || -2000,
cacheSize: parseInt(e.target.value) || 16384,
},
})
}

View File

@@ -18,6 +18,15 @@ import { runMigrations } from "./migrationRunner";
import { runDbHealthCheck } from "./healthCheck";
import { resetAllDbModuleState } from "./stateReset";
import { parseStoredPayload } from "../logPayloads";
import { DEFAULT_DATABASE_SETTINGS, type DatabaseSettings } from "@/types/databaseSettings";
import {
applyDatabaseOptimizationSettingsForDb,
applyStoredDatabaseOptimizationSettings,
getAutoVacuumModeForDb,
setAutoVacuumForDb,
setCacheSizeForDb,
setPageSizeForDb,
} from "./optimizationSettings";
import {
buildArtifactRelativePath,
writeCallArtifact,
@@ -29,6 +38,7 @@ import { invalidateDbCache } from "./readCache";
type SqliteDatabase = SqliteAdapter;
type JsonRecord = Record<string, unknown>;
type CheckpointMode = "PASSIVE" | "FULL" | "RESTART" | "TRUNCATE";
type DatabaseOptimizationSettings = DatabaseSettings["optimization"];
type PreservedTableSnapshot = {
table: string;
rowCount: number;
@@ -1340,7 +1350,7 @@ export function getDbInstance(): SqliteDatabase {
// contended op can no longer freeze the loop past the host watchdog's 6s liveness probe.
db.pragma("busy_timeout = 2000");
db.pragma("synchronous = NORMAL");
db.pragma("cache_size = -2048");
db.pragma(`cache_size = -${DEFAULT_DATABASE_SETTINGS.optimization.cacheSize}`);
db.exec(SCHEMA_SQL);
ensureProviderConnectionsColumns(db);
ensureUsageHistoryColumns(db);
@@ -1361,6 +1371,8 @@ export function getDbInstance(): SqliteDatabase {
runMigrations(db, { isNewDb });
applyStoredDatabaseOptimizationSettings(db);
offloadLegacyCallLogDetails(db);
// Auto-migrate from db.json if exists
@@ -1731,44 +1743,16 @@ function migrateFromJson(db: SqliteDatabase, jsonPath: string) {
// ──────────────── Auto-Vacuum Management ────────────────
export function applyDatabaseOptimizationSettings(settings: DatabaseOptimizationSettings): void {
applyDatabaseOptimizationSettingsForDb(getDbInstance(), settings, { applyPersistent: true });
}
export function setAutoVacuum(mode: "NONE" | "FULL" | "INCREMENTAL"): void {
const db = getDbInstance();
const currentMode = db.pragma("auto_vacuum", { simple: true }) as number;
const modeMap: Record<string, number> = {
NONE: 0,
FULL: 1,
INCREMENTAL: 2,
};
const targetMode = modeMap[mode];
if (currentMode === targetMode) {
console.log(`[DB] auto_vacuum already set to ${mode}`);
return;
}
console.log(`[DB] Changing auto_vacuum from ${currentMode} to ${mode} (${targetMode})`);
db.pragma(`auto_vacuum = ${targetMode}`);
db.exec("VACUUM");
const newMode = db.pragma("auto_vacuum", { simple: true }) as number;
console.log(`[DB] auto_vacuum changed to ${newMode}`);
setAutoVacuumForDb(getDbInstance(), mode);
}
export function getAutoVacuumMode(): "NONE" | "FULL" | "INCREMENTAL" {
const db = getDbInstance();
const mode = db.pragma("auto_vacuum", { simple: true }) as number;
const modeMap: Record<number, "NONE" | "FULL" | "INCREMENTAL"> = {
0: "NONE",
1: "FULL",
2: "INCREMENTAL",
};
return modeMap[mode] || "NONE";
return getAutoVacuumModeForDb(getDbInstance());
}
export function runManualVacuum(): { success: boolean; duration: number; error?: string } {
@@ -1790,35 +1774,9 @@ export function runManualVacuum(): { success: boolean; duration: number; error?:
}
export function setPageSize(pageSize: number): void {
const db = getDbInstance();
const currentPageSize = db.pragma("page_size", { simple: true }) as number;
if (currentPageSize === pageSize) {
console.log(`[DB] page_size already set to ${pageSize}`);
return;
}
console.log(`[DB] Changing page_size from ${currentPageSize} to ${pageSize}`);
db.pragma(`page_size = ${pageSize}`);
db.exec("VACUUM");
const newPageSize = db.pragma("page_size", { simple: true }) as number;
console.log(`[DB] page_size changed to ${newPageSize}`);
setPageSizeForDb(getDbInstance(), pageSize);
}
export function setCacheSize(cacheSizeKb: number): void {
const db = getDbInstance();
const currentCacheSize = db.pragma("cache_size", { simple: true }) as number;
const targetCacheSize = -cacheSizeKb;
if (currentCacheSize === targetCacheSize) {
console.log(`[DB] cache_size already set to ${cacheSizeKb}KB`);
return;
}
console.log(`[DB] Changing cache_size from ${Math.abs(currentCacheSize)}KB to ${cacheSizeKb}KB`);
db.pragma(`cache_size = ${targetCacheSize}`);
const newCacheSize = db.pragma("cache_size", { simple: true }) as number;
console.log(`[DB] cache_size changed to ${Math.abs(newCacheSize)}KB`);
setCacheSizeForDb(getDbInstance(), cacheSizeKb);
}

View File

@@ -3,7 +3,7 @@ import fs from "node:fs";
import { DEFAULT_DATABASE_SETTINGS, type DatabaseSettings } from "@/types/databaseSettings";
import { backupDbFile } from "./backup";
import { DATA_DIR, SQLITE_FILE, getDbInstance } from "./core";
import { DATA_DIR, SQLITE_FILE, applyDatabaseOptimizationSettings, getDbInstance } from "./core";
import { invalidateDbCache } from "./readCache";
import { getDatabaseStats } from "./stats";
import { getState as getVacuumSchedulerState, refreshVacuumScheduler } from "./vacuumScheduler";
@@ -95,6 +95,15 @@ function toBooleanSetting(value: unknown): boolean | null {
return null;
}
function normalizeOptimizationSettings(settings: UserDatabaseSettings) {
const fallback = DEFAULT_DATABASE_SETTINGS.optimization.cacheSize;
const numericCacheSize = Number(settings.optimization.cacheSize);
settings.optimization.cacheSize =
Number.isFinite(numericCacheSize) && numericCacheSize > 0
? Math.min(1000000, Math.floor(numericCacheSize))
: fallback;
}
function readNamespace(namespace: string): Record<string, unknown> {
const db = getDbInstance();
const rows = db
@@ -220,6 +229,7 @@ export function getUserDatabaseSettings(): UserDatabaseSettings {
mergeTopLevelSections(settings, mainSettings);
mergeDatabaseSettingsNamespace(settings, readNamespace(DATABASE_SETTINGS_NAMESPACE));
mergeRuntimeLogSettings(settings, mainSettings);
normalizeOptimizationSettings(settings);
return settings;
}
@@ -259,6 +269,7 @@ export function updateDatabaseSettings(
mergeSectionObject(nextSettings, section, updates[section]);
}
}
normalizeOptimizationSettings(nextSettings);
const db = getDbInstance();
const insert = db.prepare(
@@ -289,7 +300,10 @@ export function updateDatabaseSettings(
backupDbFile("pre-write");
invalidateDbCache("settings");
if (optimizationUpdated) refreshVacuumScheduler();
if (optimizationUpdated) {
applyDatabaseOptimizationSettings(nextSettings.optimization);
refreshVacuumScheduler();
}
return nextSettings;
}

View File

@@ -40,5 +40,5 @@ INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES ('databaseSetting
INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES ('databaseSettings', 'autoVacuumMode', '"INCREMENTAL"');
INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES ('databaseSettings', 'scheduledVacuum', '"weekly"');
INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES ('databaseSettings', 'pageSize', '4096');
INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES ('databaseSettings', 'cacheSize', '10000');
INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES ('databaseSettings', 'cacheSize', '16384');
INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES ('databaseSettings', 'mmapSize', '268435456');

View File

@@ -0,0 +1,10 @@
-- 104_normalize_database_cache_size.sql
-- Normalize the old SQLite cache-size default to the new UI contract:
-- databaseSettings.optimization.cacheSize is a positive KiB value, e.g. 16384.
-- Only legacy defaults are rewritten; custom positive values are preserved.
UPDATE key_value
SET value = '16384'
WHERE namespace = 'databaseSettings'
AND key IN ('cacheSize', 'optimization.cacheSize')
AND TRIM(value) IN ('-2000', '"-2000"', '10000', '"10000"');

View File

@@ -0,0 +1,284 @@
import { DEFAULT_DATABASE_SETTINGS, type DatabaseSettings } from "@/types/databaseSettings";
import type { SqliteAdapter } from "./adapters/types";
type SqliteDatabase = SqliteAdapter;
type DatabaseOptimizationSettings = DatabaseSettings["optimization"];
type AutoVacuumMode = DatabaseOptimizationSettings["autoVacuumMode"];
const AUTO_VACUUM_MODE_TO_PRAGMA: Record<AutoVacuumMode, number> = {
NONE: 0,
FULL: 1,
INCREMENTAL: 2,
};
const PRAGMA_TO_AUTO_VACUUM_MODE: Record<number, AutoVacuumMode> = {
0: "NONE",
1: "FULL",
2: "INCREMENTAL",
};
function parseKeyValueJson(raw: string | null | undefined): unknown {
if (raw === null || raw === undefined) return undefined;
try {
return JSON.parse(raw);
} catch {
return raw;
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function normalizeAutoVacuumMode(value: unknown, fallback: AutoVacuumMode): AutoVacuumMode {
return typeof value === "string" && value in AUTO_VACUUM_MODE_TO_PRAGMA
? (value as AutoVacuumMode)
: fallback;
}
function normalizePageSizeBytes(value: unknown, fallback: number): number {
const numeric = Number(value);
if (!Number.isFinite(numeric)) return fallback;
const pageSize = Math.floor(numeric);
if (pageSize < 512 || pageSize > 65536 || pageSize % 512 !== 0) return fallback;
return pageSize;
}
function normalizeVacuumHour(value: unknown, fallback: number): number {
const numeric = Number(value);
if (!Number.isFinite(numeric)) return fallback;
return Math.min(23, Math.max(0, Math.floor(numeric)));
}
function normalizeStoredCacheSizeKb(value: unknown, fallback: number): number {
const numeric = Number(value);
if (!Number.isFinite(numeric)) return fallback;
const cacheSizeKb = Math.floor(numeric);
if (cacheSizeKb < 1 || cacheSizeKb > 1000000) return fallback;
return cacheSizeKb;
}
function requireCacheSizeKb(value: number): number {
if (!Number.isInteger(value) || value < 1 || value > 1000000) {
throw new Error("cache_size must be a positive KiB value between 1 and 1000000");
}
return value;
}
function mergeOptimizationSettings(
target: DatabaseOptimizationSettings,
value: unknown
): DatabaseOptimizationSettings {
if (!isRecord(value)) return target;
return {
...target,
autoVacuumMode: normalizeAutoVacuumMode(value.autoVacuumMode, target.autoVacuumMode),
scheduledVacuum:
typeof value.scheduledVacuum === "string" &&
["never", "daily", "weekly", "monthly"].includes(value.scheduledVacuum)
? (value.scheduledVacuum as DatabaseOptimizationSettings["scheduledVacuum"])
: target.scheduledVacuum,
vacuumHour: normalizeVacuumHour(value.vacuumHour, target.vacuumHour),
pageSize: normalizePageSizeBytes(value.pageSize, target.pageSize),
cacheSize: normalizeStoredCacheSizeKb(value.cacheSize, target.cacheSize),
optimizeOnStartup:
typeof value.optimizeOnStartup === "boolean"
? value.optimizeOnStartup
: target.optimizeOnStartup,
};
}
function readDatabaseOptimizationSettings(db: SqliteDatabase): DatabaseOptimizationSettings {
let settings: DatabaseOptimizationSettings = { ...DEFAULT_DATABASE_SETTINGS.optimization };
try {
const rows = db
.prepare("SELECT namespace, key, value FROM key_value WHERE namespace IN (?, ?)")
.all("settings", "databaseSettings") as Array<{
namespace: string;
key: string;
value: string | null;
}>;
const byNamespace: Record<string, Record<string, unknown>> = {
settings: {},
databaseSettings: {},
};
for (const row of rows) {
byNamespace[row.namespace] ??= {};
byNamespace[row.namespace][row.key] = parseKeyValueJson(row.value);
}
const mainSettings = byNamespace.settings ?? {};
const databaseSettingsValue = mainSettings.databaseSettings;
if (isRecord(databaseSettingsValue)) {
settings = mergeOptimizationSettings(settings, databaseSettingsValue.optimization);
}
settings = mergeOptimizationSettings(settings, mainSettings.optimization);
const databaseSettings = byNamespace.databaseSettings ?? {};
const optimizeOnStartup =
databaseSettings["optimization.optimizeOnStartup"] ?? databaseSettings.optimizeOnStartup;
settings = mergeOptimizationSettings(settings, databaseSettings.optimization);
settings = {
...settings,
autoVacuumMode: normalizeAutoVacuumMode(
databaseSettings["optimization.autoVacuumMode"] ?? databaseSettings.autoVacuumMode,
settings.autoVacuumMode
),
pageSize: normalizePageSizeBytes(
databaseSettings["optimization.pageSize"] ?? databaseSettings.pageSize,
settings.pageSize
),
cacheSize: normalizeStoredCacheSizeKb(
databaseSettings["optimization.cacheSize"] ?? databaseSettings.cacheSize,
settings.cacheSize
),
optimizeOnStartup:
typeof optimizeOnStartup === "boolean" ? optimizeOnStartup : settings.optimizeOnStartup,
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.warn(`[DB] Failed to read database optimization settings; using defaults: ${message}`);
}
return settings;
}
export function setCacheSizeForDb(db: SqliteDatabase, cacheSizeKb: number): void {
const normalizedCacheSizeKb = requireCacheSizeKb(cacheSizeKb);
const currentCacheSize = db.pragma("cache_size", { simple: true }) as number;
const targetCacheSize = -normalizedCacheSizeKb;
if (currentCacheSize === targetCacheSize) {
console.log(`[DB] cache_size already set to ${normalizedCacheSizeKb}KB`);
return;
}
console.log(
`[DB] Changing cache_size from ${Math.abs(currentCacheSize)}KB to ${normalizedCacheSizeKb}KB`
);
db.pragma(`cache_size = ${targetCacheSize}`);
const newCacheSize = db.pragma("cache_size", { simple: true }) as number;
if (newCacheSize !== targetCacheSize) {
throw new Error(
`cache_size change did not take effect (expected ${targetCacheSize}, got ${newCacheSize})`
);
}
console.log(`[DB] cache_size changed to ${Math.abs(newCacheSize)}KB`);
}
function applyPersistentOptimizationPragmas(
db: SqliteDatabase,
settings: DatabaseOptimizationSettings
): void {
const targetAutoVacuum = AUTO_VACUUM_MODE_TO_PRAGMA[settings.autoVacuumMode];
const targetPageSize = normalizePageSizeBytes(
settings.pageSize,
DEFAULT_DATABASE_SETTINGS.optimization.pageSize
);
const currentAutoVacuum = db.pragma("auto_vacuum", { simple: true }) as number;
const currentPageSize = db.pragma("page_size", { simple: true }) as number;
if (currentAutoVacuum === targetAutoVacuum && currentPageSize === targetPageSize) return;
const originalJournalMode = String(
db.pragma("journal_mode", { simple: true }) ?? ""
).toUpperCase();
const shouldRestoreWal = originalJournalMode === "WAL";
console.log(
`[DB] Applying persistent optimization settings ` +
`(auto_vacuum ${currentAutoVacuum}->${targetAutoVacuum}, ` +
`page_size ${currentPageSize}->${targetPageSize})`
);
try {
if (shouldRestoreWal) db.pragma("journal_mode = DELETE");
db.pragma(`auto_vacuum = ${targetAutoVacuum}`);
db.pragma(`page_size = ${targetPageSize}`);
db.exec("VACUUM");
} finally {
if (shouldRestoreWal) {
try {
db.pragma("journal_mode = WAL");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.warn(`[DB] Failed to restore WAL mode after optimization settings: ${message}`);
}
}
}
const newAutoVacuum = db.pragma("auto_vacuum", { simple: true }) as number;
const newPageSize = db.pragma("page_size", { simple: true }) as number;
if (newAutoVacuum !== targetAutoVacuum || newPageSize !== targetPageSize) {
throw new Error(
`database optimization settings did not take effect ` +
`(auto_vacuum expected ${targetAutoVacuum}, got ${newAutoVacuum}; ` +
`page_size expected ${targetPageSize}, got ${newPageSize})`
);
}
}
export function applyDatabaseOptimizationSettingsForDb(
db: SqliteDatabase,
settings: DatabaseOptimizationSettings,
options: { applyPersistent: boolean }
): void {
if (options.applyPersistent) applyPersistentOptimizationPragmas(db, settings);
setCacheSizeForDb(
db,
normalizeStoredCacheSizeKb(settings.cacheSize, DEFAULT_DATABASE_SETTINGS.optimization.cacheSize)
);
}
export function applyStoredDatabaseOptimizationSettings(db: SqliteDatabase): void {
const settings = readDatabaseOptimizationSettings(db);
// Startup can happen concurrently in test workers and clustered hosts. Only
// restore connection-local settings here; page_size/auto_vacuum require VACUUM
// and are applied synchronously when the Storage settings are saved.
applyDatabaseOptimizationSettingsForDb(db, settings, {
applyPersistent: false,
});
}
export function setAutoVacuumForDb(db: SqliteDatabase, mode: AutoVacuumMode): void {
const currentMode = db.pragma("auto_vacuum", { simple: true }) as number;
const targetMode = AUTO_VACUUM_MODE_TO_PRAGMA[mode];
if (currentMode === targetMode) {
console.log(`[DB] auto_vacuum already set to ${mode}`);
return;
}
applyPersistentOptimizationPragmas(db, {
...DEFAULT_DATABASE_SETTINGS.optimization,
autoVacuumMode: mode,
pageSize: db.pragma("page_size", { simple: true }) as number,
});
}
export function getAutoVacuumModeForDb(db: SqliteDatabase): AutoVacuumMode {
const mode = db.pragma("auto_vacuum", { simple: true }) as number;
return PRAGMA_TO_AUTO_VACUUM_MODE[mode] || "NONE";
}
export function setPageSizeForDb(db: SqliteDatabase, pageSize: number): void {
const currentPageSize = db.pragma("page_size", { simple: true }) as number;
const targetPageSize = normalizePageSizeBytes(
pageSize,
DEFAULT_DATABASE_SETTINGS.optimization.pageSize
);
if (currentPageSize === targetPageSize) {
console.log(`[DB] page_size already set to ${targetPageSize}`);
return;
}
applyPersistentOptimizationPragmas(db, {
...DEFAULT_DATABASE_SETTINGS.optimization,
autoVacuumMode: getAutoVacuumModeForDb(db),
pageSize: targetPageSize,
});
}

View File

@@ -400,7 +400,7 @@ export const databaseSettingsSchema = z
.or(z.literal("monthly")),
vacuumHour: z.number().int().min(0).max(23),
pageSize: z.number().multipleOf(512).min(512).max(65536),
cacheSize: z.number().int().min(-1000000).max(1000000),
cacheSize: z.number().int().positive().max(1000000),
optimizeOnStartup: z.boolean(),
}),

View File

@@ -118,7 +118,7 @@ export const DEFAULT_DATABASE_SETTINGS: Omit<DatabaseSettings, "location" | "sta
scheduledVacuum: "weekly",
vacuumHour: 2,
pageSize: 4096,
cacheSize: -2000,
cacheSize: 16384,
optimizeOnStartup: true,
},
};

View File

@@ -121,7 +121,7 @@ test("INTENTIONALLY_INTERNAL is exported from check-db-rules.mjs", () => {
assert.ok(INTENTIONALLY_INTERNAL.size > 0, "INTENTIONALLY_INTERNAL must not be empty");
});
test("INTENTIONALLY_INTERNAL contains the expected 29 audited modules", () => {
test("INTENTIONALLY_INTERNAL contains the expected 30 audited modules", () => {
const expected = [
"_rowTypes",
"accessTokens",
@@ -141,6 +141,7 @@ test("INTENTIONALLY_INTERNAL contains the expected 29 audited modules", () => {
"migrationRunner",
"notion",
"obsidian",
"optimizationSettings",
"pluginMetrics",
"prompts",
"providerNodeSelect",

View File

@@ -146,6 +146,81 @@ test("database log settings mirror the runtime pipeline toggle", async () => {
assert.equal(databaseSettings.getUserDatabaseSettings().logs.callLogPipelineEnabled, true);
});
test("database optimization settings apply SQLite cache size immediately", () => {
const current = databaseSettings.getUserDatabaseSettings();
databaseSettings.updateDatabaseSettings({
optimization: {
...current.optimization,
autoVacuumMode: core.getAutoVacuumMode(),
pageSize: 4096,
cacheSize: 16384,
},
});
const db = core.getDbInstance();
const stored = db
.prepare(
"SELECT value FROM key_value WHERE namespace = 'databaseSettings' AND key = 'optimization.cacheSize'"
)
.get() as { value: string } | undefined;
assert.equal(db.pragma("cache_size", { simple: true }), -16384);
assert.equal(JSON.parse(stored?.value ?? "null"), 16384);
assert.equal(databaseSettings.getUserDatabaseSettings().optimization.cacheSize, 16384);
});
test("database optimization settings apply SQLite page size immediately", () => {
const current = databaseSettings.getUserDatabaseSettings();
databaseSettings.updateDatabaseSettings({
optimization: {
...current.optimization,
autoVacuumMode: core.getAutoVacuumMode(),
pageSize: 8192,
cacheSize: 16384,
},
});
assert.equal(core.getDbInstance().pragma("page_size", { simple: true }), 8192);
assert.equal(databaseSettings.getUserDatabaseSettings().optimization.pageSize, 8192);
});
test("database optimization cache size is applied when the DB is reopened", () => {
const db = core.getDbInstance();
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('databaseSettings', ?, ?)"
).run("optimization.cacheSize", JSON.stringify(32768));
core.resetDbInstance();
const reopened = core.getDbInstance();
assert.equal(reopened.pragma("cache_size", { simple: true }), -32768);
});
test("database optimization rejects negative cache size through the API", async () => {
const current = databaseSettings.getUserDatabaseSettings();
const response = await databaseSettingsRoute.PATCH(
makeJsonRequest("PATCH", {
optimization: {
...current.optimization,
cacheSize: -2000,
},
}) as never
);
assert.equal(response.status, 400);
});
test("database settings reader normalizes legacy negative cache size to the positive default", () => {
const db = core.getDbInstance();
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('databaseSettings', ?, ?)"
).run("optimization.cacheSize", JSON.stringify(-2000));
assert.equal(databaseSettings.getUserDatabaseSettings().optimization.cacheSize, 16384);
});
test("purgeDetailedLogs deletes request_detail_logs", async () => {
const db = core.getDbInstance();
db.prepare("INSERT INTO request_detail_logs (id, timestamp, duration_ms) VALUES (?, ?, ?)").run(