87 lines
3.5 KiB
TypeScript
87 lines
3.5 KiB
TypeScript
import { Injectable, OnModuleDestroy } from '@nestjs/common';
|
|
import { PrismaPg } from '@prisma/adapter-pg';
|
|
import { PrismaClient } from '@prisma/client';
|
|
import { Pool } from 'pg';
|
|
import { requestContext } from '../common/request-context';
|
|
|
|
@Injectable()
|
|
export class PrismaService extends PrismaClient implements OnModuleDestroy {
|
|
private readonly databasePool: Pool;
|
|
private readonly databasePoolMax: number;
|
|
|
|
constructor() {
|
|
const processRole = process.env.CMPP_PROCESS_ROLE?.trim() || 'api';
|
|
const workerRole = processRole === 'worker';
|
|
const outboxRole = processRole === 'outbox';
|
|
const callbackRole = processRole === 'callback';
|
|
const protocolLogRole = processRole === 'protocol-log-worker';
|
|
const databaseUrl = protocolLogRole
|
|
? process.env.API_PROTOCOL_LOG_DATABASE_URL || process.env.DATABASE_URL
|
|
: outboxRole
|
|
? process.env.API_OUTBOX_DATABASE_URL || process.env.DATABASE_URL
|
|
: callbackRole
|
|
? process.env.API_CALLBACK_DATABASE_URL || process.env.DATABASE_URL
|
|
: workerRole
|
|
? process.env.API_WORKER_DATABASE_URL || process.env.DATABASE_URL
|
|
: process.env.DATABASE_URL;
|
|
const configuredPoolMax = Number(protocolLogRole
|
|
? process.env.API_PROTOCOL_LOG_DB_POOL_MAX ?? 4
|
|
: outboxRole
|
|
? process.env.API_OUTBOX_DB_POOL_MAX ?? 6
|
|
: callbackRole
|
|
? process.env.API_CALLBACK_DB_POOL_MAX ?? 16
|
|
: workerRole
|
|
? process.env.API_WORKER_DB_POOL_MAX ?? 8
|
|
: process.env.API_DB_POOL_MAX ?? 32);
|
|
const poolMax = Number.isInteger(configuredPoolMax) && configuredPoolMax > 0
|
|
? configuredPoolMax
|
|
: protocolLogRole ? 4 : outboxRole ? 6 : callbackRole ? 16 : workerRole ? 8 : 32;
|
|
const allowDevelopmentDefault = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';
|
|
if (!databaseUrl && !allowDevelopmentDefault) {
|
|
throw new Error(`DATABASE_URL is required for CMPP process role ${processRole}`);
|
|
}
|
|
const databasePool = new Pool({
|
|
connectionString: databaseUrl
|
|
?? 'postgresql://cmpp:cmpp_password@localhost:5432/cmpp_platform?schema=public',
|
|
// API capacity must be reserved independently from the heavier Worker
|
|
// transactions; explicit bounds also protect PostgreSQL max_connections.
|
|
max: poolMax,
|
|
});
|
|
super({ adapter: new PrismaPg(databasePool, { disposeExternalPool: true }) });
|
|
this.databasePool = databasePool;
|
|
this.databasePoolMax = poolMax;
|
|
const operationLog = this.operationLog;
|
|
Object.defineProperty(this, 'operationLog', {
|
|
value: new Proxy(operationLog, {
|
|
get(target, property, receiver) {
|
|
if (property === 'create') {
|
|
return (args: { data: Record<string, unknown> }) => {
|
|
const ipAddress = requestContext.getStore()?.ipAddress;
|
|
return (target.create as (input: unknown) => unknown)({
|
|
...args,
|
|
data: { ...args.data, ipAddress: typeof args.data.ipAddress === 'string' ? args.data.ipAddress : ipAddress },
|
|
});
|
|
};
|
|
}
|
|
const value = Reflect.get(target, property, receiver);
|
|
return typeof value === 'function' ? value.bind(target) : value;
|
|
},
|
|
}),
|
|
configurable: true,
|
|
});
|
|
}
|
|
|
|
getPoolState() {
|
|
return {
|
|
max: this.databasePoolMax,
|
|
total: this.databasePool.totalCount,
|
|
idle: this.databasePool.idleCount,
|
|
waiting: this.databasePool.waitingCount,
|
|
};
|
|
}
|
|
|
|
async onModuleDestroy() {
|
|
await this.$disconnect();
|
|
}
|
|
}
|