| 72 | // and so /health stays off the query hot path. Lazily created on first probe. |
| 73 | let healthPool = null; |
| 74 | function getHealthPool() { |
| 75 | if (!healthPool) { |
| 76 | healthPool = new pg.Pool({ |
| 77 | connectionString: process.env.POSTGRES_URL, |
| 78 | max: 1, |
| 79 | connectionTimeoutMillis: 500, // fail fast on a dead DB instead of hanging |
| 80 | idleTimeoutMillis: 10_000, |
| 81 | // Abort a hung probe at the driver level too — Promise.race alone leaves |
| 82 | // the query occupying the pool's single connection, and repeated /health |
| 83 | // calls would then queue on it unboundedly. |
| 84 | query_timeout: 500, |
| 85 | }); |
| 86 | // Swallow idle-client errors — the probe query is the source of truth. |
| 87 | healthPool.on('error', () => {}); |
| 88 | } |
| 89 | return healthPool; |
| 90 | } |
| 91 | |
| 92 | // Probe Postgres with SELECT 1, capped at ~500ms so a stalled DB can't hang the |
| 93 | // health endpoint. connectionTimeoutMillis covers connect stalls; the race timer |