Skip to content

Commit 04d21a2

Browse files
enbeaGnuxie
authored andcommitted
Address #746 and a small SQLite optimization.
Explicitly set the `temp_store` pragma to `file` instead of `memory` after deciding to place temporary files in `/data` to keep RAM usage down while addressing #746. Added a helper function to automatically "flatten" transactions when you don't need SAVEPOINTs to avoid unnecessary temporary files. Signed-off-by: Bea <[email protected]>
1 parent 0bc511a commit 04d21a2

File tree

3 files changed

+24
-4
lines changed

3 files changed

+24
-4
lines changed

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ COPY --from=build-stage /tmp/src/package.json /
2323

2424
ENV NODE_ENV=production
2525
ENV NODE_CONFIG_DIR=/data/config
26+
# Set SQLite's temporary directory. See #746 for context.
27+
ENV SQLITE_TMPDIR=/data
2628

2729
CMD ["bot"]
2830
ENTRYPOINT ["./draupnir-entrypoint.sh"]

src/backingstore/better-sqlite3/BetterSqliteStore.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { Logger } from "matrix-protection-suite";
1414
const log = new Logger("BetterSqliteStore");
1515

1616
export function sqliteV0Schema(db: Database) {
17-
// we have to prepare and run them seperatley becasue prepare checks if the
17+
// we have to prepare and run them separately because `prepare` checks if the
1818
// table exists.
1919
const createTable = db.transaction(() => {
2020
db.prepare(
@@ -176,3 +176,20 @@ export abstract class BetterSqliteStore {
176176
throw Error("Couldn't fetch schema version");
177177
}
178178
}
179+
180+
/**
181+
* Wraps `fn` in a transaction without creating nested SAVEPOINTs.
182+
* Reduces the likelihood of temporary file creation.
183+
* Initially investigated as part of #746.
184+
*
185+
* See https://www.sqlite.org/lang_savepoint.html
186+
* See https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#transactionfunction---function
187+
*/
188+
export function flatTransaction<Arguments extends unknown[], Result>(
189+
db: Database,
190+
fn: (...args: Arguments) => Result
191+
) {
192+
const t = db.transaction(fn);
193+
return (...args: Arguments): Result =>
194+
db.inTransaction ? fn(...args) : t(...args);
195+
}

src/backingstore/better-sqlite3/SqliteRoomStateBackingStore.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
StateEvent,
1616
isError,
1717
} from "matrix-protection-suite";
18-
import { BetterSqliteStore } from "./BetterSqliteStore";
18+
import { BetterSqliteStore, flatTransaction } from "./BetterSqliteStore";
1919
import { jsonReviver } from "../../utils";
2020
import { StringRoomID } from "@the-draupnir-project/matrix-basic-types";
2121

@@ -64,6 +64,7 @@ export class SqliteRoomStateBackingStore
6464
);
6565
this.db.pragma("journal_mode = WAL");
6666
this.db.pragma("foreign_keys = ON");
67+
this.db.pragma("temp_store = file"); // Avoid unnecessary memory usage.
6768
this.ensureSchema();
6869
}
6970

@@ -85,8 +86,8 @@ export class SqliteRoomStateBackingStore
8586
JSON.stringify(event),
8687
];
8788
};
88-
// i don't understand why the library makes us do this but ok.
89-
const replace = this.db.transaction((events: StateEvent[]) => {
89+
// `flatTransaction` optimizes away unnecessary temporary files.
90+
const replace = flatTransaction(this.db, (events: StateEvent[]) => {
9091
for (const event of events) {
9192
replaceStatement.run(createValue(event));
9293
}

0 commit comments

Comments
 (0)