import * as E from "fp-ts/lib/Either"; import * as TE from "fp-ts/lib/TaskEither"; import ms from "ms"; import { sql, createPool } from "slonik"; import { ConnectionRoutine, DatabasePool, TransactionFunction, } from "slonik/dist/src/types"; import type { Config } from "../../config"; import { createLogger } from "../logger"; import type { Telemetry } from "../telemetry"; import { getSpanOptions, createSlonikTelemetryInterceptor, } from "../telemetry/instrumentations/slonik"; interface Dependencies { readonly config: Config; readonly telemetry: Telemetry; } export interface Database { readonly runInConnection: ( callback: ConnectionRoutine ) => TE.TaskEither; readonly runInTransaction: ( callback: TransactionFunction ) => TE.TaskEither; readonly end: () => TE.TaskEither; } export { DatabasePoolConnection } from "slonik"; export function createDatabase({ config, telemetry, }: Dependencies): TE.TaskEither { const logger = createLogger("database", { config }); const { databaseIdleTimeout: idleTimeout, databaseStatementTimeout: statementTimeout, databaseMaximumPoolSize: maximumPoolSize, databaseUrl: url, } = config; return telemetry.withSpan( "database.connect", getSpanOptions({ idleTimeout, maximumPoolSize }) )(async () => { logger.debug(`connecting to database...`)(); let pool: DatabasePool; try { pool = await createPool(url, { captureStackTrace: false, statementTimeout, interceptors: [createSlonikTelemetryInterceptor({ telemetry })], idleTimeout, maximumPoolSize, }); await pool.query(sql`select 1`); } catch (error) { return E.left(E.toError(error)); } logger.info(`connected to database`)(); return E.right(makeSlonikFunctionalWrapper(pool)); }); } export function makeSlonikFunctionalWrapper(pool: DatabasePool): Database { return { runInConnection(callback) { return TE.tryCatch(() => pool.connect(callback), E.toError); }, runInTransaction(callback) { return TE.tryCatch(() => pool.transaction(callback), E.toError); }, end() { return TE.tryCatch(async () => { await pool.end(); console.log("POOL END"); return true; }, E.toError); }, }; }