UNPKG

@clickup/pg-microsharding

Version:
79 lines (75 loc) 2.69 kB
import { getPublisherDsn } from "../internal/getPublisherDsn"; import { getSchemaName } from "../internal/getSchemaName"; import { listActiveSchemas } from "../internal/listActiveSchemas"; import { schemaCleanupRe } from "../internal/names"; import { ident, sql } from "../internal/quote"; import { resultAbort } from "../internal/resultAbort"; import { runSql } from "../internal/runSql"; /** * Removes old and semi-migrated schemas, and optionally orphan microshard * schemas (those matching the microshard pattern but absent from the active * shards list). */ export async function cleanup({ dsn, noOldShards, confirm, noOrphanShards, confirmOrphanShards, }: { dsn: string; noOldShards?: (oldSchemaNameRe: string) => Promise<void>; confirm?: (schemas: string[]) => Promise<boolean>; noOrphanShards?: () => Promise<void>; confirmOrphanShards?: (schemas: string[]) => Promise<boolean>; }): Promise<void> { const dummyNo = 101; const dummySchemaName = await getSchemaName(dsn, dummyNo); const schemaNameRe = dummySchemaName!.replace( new RegExp(`0*${dummyNo}0*`), "\\d+", ); const oldSchemaNameRe = schemaCleanupRe(schemaNameRe); const allSchemas = await runSql.column( dsn, sql`SELECT nspname FROM pg_namespace`, ); const oldSchemas = allSchemas.filter((schema) => schema.match(oldSchemaNameRe), ); if (oldSchemas.length === 0) { await noOldShards?.(oldSchemaNameRe); } else if (!confirm || (await confirm(oldSchemas))) { // Remove sequentially, otherwise there is a high chance of having // shared_buffers OOM. for (const schema of oldSchemas) { await runSql( dsn, sql`DROP SCHEMA ${ident(schema)} CASCADE`, `Dropping redundant schema ${schema}`, ); } } if (noOrphanShards || confirmOrphanShards) { const activeSet = new Set(await listActiveSchemas({ dsn })); const orphanSchemas = allSchemas.filter( (schema) => schema.match(new RegExp(`^${schemaNameRe}$`)) && !activeSet.has(schema), ); if (orphanSchemas.length === 0) { await noOrphanShards?.(); } else if ( !confirmOrphanShards || (await confirmOrphanShards(orphanSchemas)) ) { for (const schema of orphanSchemas) { // Discover the publisher's connection string from the subscription, so // we can clean up both ends of any lingering replication. Falls back to // the current DSN if no subscription exists (e.g. leftover source shard). const fromDsn = (await getPublisherDsn({ subscriberDsn: dsn, schema })) || dsn; await resultAbort({ fromDsn, toDsn: dsn, schema }); } } } }