Files
ZachariahSharma 8920e746f4 fix: add pg pool timeouts to prevent hanging requests
Without timeouts, a postgres connection or query that hangs causes the
entire authenticated request to hang indefinitely, making the reverse
proxy return 502. Now fails fast at 5s connect / 10s query.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 20:15:49 -06:00

81 lines
2.1 KiB
JavaScript

"use strict"
const { Pool } = require("pg")
const fs = require("fs")
const path = require("path")
const MIGRATIONS_DIR = path.join(__dirname, "..", "migrations")
const ADVISORY_LOCK_KEY = 12345678
function createPool(databaseUrl) {
return new Pool({
connectionString: databaseUrl,
connectionTimeoutMillis: 5000,
query_timeout: 10000,
idleTimeoutMillis: 30000,
})
}
async function withTransaction(pool, fn) {
const client = await pool.connect()
try {
await client.query("BEGIN")
const result = await fn(client)
await client.query("COMMIT")
return result
} catch (err) {
await client.query("ROLLBACK")
throw err
} finally {
client.release()
}
}
async function runMigrations(pool) {
const client = await pool.connect()
try {
// Acquire advisory lock to prevent concurrent migrations
await client.query(`SELECT pg_advisory_lock(${ADVISORY_LOCK_KEY})`)
try {
// Create schema_migrations table if it doesn't exist
await client.query(`
CREATE TABLE IF NOT EXISTS schema_migrations (
name text PRIMARY KEY,
applied_at timestamptz DEFAULT now()
)
`)
// Read all .sql files from migrations directory, sorted alphabetically
const files = fs
.readdirSync(MIGRATIONS_DIR)
.filter((f) => f.endsWith(".sql"))
.sort()
for (const file of files) {
// Check if migration has already been applied
const { rows } = await client.query(
"SELECT name FROM schema_migrations WHERE name = $1",
[file]
)
if (rows.length === 0) {
const sql = fs.readFileSync(path.join(MIGRATIONS_DIR, file), "utf8")
await client.query(sql)
await client.query(
"INSERT INTO schema_migrations (name) VALUES ($1)",
[file]
)
}
}
} finally {
// Always release the advisory lock
await client.query(`SELECT pg_advisory_unlock(${ADVISORY_LOCK_KEY})`)
}
} finally {
client.release()
}
}
module.exports = { createPool, withTransaction, runMigrations }