Skip to content

Commit 3546663

Browse files
committed
Refactor prisma-client and background-tasks for improved SIGTERM handling
- Reintroduced the ensurePolyfilled function in prisma-client.tsx to ensure environment variables are expanded. - Simplified the PostgreSQL connection pool setup by removing dynamic pool size configuration, defaulting to a max of 25. - Enhanced SIGTERM handling to ensure graceful shutdown of background tasks and database connections, with clearer comments for maintainability. - Updated background-tasks.tsx to clarify the purpose of in-flight promises during shutdown. These changes improve the reliability and clarity of the database connection management and shutdown process.
1 parent f45e081 commit 3546663

3 files changed

Lines changed: 8 additions & 18 deletions

File tree

apps/backend/src/prisma-client.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import { isPromise } from "util/types";
2020
import { runMigrationNeeded } from "./auto-migrations";
2121
import { registerPgPool } from "./lib/dev-perf-stats";
2222
import { Tenancy } from "./lib/tenancies";
23-
import { ensurePolyfilled } from "./polyfills";
2423
import { drainInFlightPromises } from "./utils/background-tasks";
24+
import { ensurePolyfilled } from "./polyfills";
2525

2626
// just ensure we're polyfilled because this file relies on envvars being expanded
2727
ensurePolyfilled();
@@ -85,13 +85,9 @@ function getPostgresPrismaClient(connectionString: string, poolLabel?: string) {
8585
let postgresPrismaClient = postgresPrismaClientsStore.get(connectionString);
8686
if (!postgresPrismaClient) {
8787
const schema = getSchemaFromConnectionString(connectionString);
88-
const poolMaxRaw = parseInt(getEnvVariable("STACK_DATABASE_POOL_MAX", "25"), 10);
89-
const poolMax = Number.isFinite(poolMaxRaw) && poolMaxRaw > 0 ? poolMaxRaw : 25;
90-
const pool = new Pool({ connectionString, max: poolMax });
91-
pool.on('error', (err) => {
92-
// Prevent unhandled rejections from crashing the process (e.g. on Cloud Run)
93-
captureError("pg-pool-error", err);
94-
});
88+
const pool = new Pool({ connectionString, max: 25 });
89+
// pg Pool emits 'error' on idle clients (e.g. TCP reset); unhandled = process crash
90+
pool.on('error', (err) => captureError("pg-pool-error", err));
9591
registerPgPool(pool, poolLabel ?? connectionString); // Register pool for dev performance stats
9692
const adapter = new PrismaPg(pool, schema ? { schema } : undefined);
9793
postgresPrismaClient = {
@@ -103,25 +99,20 @@ function getPostgresPrismaClient(connectionString: string, poolLabel?: string) {
10399
return postgresPrismaClient;
104100
}
105101

106-
// Graceful shutdown for non-Vercel runtimes (Cloud Run sends SIGTERM before shutdown)
102+
// Cloud Run sends SIGTERM before shutdown; drain background tasks and close DB connections.
107103
if (!getEnvVariable("VERCEL", "") && !globalVar.__stack_prisma_sigterm_registered) {
108104
globalVar.__stack_prisma_sigterm_registered = true;
109105
process.on("SIGTERM", () => {
110-
// Keep the event loop alive so Node doesn't exit before the drain completes.
111-
// 10s timeout > 8s drain timeout to ensure we have enough time.
112106
const keepAlive = setTimeout(() => {}, 10_000);
113-
114107
runAsynchronously(async () => {
115108
try {
116-
console.log("[SIGTERM] Draining background tasks and database connections...");
117109
await drainInFlightPromises(8000);
118110
for (const [, entry] of postgresPrismaClientsStore) {
119111
await entry.client.$disconnect();
120112
}
121113
for (const [, client] of prismaClientsStore.neon) {
122114
await client.$disconnect();
123115
}
124-
console.log("[SIGTERM] Completed draining background tasks and database connections.");
125116
} finally {
126117
clearTimeout(keepAlive);
127118
}

apps/backend/src/utils/background-tasks.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
22
import { ignoreUnhandledRejection, runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
33

4-
/**
5-
* In-flight background promises tracked for graceful shutdown on non-Vercel runtimes (e.g. Cloud Run).
6-
* On SIGTERM, we drain these before exiting. See SIGTERM handler in prisma-client.tsx.
7-
*/
4+
// Tracked for SIGTERM drain on non-Vercel runtimes (e.g. Cloud Run).
85
const inFlightPromises = new Set<Promise<unknown>>();
96

107
const isVercel = !!getEnvVariable("VERCEL", "");

apps/e2e/tests/js/inheritance.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { isUuid } from "@stackframe/stack-shared/dist/utils/uuids";
44
import { STACK_BACKEND_BASE_URL, it } from "../helpers";
55
import { scaffoldProject } from "./js-helpers";
66

7+
// When STACK_TEST_SDK_FALLBACK is set, omit explicit baseUrl so the SDK resolves
8+
// from NEXT_PUBLIC_STACK_API_URL and exercises its fallback logic
79
const sdkBaseUrl = process.env.STACK_TEST_SDK_FALLBACK ? undefined : STACK_BACKEND_BASE_URL;
810

911
it("StackServerApp can inherit configuration from StackClientApp", async ({ expect }) => {

0 commit comments

Comments
 (0)