fix: Apply statement_timeout on request-handling processes (#12422)

* fix: Apply Postgres statement_timeout on request-handling processes

Sets `statement_timeout` to REQUEST_TIMEOUT on the Sequelize connection
pool when the process handles HTTP requests (web/api/collaboration/
websockets/admin) and does not also run worker/cron. Prevents a single
runaway query from saturating the shared Postgres instance and cascading
into timeouts across all endpoints.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Drop dead `api` service check

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Only apply statement_timeout in forked cluster workers

Skips the timeout in the master process so startup migrations driven
from `checkPendingMigrations` are not cancelled mid-execution.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Tom Moor
2026-05-21 21:26:30 -04:00
committed by GitHub
parent cf488508b7
commit 63a6ed7f8d
+18
View File
@@ -1,3 +1,4 @@
import cluster from "node:cluster";
import path from "node:path";
import type { InferAttributes, InferCreationAttributes } from "sequelize";
import sequelizeStrictAttributes from "sequelize-strict-attributes";
@@ -45,6 +46,22 @@ const poolMin = env.DATABASE_CONNECTION_POOL_MIN ?? 0;
const databaseConfig = env.DATABASE_CONNECTION_POOL_URL || getDatabaseConfig();
const schema = env.DATABASE_SCHEMA;
// Request-handling processes get a Postgres `statement_timeout` matching the
// HTTP request timeout, so a single slow query cannot hold a connection past
// the point at which its response could be delivered. Worker/cron processes
// are exempted because background jobs may legitimately run long queries.
// Only applied in forked cluster workers so that startup work driven from
// the master process (notably migrations) is not subject to the timeout.
const isApiProcess =
(env.SERVICES.includes("web") ||
env.SERVICES.includes("collaboration") ||
env.SERVICES.includes("websockets") ||
env.SERVICES.includes("admin")) &&
!env.SERVICES.includes("worker") &&
!env.SERVICES.includes("cron");
const statementTimeout =
isApiProcess && cluster.isWorker ? env.REQUEST_TIMEOUT : undefined;
export function createDatabaseInstance(
databaseConfig: string | object,
input: {
@@ -68,6 +85,7 @@ export function createDatabaseInstance(
logQueryParameters: env.isDevelopment,
dialectOptions: {
application_name: getConnectionName(),
statement_timeout: statementTimeout,
ssl:
env.isProduction && !isSSLDisabled
? {