mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Gracefully start and exit the database
This commit is contained in:
parent
d67f2a27ea
commit
fc912f185e
6 changed files with 74 additions and 6 deletions
37
src/DittoExit.ts
Normal file
37
src/DittoExit.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Stickynotes } from '@soapbox/stickynotes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add cleanup tasks to this module,
|
||||||
|
* then they will automatically be called (and the program exited) after SIGINT.
|
||||||
|
*/
|
||||||
|
export class DittoExit {
|
||||||
|
private static tasks: Array<() => Promise<unknown>> = [];
|
||||||
|
private static console = new Stickynotes('ditto:exit');
|
||||||
|
|
||||||
|
static {
|
||||||
|
Deno.addSignalListener('SIGINT', () => this.finish('SIGINT'));
|
||||||
|
Deno.addSignalListener('SIGTERM', () => this.finish('SIGTERM'));
|
||||||
|
Deno.addSignalListener('SIGHUP', () => this.finish('SIGHUP'));
|
||||||
|
Deno.addSignalListener('SIGQUIT', () => this.finish('SIGQUIT'));
|
||||||
|
Deno.addSignalListener('SIGABRT', () => this.finish('SIGABRT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static add(task: () => Promise<unknown>): void {
|
||||||
|
this.tasks.push(task);
|
||||||
|
this.console.debug(`Added cleanup task #${this.tasks.length}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async cleanup(): Promise<void> {
|
||||||
|
this.console.debug(`Running ${this.tasks.length} cleanup tasks...`);
|
||||||
|
await Promise.allSettled(
|
||||||
|
this.tasks.map((task) => task()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async finish(signal: Deno.Signal): Promise<void> {
|
||||||
|
this.console.debug(signal);
|
||||||
|
await this.cleanup();
|
||||||
|
this.console.debug('Exiting gracefully.');
|
||||||
|
Deno.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ export interface DittoDatabase {
|
||||||
readonly kysely: Kysely<DittoTables>;
|
readonly kysely: Kysely<DittoTables>;
|
||||||
readonly poolSize: number;
|
readonly poolSize: number;
|
||||||
readonly availableConnections: number;
|
readonly availableConnections: number;
|
||||||
|
readonly waitReady: Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DittoDatabaseOpts {
|
export interface DittoDatabaseOpts {
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ import { KyselyLogger } from '@/db/KyselyLogger.ts';
|
||||||
|
|
||||||
export class DittoPglite {
|
export class DittoPglite {
|
||||||
static create(databaseUrl: string): DittoDatabase {
|
static create(databaseUrl: string): DittoDatabase {
|
||||||
|
const pglite = new PGlite(databaseUrl);
|
||||||
|
|
||||||
const kysely = new Kysely<DittoTables>({
|
const kysely = new Kysely<DittoTables>({
|
||||||
dialect: new PgliteDialect({
|
dialect: new PgliteDialect({
|
||||||
database: new PGlite(databaseUrl),
|
database: pglite,
|
||||||
}),
|
}),
|
||||||
log: KyselyLogger,
|
log: KyselyLogger,
|
||||||
});
|
});
|
||||||
|
|
@ -19,6 +21,7 @@ export class DittoPglite {
|
||||||
kysely,
|
kysely,
|
||||||
poolSize: 1,
|
poolSize: 1,
|
||||||
availableConnections: 1,
|
availableConnections: 1,
|
||||||
|
waitReady: pglite.waitReady,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ export class DittoPostgres {
|
||||||
get availableConnections() {
|
get availableConnections() {
|
||||||
return pg.connections.idle;
|
return pg.connections.idle;
|
||||||
},
|
},
|
||||||
|
waitReady: Promise.resolve(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,16 @@ import '@/sentry.ts';
|
||||||
import '@/nostr-wasm.ts';
|
import '@/nostr-wasm.ts';
|
||||||
import app from '@/app.ts';
|
import app from '@/app.ts';
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
import { DittoExit } from '@/DittoExit.ts';
|
||||||
|
|
||||||
Deno.serve({ port: Conf.port }, app.fetch);
|
const ac = new AbortController();
|
||||||
|
// deno-lint-ignore require-await
|
||||||
|
DittoExit.add(async () => ac.abort());
|
||||||
|
|
||||||
|
Deno.serve(
|
||||||
|
{
|
||||||
|
port: Conf.port,
|
||||||
|
signal: ac.signal,
|
||||||
|
},
|
||||||
|
app.fetch,
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import { DittoDatabase } from '@/db/DittoDatabase.ts';
|
import { DittoDatabase } from '@/db/DittoDatabase.ts';
|
||||||
import { DittoDB } from '@/db/DittoDB.ts';
|
import { DittoDB } from '@/db/DittoDB.ts';
|
||||||
|
import { DittoExit } from '@/DittoExit.ts';
|
||||||
import { AdminStore } from '@/storages/AdminStore.ts';
|
import { AdminStore } from '@/storages/AdminStore.ts';
|
||||||
import { EventsDB } from '@/storages/EventsDB.ts';
|
import { EventsDB } from '@/storages/EventsDB.ts';
|
||||||
import { SearchStore } from '@/storages/search-store.ts';
|
import { SearchStore } from '@/storages/search-store.ts';
|
||||||
|
|
@ -10,9 +11,11 @@ import { NPool, NRelay1 } from '@nostrify/nostrify';
|
||||||
import { getRelays } from '@/utils/outbox.ts';
|
import { getRelays } from '@/utils/outbox.ts';
|
||||||
import { seedZapSplits } from '@/utils/zap-split.ts';
|
import { seedZapSplits } from '@/utils/zap-split.ts';
|
||||||
|
|
||||||
|
DittoExit.add(() => Storages.close());
|
||||||
|
|
||||||
export class Storages {
|
export class Storages {
|
||||||
private static _db: Promise<EventsDB> | undefined;
|
private static _db: Promise<EventsDB> | undefined;
|
||||||
private static _database: DittoDatabase | undefined;
|
private static _database: Promise<DittoDatabase> | undefined;
|
||||||
private static _admin: Promise<AdminStore> | undefined;
|
private static _admin: Promise<AdminStore> | undefined;
|
||||||
private static _client: Promise<NPool> | undefined;
|
private static _client: Promise<NPool> | undefined;
|
||||||
private static _pubsub: Promise<InternalRelay> | undefined;
|
private static _pubsub: Promise<InternalRelay> | undefined;
|
||||||
|
|
@ -20,8 +23,12 @@ export class Storages {
|
||||||
|
|
||||||
public static async database(): Promise<DittoDatabase> {
|
public static async database(): Promise<DittoDatabase> {
|
||||||
if (!this._database) {
|
if (!this._database) {
|
||||||
this._database = DittoDB.create(Conf.databaseUrl, { poolSize: Conf.pg.poolSize });
|
this._database = (async () => {
|
||||||
await DittoDB.migrate(this._database.kysely);
|
const db = DittoDB.create(Conf.databaseUrl, { poolSize: Conf.pg.poolSize });
|
||||||
|
await db.waitReady;
|
||||||
|
await DittoDB.migrate(db.kysely);
|
||||||
|
return db;
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
return this._database;
|
return this._database;
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +42,7 @@ export class Storages {
|
||||||
public static async db(): Promise<EventsDB> {
|
public static async db(): Promise<EventsDB> {
|
||||||
if (!this._db) {
|
if (!this._db) {
|
||||||
this._db = (async () => {
|
this._db = (async () => {
|
||||||
const { kysely } = await this.database();
|
const kysely = await this.kysely();
|
||||||
const store = new EventsDB({ kysely, pubkey: Conf.pubkey, timeout: Conf.db.timeouts.default });
|
const store = new EventsDB({ kysely, pubkey: Conf.pubkey, timeout: Conf.db.timeouts.default });
|
||||||
await seedZapSplits(store);
|
await seedZapSplits(store);
|
||||||
return store;
|
return store;
|
||||||
|
|
@ -118,4 +125,12 @@ export class Storages {
|
||||||
}
|
}
|
||||||
return this._search;
|
return this._search;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Close the database connection, if one has been opened. */
|
||||||
|
public static async close(): Promise<void> {
|
||||||
|
if (this._database) {
|
||||||
|
const { kysely } = await this._database;
|
||||||
|
await kysely.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue