97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
import { runMigrations } from "graphile-worker";
|
|
import type { PrismaClient } from "~/db.server";
|
|
import { prisma } from "~/db.server";
|
|
import { env } from "~/env.server";
|
|
import { logger } from "~/services/logger.server";
|
|
import { PgNotifyService } from "./pgNotify.server";
|
|
import { z } from "zod";
|
|
|
|
export class GraphileMigrationHelperService {
|
|
#prismaClient: PrismaClient;
|
|
|
|
constructor(prismaClient: PrismaClient = prisma) {
|
|
this.#prismaClient = prismaClient;
|
|
}
|
|
|
|
public async call() {
|
|
this.#logDebug("GraphileMigrationHelperService.call");
|
|
|
|
await this.#detectAndPrepareForMigrations();
|
|
|
|
await runMigrations({
|
|
connectionString: env.DATABASE_URL,
|
|
schema: env.WORKER_SCHEMA,
|
|
});
|
|
}
|
|
|
|
#logDebug(message: string, args?: any) {
|
|
logger.debug(`[migrationHelper] ${message}`, args);
|
|
}
|
|
|
|
async #getLatestMigration() {
|
|
const migrationQueryResult = await this.#prismaClient.$queryRawUnsafe(`
|
|
SELECT id FROM ${env.WORKER_SCHEMA}.migrations
|
|
ORDER BY id DESC LIMIT 1
|
|
`);
|
|
|
|
const MigrationQueryResultSchema = z.array(z.object({ id: z.number() }));
|
|
|
|
const migrationResults = MigrationQueryResultSchema.parse(migrationQueryResult);
|
|
|
|
if (!migrationResults.length) {
|
|
// no migrations applied yet
|
|
return -1;
|
|
}
|
|
|
|
return migrationResults[0].id;
|
|
}
|
|
|
|
async #graphileSchemaExists() {
|
|
const schemaCount = await this.#prismaClient.$executeRaw`
|
|
SELECT schema_name FROM information_schema.schemata
|
|
WHERE schema_name = ${env.WORKER_SCHEMA}
|
|
`;
|
|
|
|
return schemaCount === 1;
|
|
}
|
|
|
|
/** Helper for graphile-worker v0.14.0 migration. No-op if already migrated. */
|
|
async #detectAndPrepareForMigrations() {
|
|
if (!(await this.#graphileSchemaExists())) {
|
|
// no schema yet, likely first start
|
|
return;
|
|
}
|
|
|
|
const latestMigration = await this.#getLatestMigration();
|
|
|
|
if (latestMigration < 0) {
|
|
// no migrations found
|
|
return;
|
|
}
|
|
|
|
// the first v0.14.0 migration has ID 11
|
|
if (latestMigration > 10) {
|
|
// already migrated
|
|
return;
|
|
}
|
|
|
|
// add 15s to graceful shutdown timeout, just to be safe
|
|
const migrationDelayInMs = env.GRACEFUL_SHUTDOWN_TIMEOUT + 15000;
|
|
|
|
this.#logDebug("Delaying worker startup due to pending migration", {
|
|
latestMigration,
|
|
migrationDelayInMs,
|
|
});
|
|
|
|
console.log(`⚠️ detected pending graphile migration`);
|
|
console.log(`⚠️ notifying running workers`);
|
|
|
|
const pgNotify = new PgNotifyService();
|
|
await pgNotify.call("trigger:graphile:migrate", { latestMigration });
|
|
|
|
console.log(`⚠️ delaying worker startup by ${migrationDelayInMs}ms`);
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, migrationDelayInMs));
|
|
}
|
|
}
|