diff --git a/deploy/deploy.schema.json b/deploy/deploy.schema.json index 3000128a..a41f16f9 100644 --- a/deploy/deploy.schema.json +++ b/deploy/deploy.schema.json @@ -385,7 +385,12 @@ "type": "object", "properties": { "image": { "type": "string", "minLength": 1 }, - "poolMax": { "type": "integer", "minimum": 1 } + "poolMax": { "type": "integer", "minimum": 1 }, + "connectionTimeoutMs": { "type": "integer", "minimum": 1 }, + "queryTimeoutMs": { "type": "integer", "minimum": 1 }, + "queryRetryMaxAttempts": { "type": "integer", "minimum": 1 }, + "queryRetryInitialDelayMs": { "type": "integer", "minimum": 0 }, + "queryRetryMaxDelayMs": { "type": "integer", "minimum": 0 } }, "additionalProperties": false }, diff --git a/deploy/deploy.yaml b/deploy/deploy.yaml index a3d7d089..c56194bd 100644 --- a/deploy/deploy.yaml +++ b/deploy/deploy.yaml @@ -304,6 +304,7 @@ lanes: image: postgres:16-alpine poolMax: 4 connectionTimeoutMs: 5000 + queryTimeoutMs: 3000 queryRetryMaxAttempts: 5 queryRetryInitialDelayMs: 250 queryRetryMaxDelayMs: 5000 diff --git a/internal/db/runtime-store.ts b/internal/db/runtime-store.ts index 68b03ca4..a707e84a 100644 --- a/internal/db/runtime-store.ts +++ b/internal/db/runtime-store.ts @@ -109,17 +109,22 @@ export function createConfiguredCloudRuntimeStore(options = {}) { return createCloudRuntimeStore(options); } -export function buildPostgresPoolConfig({ dbUrl, sslMode = "require", timeoutMs, poolMax } = {}) { +export function buildPostgresPoolConfig({ dbUrl, sslMode = "require", timeoutMs, poolMax, queryTimeoutMs } = {}) { const normalizedSslMode = normalizePostgresSslMode(sslMode); const normalizedPoolMax = normalizeRuntimePoolMax(poolMax); + const normalizedQueryTimeoutMs = queryTimeoutMs === undefined || queryTimeoutMs === null || String(queryTimeoutMs).trim() === "" + ? null + : normalizeRuntimeTimeoutMs(queryTimeoutMs); return { connectionString: normalizePostgresConnectionStringForSslMode(dbUrl, normalizedSslMode), ssl: postgresSslOption(normalizedSslMode), connectionTimeoutMillis: normalizeRuntimeTimeoutMs(timeoutMs), + ...(normalizedQueryTimeoutMs !== null ? { query_timeout: normalizedQueryTimeoutMs, statement_timeout: normalizedQueryTimeoutMs } : {}), ...(normalizedPoolMax !== null ? { max: normalizedPoolMax } : {}) }; } +const RUNTIME_DB_QUERY_TIMEOUT_MS_ENV = "HWLAB_CLOUD_DB_QUERY_TIMEOUT_MS"; const RUNTIME_DB_QUERY_RETRY_MAX_ATTEMPTS_ENV = "HWLAB_CLOUD_DB_QUERY_RETRY_MAX_ATTEMPTS"; const RUNTIME_DB_QUERY_RETRY_INITIAL_DELAY_MS_ENV = "HWLAB_CLOUD_DB_QUERY_RETRY_INITIAL_DELAY_MS"; const RUNTIME_DB_QUERY_RETRY_MAX_DELAY_MS_ENV = "HWLAB_CLOUD_DB_QUERY_RETRY_MAX_DELAY_MS"; @@ -1952,6 +1957,7 @@ export class PostgresCloudRuntimeStore { dbUrl: this.dbUrl, sslMode: this.sslMode, timeoutMs: this.env?.HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS, + queryTimeoutMs: this.env?.[RUNTIME_DB_QUERY_TIMEOUT_MS_ENV], poolMax: this.env?.[RUNTIME_DB_POOL_MAX_ENV] })); if (typeof this.pool.on === "function") { diff --git a/scripts/gitops-render.mjs b/scripts/gitops-render.mjs index 8e92e66d..584c4128 100644 --- a/scripts/gitops-render.mjs +++ b/scripts/gitops-render.mjs @@ -1004,6 +1004,9 @@ function runtimeStoreEnvForProfile(deploy, profile, serviceId) { if (Number.isInteger(postgres.connectionTimeoutMs)) { env.HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS = String(postgres.connectionTimeoutMs); } + if (Number.isInteger(postgres.queryTimeoutMs)) { + env.HWLAB_CLOUD_DB_QUERY_TIMEOUT_MS = String(postgres.queryTimeoutMs); + } if (Number.isInteger(postgres.queryRetryMaxAttempts)) { env.HWLAB_CLOUD_DB_QUERY_RETRY_MAX_ATTEMPTS = String(postgres.queryRetryMaxAttempts); }