Skip to content

fix: close MCP audit SQLite connections on shutdown - #1348

Merged
diegosouzapw merged 1 commit into
diegosouzapw:release/v3.6.7from
rdself:coder/fix-sqlite-wal-shutdown
Apr 16, 2026
Merged

diegosouzapw merged 1 commit into
diegosouzapw:release/v3.6.7from
rdself:coder/fix-sqlite-wal-shutdown

Conversation

@rdself

@rdself rdself commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • close the MCP audit SQLite connection during shutdown and stdio server teardown
  • checkpoint the audit connection before close so WAL changes are flushed back into storage.sqlite
  • align the default Docker Compose stop grace period with the app shutdown window
  • add regression coverage for audit database shutdown behavior

Root Cause

OmniRoute already checkpoints and closes the main SQLite singleton during graceful shutdown, but the MCP audit logger kept a separate better-sqlite3 connection open without a matching shutdown path. That extra connection could keep storage.sqlite-wal and storage.sqlite-shm on disk after an otherwise normal stop, especially in Docker when the container stop timeout was shorter than the app shutdown timeout.

Impact

  • normal shutdown now closes every known SQLite connection involved in the app process
  • docker stop and docker compose down have enough time to finish WAL cleanup with the bundled Compose setup
  • WAL sidecar files can still remain after hard kills or if another external process keeps the database open, which matches normal SQLite WAL behavior

Validation

  • ./node_modules/.bin/vitest run --config vitest.mcp.config.ts open-sse/mcp-server/__tests__/audit.test.ts open-sse/mcp-server/__tests__/essentialTools.test.ts
  • ./node_modules/.bin/eslint open-sse/mcp-server/audit.ts open-sse/mcp-server/server.ts src/lib/gracefulShutdown.ts open-sse/mcp-server/__tests__/audit.test.ts
  • ./node_modules/.bin/tsc --pretty false -p tsconfig.typecheck-core.json

Copilot AI review requested due to automatic review settings April 16, 2026 18:45
@rdself
rdself requested a review from diegosouzapw as a code owner April 16, 2026 18:45
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses SQLite WAL sidecar files persisting after shutdown by ensuring the MCP audit logger’s separate better-sqlite3 connection is checkpointed and closed during teardown, and by aligning Docker Compose stop timing with the app’s graceful shutdown window.

Changes:

  • Add closeAuditDb() to checkpoint/close the MCP audit SQLite connection during shutdown and stdio server teardown.
  • Add Vitest regression tests covering audit DB checkpoint/close behavior (including checkpoint failure).
  • Increase Docker Compose stop_grace_period to 40s to accommodate the shutdown timeout and cleanup.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/lib/gracefulShutdown.ts Invokes MCP audit DB shutdown alongside the main DB shutdown during graceful cleanup.
open-sse/mcp-server/server.ts Ensures audit DB is closed when the stdio MCP server exits.
open-sse/mcp-server/audit.ts Adds explicit WAL checkpoint + close logic for the audit DB connection.
open-sse/mcp-server/tests/audit.test.ts Adds regression coverage for audit DB shutdown behavior via mocked better-sqlite3.
docker-compose.yml Extends stop grace period to allow cleanup to complete in container stops.

Comment on lines +177 to +202
export function closeAuditDb(): boolean {
if (!db) return false;

const database = db;
db = null;

try {
try {
if (database.open !== false) {
database.pragma("wal_checkpoint(TRUNCATE)");
}
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
console.warn("[MCP Audit] WAL checkpoint failed during close:", message);
}
} finally {
try {
database.close();
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
console.warn("[MCP Audit] Failed to close database:", message);
}
}

return true;
}
Comment on lines 147 to +188
async function getDb(): Promise<AuditDatabase | null> {
if (db) return db;

try {
// Try importing the db module from the main app
const { homedir } = await import("node:os");
const { join } = await import("node:path");
const { existsSync } = await import("node:fs");

const dbPath = process.env.DATA_DIR
? join(process.env.DATA_DIR, "storage.sqlite")
: join(homedir(), ".omniroute", "storage.sqlite");

if (!existsSync(dbPath)) {
console.error(`[MCP Audit] Database not found at ${dbPath} — audit logging disabled`);
return null;
}

const Database = (await import("better-sqlite3")).default as unknown as new (
dbPath: string
) => AuditDatabase;
db = new Database(dbPath);
return db;
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
console.error("[MCP Audit] Failed to connect to database:", message);
return null;
}
}

export function closeAuditDb(): boolean {
if (!db) return false;

const database = db;
db = null;

try {
try {
if (database.open !== false) {
database.pragma("wal_checkpoint(TRUNCATE)");
}
} catch (err: unknown) {
Comment on lines +23 to +34
let dbFile: string;

beforeEach(() => {
vi.resetModules();
dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-mcp-audit-"));
dbFile = path.join(dataDir, "storage.sqlite");
fs.writeFileSync(dbFile, "");
process.env.DATA_DIR = dataDir;
});

afterEach(() => {
delete process.env.DATA_DIR;
Comment on lines +99 to +105
const [{ closeAuditDb }, { closeDbInstance }] = await Promise.all([
import("@omniroute/open-sse/mcp-server/audit.ts"),
import("@/lib/db/core"),
]);
if (closeAuditDb()) {
console.log("[Shutdown] MCP audit database checkpointed and closed.");
}
@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.6.7 April 16, 2026 19:03
@diegosouzapw
diegosouzapw merged commit bc7fb96 into diegosouzapw:release/v3.6.7 Apr 16, 2026
2 checks passed
@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks @rdself for this great contribution! 🎉 This PR has been evaluated and successfully integrated into the release/v3.6.7 branch and will be part of the final production release. We appreciate your effort!

@rdself
rdself deleted the coder/fix-sqlite-wal-shutdown branch April 20, 2026 05:35
@diegosouzapw diegosouzapw mentioned this pull request Apr 22, 2026
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants