perf: batch gateway submits and isolate callbacks
This commit is contained in:
@@ -7,7 +7,25 @@ describe('PrismaService', () => {
|
||||
expect(Object.getOwnPropertyDescriptor(prisma, 'operationLog')).toEqual(
|
||||
expect.objectContaining({ configurable: true }),
|
||||
);
|
||||
expect(prisma.getPoolState()).toEqual({ max: 32, total: 0, idle: 0, waiting: 0 });
|
||||
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
|
||||
it('reserves a bounded database pool for the isolated Gateway callback process', async () => {
|
||||
const previousRole = process.env.CMPP_PROCESS_ROLE;
|
||||
const previousMax = process.env.API_CALLBACK_DB_POOL_MAX;
|
||||
process.env.CMPP_PROCESS_ROLE = 'callback';
|
||||
process.env.API_CALLBACK_DB_POOL_MAX = '12';
|
||||
try {
|
||||
const prisma = new PrismaService();
|
||||
expect(prisma.getPoolState()).toEqual({ max: 12, total: 0, idle: 0, waiting: 0 });
|
||||
await prisma.$disconnect();
|
||||
} finally {
|
||||
if (previousRole === undefined) delete process.env.CMPP_PROCESS_ROLE;
|
||||
else process.env.CMPP_PROCESS_ROLE = previousRole;
|
||||
if (previousMax === undefined) delete process.env.API_CALLBACK_DB_POOL_MAX;
|
||||
else process.env.API_CALLBACK_DB_POOL_MAX = previousMax;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,30 +1,46 @@
|
||||
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 workerRole = process.env.CMPP_PROCESS_ROLE === 'worker';
|
||||
const databaseUrl = workerRole
|
||||
? process.env.API_WORKER_DATABASE_URL || process.env.DATABASE_URL
|
||||
: process.env.DATABASE_URL;
|
||||
const configuredPoolMax = Number(workerRole
|
||||
? process.env.API_WORKER_DB_POOL_MAX ?? 8
|
||||
: process.env.API_DB_POOL_MAX ?? 32);
|
||||
const processRole = process.env.CMPP_PROCESS_ROLE?.trim() || 'api';
|
||||
const workerRole = processRole === 'worker';
|
||||
const outboxRole = processRole === 'outbox';
|
||||
const callbackRole = processRole === 'callback';
|
||||
const databaseUrl = 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(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
|
||||
: workerRole ? 8 : 32;
|
||||
super({
|
||||
adapter: new PrismaPg({
|
||||
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,
|
||||
}),
|
||||
: outboxRole ? 6 : callbackRole ? 16 : workerRole ? 8 : 32;
|
||||
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, {
|
||||
@@ -46,6 +62,15 @@ export class PrismaService extends PrismaClient implements OnModuleDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
getPoolState() {
|
||||
return {
|
||||
max: this.databasePoolMax,
|
||||
total: this.databasePool.totalCount,
|
||||
idle: this.databasePool.idleCount,
|
||||
waiting: this.databasePool.waitingCount,
|
||||
};
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
await this.$disconnect();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user