chore: USE_POOL env var for api v2 prisma pooling
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import "./instrument";
|
||||
|
||||
import process from "node:process";
|
||||
import {
|
||||
API_VERSIONS,
|
||||
API_VERSIONS_ENUM,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { getEnv } from "@/env";
|
||||
|
||||
import type { AppConfig } from "./type";
|
||||
|
||||
const loadConfig = (): AppConfig => {
|
||||
@@ -31,6 +30,7 @@ const loadConfig = (): AppConfig => {
|
||||
workerReadPoolMax: getEnv("DATABASE_READ_WORKER_POOL_MAX", 4),
|
||||
workerWritePoolMax: getEnv("DATABASE_WRITE_WORKER_POOL_MAX", 6),
|
||||
redisUrl: getEnv("REDIS_URL"),
|
||||
usePool: getEnv("USE_POOL", "true") === "true",
|
||||
},
|
||||
next: {
|
||||
authSecret: getEnv("NEXTAUTH_SECRET"),
|
||||
|
||||
@@ -18,6 +18,7 @@ export type AppConfig = {
|
||||
workerReadPoolMax: number;
|
||||
workerWritePoolMax: number;
|
||||
redisUrl: string;
|
||||
usePool: boolean;
|
||||
};
|
||||
next: {
|
||||
authSecret: string;
|
||||
|
||||
@@ -40,11 +40,10 @@ export type Environment = {
|
||||
DATABASE_WRITE_WORKER_POOL_MAX: number;
|
||||
ENABLE_SLOTS_WORKERS: string;
|
||||
SLOTS_WORKER_POOL_SIZE: string;
|
||||
USE_POOL: string;
|
||||
};
|
||||
|
||||
export const getEnv = <K extends keyof Environment>(key: K, fallback?: Environment[K]): Environment[K] => {
|
||||
// biome-ignore lint/style/noProcessEnv: <config file>
|
||||
// biome-ignore lint/correctness/noProcessGlobal: <config file>
|
||||
const value = process.env[key] as Environment[K] | undefined;
|
||||
|
||||
if (value === undefined) {
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
import "dotenv/config";
|
||||
|
||||
import { IncomingMessage, Server, ServerResponse } from "node:http";
|
||||
import process from "node:process";
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import type { NestExpressApplication } from "@nestjs/platform-express";
|
||||
|
||||
import { WinstonModule } from "nest-winston";
|
||||
import * as qs from "qs";
|
||||
|
||||
import type { AppConfig } from "@/config/type";
|
||||
|
||||
import { AppModule } from "./app.module";
|
||||
import { bootstrap } from "./bootstrap";
|
||||
import { loggerConfig } from "./lib/logger";
|
||||
|
||||
@@ -1,63 +1,66 @@
|
||||
import type { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { PrismaClient } from "@calcom/prisma/client";
|
||||
import { Injectable, Logger, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { Pool } from "pg";
|
||||
|
||||
import { PrismaClient } from "@calcom/prisma/client";
|
||||
|
||||
const DB_MAX_POOL_CONNECTION = 10;
|
||||
|
||||
export interface PrismaServiceOptions {
|
||||
readUrl?: string;
|
||||
maxReadConnections?: number;
|
||||
e2e?: boolean;
|
||||
type: "main" | "worker";
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PrismaReadService implements OnModuleInit, OnModuleDestroy {
|
||||
private logger = new Logger("PrismaReadService");
|
||||
|
||||
public prisma!: PrismaClient;
|
||||
private pool!: Pool;
|
||||
private options!: PrismaServiceOptions;
|
||||
|
||||
constructor(private configService?: ConfigService) {
|
||||
constructor(configService?: ConfigService) {
|
||||
if (configService) {
|
||||
// Use ConfigService defaults
|
||||
const readUrl = configService.get<string>("db.readUrl", { infer: true });
|
||||
const poolMax = parseInt(
|
||||
configService.get<number>("db.readPoolMax", { infer: true }) ?? DB_MAX_POOL_CONNECTION
|
||||
configService.get<number>("db.readPoolMax", { infer: true }) ?? DB_MAX_POOL_CONNECTION,
|
||||
10
|
||||
);
|
||||
const e2e = configService.get<boolean>("e2e", { infer: true }) ?? false;
|
||||
const usePool = configService.get<boolean>("db.usePool", { infer: true }) ?? true;
|
||||
|
||||
this.setOptions({
|
||||
readUrl,
|
||||
maxReadConnections: poolMax,
|
||||
e2e,
|
||||
usePool,
|
||||
type: "main",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setOptions(options: PrismaServiceOptions) {
|
||||
this.options = options;
|
||||
|
||||
setOptions(options: PrismaServiceOptions): void {
|
||||
const dbUrl = options.readUrl;
|
||||
const isE2E = options.e2e ?? false;
|
||||
const usePool = options.usePool ?? true;
|
||||
|
||||
this.pool = new Pool({
|
||||
connectionString: dbUrl,
|
||||
max: isE2E ? 1 : options.maxReadConnections ?? DB_MAX_POOL_CONNECTION,
|
||||
idleTimeoutMillis: 300000,
|
||||
});
|
||||
if (usePool) {
|
||||
let maxReadConnections = options.maxReadConnections ?? DB_MAX_POOL_CONNECTION;
|
||||
if (isE2E) {
|
||||
maxReadConnections = 1;
|
||||
}
|
||||
|
||||
const adapter = new PrismaPg(this.pool);
|
||||
this.prisma = new PrismaClient({ adapter });
|
||||
this.pool = new Pool({
|
||||
connectionString: dbUrl,
|
||||
max: maxReadConnections,
|
||||
idleTimeoutMillis: 300000,
|
||||
});
|
||||
|
||||
const adapter = new PrismaPg(this.pool);
|
||||
this.prisma = new PrismaClient({ adapter });
|
||||
} else {
|
||||
this.prisma = new PrismaClient({
|
||||
datasourceUrl: dbUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
async onModuleInit(): Promise<void> {
|
||||
if (!this.prisma) return;
|
||||
|
||||
try {
|
||||
@@ -68,7 +71,7 @@ export class PrismaReadService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
async onModuleDestroy(): Promise<void> {
|
||||
try {
|
||||
if (this.prisma) await this.prisma.$disconnect();
|
||||
if (this.pool) await this.pool.end();
|
||||
@@ -77,3 +80,11 @@ export class PrismaReadService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface PrismaServiceOptions {
|
||||
readUrl?: string;
|
||||
maxReadConnections?: number;
|
||||
e2e?: boolean;
|
||||
usePool?: boolean;
|
||||
type: "main" | "worker";
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
@@ -12,7 +12,7 @@ import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
const service = new PrismaReadService();
|
||||
service.setOptions({
|
||||
readUrl: config.get<string>("db.readUrl", { infer: true }),
|
||||
maxReadConnections: parseInt(config.get<number>("db.workerReadPoolMax", { infer: true })),
|
||||
maxReadConnections: parseInt(config.get<number>("db.workerReadPoolMax", { infer: true }), 10),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "worker",
|
||||
});
|
||||
@@ -26,7 +26,7 @@ import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
const service = new PrismaWriteService();
|
||||
service.setOptions({
|
||||
writeUrl: config.get<string>("db.writeUrl", { infer: true }),
|
||||
maxWriteConnections: parseInt(config.get<number>("db.workerWritePoolMax", { infer: true })),
|
||||
maxWriteConnections: parseInt(config.get<number>("db.workerWritePoolMax", { infer: true }), 10),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "worker",
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { OnModuleDestroy, OnModuleInit, Injectable, Logger } from "@nestjs/common";
|
||||
import { PrismaClient } from "@calcom/prisma/client";
|
||||
import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { Pool } from "pg";
|
||||
|
||||
import { PrismaClient } from "@calcom/prisma/client";
|
||||
|
||||
const DB_MAX_POOL_CONNECTION = 5;
|
||||
|
||||
export interface PrismaServiceOptions {
|
||||
writeUrl?: string;
|
||||
maxWriteConnections?: number;
|
||||
e2e?: boolean;
|
||||
usePool?: boolean;
|
||||
type: "main" | "worker";
|
||||
}
|
||||
|
||||
@@ -19,41 +19,54 @@ export class PrismaWriteService implements OnModuleInit, OnModuleDestroy {
|
||||
private logger = new Logger("PrismaWriteService");
|
||||
public prisma!: PrismaClient;
|
||||
private pool!: Pool;
|
||||
private options!: PrismaServiceOptions;
|
||||
|
||||
constructor(private configService?: ConfigService) {
|
||||
constructor(configService?: ConfigService) {
|
||||
if (configService) {
|
||||
const writeUrl = configService.get<string>("db.writeUrl", { infer: true });
|
||||
const poolMax = parseInt(
|
||||
configService.get<number>("db.writePoolMax", { infer: true }) ?? DB_MAX_POOL_CONNECTION
|
||||
String(configService.get<number>("db.writePoolMax", { infer: true }) ?? DB_MAX_POOL_CONNECTION),
|
||||
10
|
||||
);
|
||||
const e2e = configService.get<boolean>("e2e", { infer: true }) ?? false;
|
||||
const usePool = configService.get<boolean>("db.usePool", { infer: true }) ?? true;
|
||||
|
||||
this.setOptions({
|
||||
writeUrl,
|
||||
maxWriteConnections: poolMax,
|
||||
e2e,
|
||||
usePool,
|
||||
type: "main",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setOptions(options: PrismaServiceOptions) {
|
||||
this.options = options;
|
||||
|
||||
setOptions(options: PrismaServiceOptions): void {
|
||||
const dbUrl = options.writeUrl;
|
||||
const isE2E = options.e2e ?? false;
|
||||
this.pool = new Pool({
|
||||
connectionString: dbUrl,
|
||||
max: isE2E ? 1 : options.maxWriteConnections ?? DB_MAX_POOL_CONNECTION,
|
||||
idleTimeoutMillis: 300000,
|
||||
});
|
||||
const usePool = options.usePool ?? true;
|
||||
|
||||
const adapter = new PrismaPg(this.pool);
|
||||
this.prisma = new PrismaClient({ adapter });
|
||||
if (usePool) {
|
||||
let maxWriteConnections = options.maxWriteConnections ?? DB_MAX_POOL_CONNECTION;
|
||||
if (isE2E) {
|
||||
maxWriteConnections = 1;
|
||||
}
|
||||
|
||||
this.pool = new Pool({
|
||||
connectionString: dbUrl,
|
||||
max: maxWriteConnections,
|
||||
idleTimeoutMillis: 300000,
|
||||
});
|
||||
|
||||
const adapter = new PrismaPg(this.pool);
|
||||
this.prisma = new PrismaClient({ adapter });
|
||||
} else {
|
||||
this.prisma = new PrismaClient({
|
||||
datasourceUrl: dbUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
async onModuleInit(): Promise<void> {
|
||||
if (!this.prisma) return;
|
||||
|
||||
try {
|
||||
@@ -64,7 +77,7 @@ export class PrismaWriteService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
async onModuleDestroy(): Promise<void> {
|
||||
try {
|
||||
if (this.prisma) await this.prisma.$disconnect();
|
||||
if (this.pool) await this.pool.end();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
@@ -12,7 +12,7 @@ import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
const service = new PrismaReadService();
|
||||
service.setOptions({
|
||||
readUrl: config.get<string>("db.readUrl", { infer: true }),
|
||||
maxReadConnections: parseInt(config.get<number>("db.readPoolMax", { infer: true })),
|
||||
maxReadConnections: parseInt(config.get<number>("db.readPoolMax", { infer: true }), 10),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "main",
|
||||
});
|
||||
@@ -26,7 +26,7 @@ import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
const service = new PrismaWriteService();
|
||||
service.setOptions({
|
||||
writeUrl: config.get<string>("db.writeUrl", { infer: true }),
|
||||
maxWriteConnections: parseInt(config.get<number>("db.writePoolMax", { infer: true })),
|
||||
maxWriteConnections: parseInt(config.get<number>("db.writePoolMax", { infer: true }), 10),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "main",
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import "dotenv/config";
|
||||
import process from "node:process";
|
||||
|
||||
import { bootstrap } from "../bootstrap";
|
||||
import { createNestApp } from "../main";
|
||||
|
||||
+14
-10
@@ -78,16 +78,6 @@
|
||||
"linter": { "enabled": false },
|
||||
"formatter": { "enabled": false }
|
||||
},
|
||||
{
|
||||
"includes": ["apps/api/v2/**/*.ts", "apps/api/v2/**/*.controller.ts"],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useImportType": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"includes": ["**/*.tsx"],
|
||||
"javascript": {
|
||||
@@ -158,6 +148,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"includes": ["apps/api/v2/**/*.ts", "apps/api/v2/**/*.controller.ts"],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useImportType": "off",
|
||||
"noProcessEnv": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noProcessGlobal": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"includes": ["**/*.test.ts", "**/*.test.tsx", "**/*.e2e-spec.ts"],
|
||||
"linter": {
|
||||
|
||||
Reference in New Issue
Block a user