Use node:Sqlite
This commit is contained in:
+59
@@ -0,0 +1,59 @@
|
||||
import { Config } from './services/config.ts';
|
||||
import { Database, MigrationService } from './services/storage/index.ts';
|
||||
import { Logger } from './utils/logger.ts';
|
||||
|
||||
/** Application composition root. */
|
||||
export class App {
|
||||
/**
|
||||
* Construct infrastructure and validate all routes before returning a runnable app.
|
||||
*/
|
||||
static async create(): Promise<App> {
|
||||
const config = Config.fromEnv();
|
||||
const debug = new Logger('sync-server-v2');
|
||||
debug('config loaded: %O', config);
|
||||
|
||||
// Persistence must be ready before routes accept traffic.
|
||||
const database = new Database({ path: config.database.path, debug });
|
||||
const migrations = new MigrationService(database, debug);
|
||||
await migrations.migrateToLatest();
|
||||
|
||||
return new App(database);
|
||||
}
|
||||
|
||||
private stopPromise: Promise<void> | undefined;
|
||||
|
||||
constructor(private readonly database: Database) {}
|
||||
|
||||
async start(): Promise<void> {}
|
||||
|
||||
/** Stop transports before releasing the database they may still use. */
|
||||
async stop(): Promise<void> {
|
||||
this.stopPromise ??= this.database.destroy();
|
||||
await this.stopPromise;
|
||||
}
|
||||
}
|
||||
|
||||
const app = await App.create();
|
||||
let shuttingDown = false;
|
||||
|
||||
const shutdown = (signal: NodeJS.Signals): void => {
|
||||
if (shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
shuttingDown = true;
|
||||
void app.stop().catch((error) => {
|
||||
console.error('Graceful shutdown failed after ' + signal, error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
};
|
||||
|
||||
process.once('SIGINT', (): void => shutdown('SIGINT'));
|
||||
process.once('SIGTERM', (): void => shutdown('SIGTERM'));
|
||||
|
||||
try {
|
||||
await app.start();
|
||||
} catch (error: unknown) {
|
||||
await app.stop();
|
||||
throw error;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import DatabaseConstructor from 'better-sqlite3';
|
||||
import { Kysely, SqliteDialect } from 'kysely';
|
||||
import { CompiledQuery, Kysely } from 'kysely';
|
||||
import { NodeNativeSqliteDialect } from 'kysely-node-native-sqlite';
|
||||
|
||||
import type { DatabaseTables } from './tables.ts';
|
||||
import type { Logger } from '../../utils/logger.ts';
|
||||
|
||||
@@ -13,13 +14,13 @@ export type DatabaseOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Thin wrapper around better-sqlite3 and Kysely.
|
||||
* Thin wrapper around Kysely and NodeNativeSqliteDialect (which uses node:sqlite).
|
||||
*
|
||||
* Owns connection setup, pragma configuration, and graceful teardown.
|
||||
*/
|
||||
export class Database {
|
||||
private readonly debug: Logger;
|
||||
private readonly sqlite: DatabaseConstructor.Database;
|
||||
private readonly dialect: NodeNativeSqliteDialect;
|
||||
private readonly kysely: Kysely<DatabaseTables>;
|
||||
|
||||
/**
|
||||
@@ -32,13 +33,15 @@ export class Database {
|
||||
this.debug = options.debug.extend('database');
|
||||
|
||||
// Create the SQLite database.
|
||||
this.sqlite = new DatabaseConstructor(options.path);
|
||||
this.configurePragmas();
|
||||
this.dialect = new NodeNativeSqliteDialect(options.path);
|
||||
|
||||
// Create the Kysely database.
|
||||
this.kysely = new Kysely<DatabaseTables>({
|
||||
dialect: new SqliteDialect({ database: this.sqlite }),
|
||||
dialect: this.dialect,
|
||||
});
|
||||
|
||||
// Configure the SQLite pragmas.
|
||||
this.configurePragmas();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +68,8 @@ export class Database {
|
||||
*/
|
||||
private configurePragmas(): void {
|
||||
this.debug('configuring SQLite pragmas');
|
||||
this.sqlite.pragma('journal_mode = WAL');
|
||||
this.sqlite.pragma('foreign_keys = ON');
|
||||
|
||||
this.kysely.executeQuery(CompiledQuery.raw('PRAGMA journal_mode = WAL'));
|
||||
this.kysely.executeQuery(CompiledQuery.raw('PRAGMA foreign_keys = ON'));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user