const { Pool } = require("pg"); let pool = null; /** * Get or create a PostgreSQL connection pool. * Uses connection pooling optimized for Lambda: * - Reuses connections across invocations (when container is warm) * - Small pool size to avoid exhausting database connections * * Expects DATABASE_URL environment variable in format: * postgresql://user:password@host:port/database */ function getPool() { if (!pool) { const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error("DATABASE_URL environment variable is required"); } pool = new Pool({ connectionString, // Lambda-optimized settings max: 1, // Single connection per Lambda instance idleTimeoutMillis: 120000, // 2 minutes - keep connection warm connectionTimeoutMillis: 5000, // 5 seconds to connect }); // Handle pool errors pool.on("error", (err) => { console.error("Unexpected database pool error:", err); pool = null; // Reset pool on error }); } return pool; } /** * Execute a query with automatic connection management. * @param {string} text - SQL query text * @param {Array} params - Query parameters * @returns {Promise} Query result */ async function query(text, params) { const pool = getPool(); const start = Date.now(); try { const result = await pool.query(text, params); const duration = Date.now() - start; console.log(JSON.stringify({ level: "debug", message: "Executed query", query: text.substring(0, 100), duration, rows: result.rowCount, })); return result; } catch (error) { console.error(JSON.stringify({ level: "error", message: "Query failed", query: text.substring(0, 100), error: error.message, })); throw error; } } /** * Close the connection pool (for cleanup). * Call this at the end of Lambda execution if needed. */ async function closePool() { if (pool) { await pool.end(); pool = null; } } module.exports = { getPool, query, closePool, };