Files
livedash-node/lib/database-pool.ts
Kaj Kowalski 0e526641ce feat: implement comprehensive database connection pooling optimization
🎯 SESSION POOLING PERFORMANCE BREAKTHROUGH!

 Critical Issues Fixed:
- Eliminated multiple PrismaClient instances across schedulers
- Fixed connection pool exhaustion risk in processing modules
- Implemented singleton pattern for all database connections
- Added graceful shutdown and connection cleanup

🚀 Enhanced Pooling Features:
- Dual-mode connection pooling (standard + enhanced)
- PostgreSQL native pooling with @prisma/adapter-pg
- Advanced connection monitoring and health checks
- Configurable pool limits and timeouts via environment variables
- Real-time connection statistics and metrics

📊 Performance Optimizations:
- Single shared connection pool across all schedulers
- Configurable connection limits (DATABASE_CONNECTION_LIMIT=20)
- Idle timeout management (DATABASE_POOL_TIMEOUT=10)
- Connection cycling and health validation
- Process termination signal handling

🛠️ New Infrastructure:
- lib/database-pool.ts - Advanced pooling configuration
- app/api/admin/database-health/route.ts - Connection monitoring
- Enhanced lib/prisma.ts with dual-mode support
- Comprehensive documentation in docs/database-connection-pooling.md
- Graceful shutdown handling in lib/schedulers.ts

🎛️ Environment Configuration:
- USE_ENHANCED_POOLING=true for production optimization
- DATABASE_CONNECTION_LIMIT for pool size control
- DATABASE_POOL_TIMEOUT for idle connection management
- Automatic enhanced pooling in production environments

📈 Expected Impact:
- Eliminates connection pool exhaustion under load
- Reduces memory footprint from idle connections
- Improves scheduler performance and reliability
- Enables better resource monitoring and debugging
- Supports horizontal scaling with proper connection management

Production-ready connection pooling with monitoring and health checks!
2025-06-29 09:40:57 +02:00

123 lines
3.4 KiB
TypeScript

// Advanced database connection pooling configuration
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@prisma/client";
import { Pool } from "pg";
import { env } from "./env.js";
// Enhanced connection pool configuration
const createConnectionPool = () => {
// Parse DATABASE_URL to get connection parameters
const databaseUrl = new URL(env.DATABASE_URL);
const pool = new Pool({
host: databaseUrl.hostname,
port: Number.parseInt(databaseUrl.port) || 5432,
user: databaseUrl.username,
password: databaseUrl.password,
database: databaseUrl.pathname.slice(1), // Remove leading slash
ssl: databaseUrl.searchParams.get("sslmode") !== "disable",
// Connection pool configuration
max: env.DATABASE_CONNECTION_LIMIT, // Maximum number of connections
min: 2, // Minimum number of connections to maintain
idleTimeoutMillis: env.DATABASE_POOL_TIMEOUT * 1000, // Close idle connections after timeout
connectionTimeoutMillis: 10000, // Connection timeout
maxUses: 1000, // Maximum uses per connection before cycling
allowExitOnIdle: true, // Allow process to exit when all connections are idle
// Health check configuration
query_timeout: 30000, // Query timeout
keepAlive: true,
keepAliveInitialDelayMillis: 30000,
});
// Connection pool event handlers
pool.on("connect", (_client) => {
console.log(
`Database connection established. Active connections: ${pool.totalCount}`
);
});
pool.on("acquire", (_client) => {
console.log(
`Connection acquired from pool. Waiting: ${pool.waitingCount}, Idle: ${pool.idleCount}`
);
});
pool.on("release", (_client) => {
console.log(
`Connection released to pool. Active: ${pool.totalCount - pool.idleCount}, Idle: ${pool.idleCount}`
);
});
pool.on("error", (err) => {
console.error("Database pool error:", err);
});
pool.on("remove", (_client) => {
console.log(
`Connection removed from pool. Total connections: ${pool.totalCount}`
);
});
return pool;
};
// Create adapter with connection pool
export const createEnhancedPrismaClient = () => {
const pool = createConnectionPool();
const adapter = new PrismaPg(pool);
return new PrismaClient({
adapter,
log:
process.env.NODE_ENV === "development"
? ["query", "info", "warn", "error"]
: ["error"],
});
};
// Connection pool monitoring utilities
export const getPoolStats = (pool: Pool) => {
return {
totalConnections: pool.totalCount,
idleConnections: pool.idleCount,
waitingQueries: pool.waitingCount,
activeConnections: pool.totalCount - pool.idleCount,
};
};
// Health check for the connection pool
export const checkPoolHealth = async (
pool: Pool
): Promise<{
healthy: boolean;
stats: ReturnType<typeof getPoolStats>;
error?: string;
}> => {
try {
const client = await pool.connect();
await client.query("SELECT 1");
client.release();
return {
healthy: true,
stats: getPoolStats(pool),
};
} catch (error) {
return {
healthy: false,
stats: getPoolStats(pool),
error: error instanceof Error ? error.message : String(error),
};
}
};
// Graceful pool shutdown
export const shutdownPool = async (pool: Pool) => {
console.log("Shutting down database connection pool...");
await pool.end();
console.log("Database connection pool shut down successfully.");
};