Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/db/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ interface TimedStatementOperationArgs<TValue> {
}

const DEFAULT_SLOW_DB_QUERY_LOG_THRESHOLD_MS = 100;
/** 256 MiB page cache. Negative cache_size is kibibytes. */
export const SQLITE_CACHE_SIZE_KIB = 262_144;
/** Memory-map the first 1 GiB of the database file. */
export const SQLITE_MMAP_SIZE_BYTES = 1_073_741_824;
export const SQLITE_BUSY_TIMEOUT_MS = 5_000;
const MAX_LOGGED_SQL_LENGTH = 1_000;
const SQL_TRUNCATION_SUFFIX = "...";
// Keep ORM-generated quoted identifiers intact. SQLite accepts double-quoted
Expand Down Expand Up @@ -171,6 +176,13 @@ export function createConnection(
sqlite.pragma("journal_mode = WAL");
// Enable foreign keys
sqlite.pragma("foreign_keys = ON");
// WAL + NORMAL: no fsync per commit. Power loss can drop the last
// transactions; it cannot corrupt the file. cache_size/mmap_size replace
// the 2 MiB default cache so a multi-GB database is not re-read per query.
sqlite.pragma("synchronous = NORMAL");
sqlite.pragma(`cache_size = -${SQLITE_CACHE_SIZE_KIB}`);
sqlite.pragma(`mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);
sqlite.pragma(`busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS}`);
instrumentSqliteClient(sqlite, options);

const db = drizzle({ client: sqlite, schema });
Expand Down
38 changes: 38 additions & 0 deletions packages/db/test/connection.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { eq } from "drizzle-orm";
import {
createConnection,
SQLITE_BUSY_TIMEOUT_MS,
SQLITE_CACHE_SIZE_KIB,
SQLITE_MMAP_SIZE_BYTES,
type SlowDbQueryLogger,
type SlowDbQueryLogFields,
} from "../src/connection.js";
Expand Down Expand Up @@ -124,4 +130,36 @@ describe("createConnection", () => {

db.$client.close();
});

it("applies the hot-path sqlite pragmas on a file database", () => {
const directory = mkdtempSync(join(tmpdir(), "bb-db-pragmas-"));
const db = createConnection(join(directory, "bb.db"));

try {
expect(
db.$client.prepare("PRAGMA cache_size").get() as {
cache_size: number;
},
).toEqual({ cache_size: -SQLITE_CACHE_SIZE_KIB });
expect(
db.$client.prepare("PRAGMA synchronous").get() as {
synchronous: number;
},
).toEqual({ synchronous: 1 });
expect(
db.$client.prepare("PRAGMA mmap_size").get() as { mmap_size: number },
).toEqual({ mmap_size: SQLITE_MMAP_SIZE_BYTES });
expect(
db.$client.prepare("PRAGMA busy_timeout").get() as { timeout: number },
).toEqual({ timeout: SQLITE_BUSY_TIMEOUT_MS });
expect(
db.$client.prepare("PRAGMA journal_mode").get() as {
journal_mode: string;
},
).toEqual({ journal_mode: "wal" });
} finally {
db.$client.close();
rmSync(directory, { force: true, recursive: true });
}
});
});
Loading