Files
TREK/server/src/db/connection.ts
T
Maurice 83d591dc6c fix(db): keep SQLite temp files in memory so trips can be deleted (#2518)
Deleting a trip failed with SQLITE_IOERR_GETTEMPPATH when the container
runs with a read-only root filesystem and no tmpfs on /tmp. The cascade
behind DELETE FROM trips keeps a statement journal, SQLite moves it to a
temp file once it passes 64 KiB, and it looks for a directory for that
file in SQLITE_TMPDIR, TMPDIR, /var/tmp, /usr/tmp, /tmp and the working
directory. None of them is writable there. Installs on the shipped
compose file have a tmpfs on /tmp and never hit it.

Every connection the server opens now comes from openDatabase() in
src/db/connection.ts, which sets temp_store = MEMORY: the main database
(also when it is reopened after a restore), each plugin's own database
and the uploaded backup checked before a restore. reset-admin.js and the
key rotation script cannot import src/ in the image, so they set the
pragma themselves. A test pins that nothing else in src/ constructs a
handle, and one deletes a full trip on the real schema and watches for
temp files.
2026-09-26 10:36:02 +02:00

28 lines
1.4 KiB
TypeScript

import Database from 'better-sqlite3';
/**
* Opens a SQLite connection. Every handle the server opens comes from here (the
* main database, each plugin's own database, a backup being checked before a
* restore), so what has to hold for all of them is set in one place.
*
* Temporary storage stays in memory. Statement journals, sort runs and transient
* indices move to a temp file once they outgrow a small in-memory buffer, and
* SQLite looks for a directory to hold that file in SQLITE_TMPDIR, TMPDIR,
* /var/tmp, /usr/tmp, /tmp and the working directory. A container with a
* read-only root filesystem and no tmpfs on /tmp has none of them writable, so
* the first statement that needs a temp file fails with SQLITE_IOERR_GETTEMPPATH.
* Deleting a trip is where that showed: the cascade keeps a statement journal
* larger than the 64 KiB SQLite holds before it spills to disk (#2518).
*
* The data directory is the only place TREK can count on being able to write,
* and SQLite has no supported per-connection way to put temp files there, so
* they stay in RAM. The shipped compose file already had them there, its /tmp
* being a tmpfs. VACUUM INTO, the only VACUUM TREK runs, writes straight into
* its target file and builds no temporary database.
*/
export function openDatabase(filename: string, options?: Database.Options): Database.Database {
const db = new Database(filename, options);
db.pragma('temp_store = MEMORY');
return db;
}