Configuring database connections for applications
Scope: Connection pool configuration, sizing, ORM-specific patterns Lines: ~220 Last Updated: 2025-10-18
Activate this skill when:
Problem: Creating new database connections is expensive (TCP handshake, authentication, initialization).
Solution: Reuse connections via a pool.
How it works:
Benefits:
pool_size (or max_connections): Maximum number of connections in pool.
# SQLAlchemy example
engine = create_engine(
"postgresql://...",
pool_size=10 # Max 10 connections
)
How to choose:
pool_size = (num_threads or num_workers) × N
Where N = 1-3 connections per thread/worker
Examples:
Too small: Connection exhaustion, queries wait Too large: Database overload, memory waste
max_overflow: Additional connections beyond pool_size (temporary).
engine = create_engine(
"postgresql://...",
pool_size=10,
max_overflow=5 # Up to 15 total connections
)
Total connections = pool_size + max_overflow
Use case: Handle traffic spikes without exhausting pool.
pool_timeout: Seconds to wait for available connection.
engine = create_engine(
"postgresql://...",
pool_timeout=30 # Wait up to 30 seconds
)
What happens on timeout:
TimeoutError)Recommended: 10-30 seconds
pool_recycle: Seconds before recycling idle connections.
engine = create_engine(
"postgresql://...",
pool_recycle=3600 # Recycle after 1 hour
)
Why recycle:
wait_timeout)Recommended: Slightly less than database's wait_timeout (typically 1-8 hours).
pool_pre_ping: Test connection before use (detect closed connections).
engine = create_engine(
"postgresql://...",
pool_pre_ping=True # Test connection before use
)
How it works: Issues lightweight query (SELECT 1) before returning connection.
Pros: Prevents "connection closed" errors Cons: Slight overhead per query
Recommended: Enable for production reliability.
Optimal pool size = (Tn × (Cm - 1)) + 1
Where:
Tn = Number of threads/workers
Cm = Average number of concurrent queries per request
Example 1: Web app with 20 workers, 1 query per request
pool_size = (20 × (1 - 1)) + 1 = 1
pool_size = 20 (add buffer) → Use 20-30
Example 2: API with 10 workers, 3 queries per request (joining tables)
pool_size = (10 × (3 - 1)) + 1 = 21
pool_size = 21 → Use 25-30
connections = ((core_count × 2) + effective_spindle_count)
For SSDs (no spindles):
connections = (core_count × 2)
Example: 4-core server with SSD
connections = 4 × 2 = 8
Start small, increase if needed:
pool_size = num_workersDatabase limits: PostgreSQL default max_connections = 100, MySQL default 151.
Leave headroom: Don't use all available connections (reserve for admin, monitoring).
from sqlalchemy import create_engine
engine = create_engine(
"postgresql://user:pass@localhost/db",
pool_size=10, # Base pool size
max_overflow=5, # Burst capacity
pool_timeout=30, # Wait time for connection
pool_recycle=3600, # Recycle after 1 hour
pool_pre_ping=True, # Test before use
echo_pool=True # Log pool events (debug only)
)
Pool types:
QueuePool (default): Thread-safe, multiple threadsNullPool: No pooling (creates new connection each time)StaticPool: Single connection (SQLite)// In schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Connection string with pool params
DATABASE_URL="postgresql://user:pass@localhost/db?schema=public&connection_limit=10"
// Or in PrismaClient
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL + "?connection_limit=10&pool_timeout=20"
}
}
})
Parameters:
connection_limit: Max connections (default: num_cpus × 2 + 1)pool_timeout: Wait time in seconds (default: 10)import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
dsn := "host=localhost user=postgres password=pass dbname=db"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
sqlDB, err := db.DB()
// Configure pool
sqlDB.SetMaxOpenConns(25) // Max connections
sqlDB.SetMaxIdleConns(5) // Idle connections
sqlDB.SetConnMaxLifetime(time.Hour) // Max lifetime
sqlDB.SetConnMaxIdleTime(10 * time.Minute) // Max idle time
Parameters:
SetMaxOpenConns: Total max connectionsSetMaxIdleConns: Idle connections in poolSetConnMaxLifetime: Max connection ageSetConnMaxIdleTime: Max idle duration before closeuse diesel::r2d2::{self, ConnectionManager};
use diesel::pg::PgConnection;
let manager = ConnectionManager::<PgConnection>::new(database_url);
let pool = r2d2::Pool::builder()
.max_size(10) // Max connections
.min_idle(Some(2)) // Min idle connections
.connection_timeout(Duration::from_secs(30))
.idle_timeout(Some(Duration::from_secs(600)))
.build(manager)?;
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'db',
'USER': 'user',
'PASSWORD': 'pass',
'HOST': 'localhost',
'PORT': '5432',
'CONN_MAX_AGE': 600, # Connection lifetime (seconds), 0 = no pooling
'OPTIONS': {
'connect_timeout': 10,
}
}
}
Note: Django doesn't have true pooling by default. Use django-db-pool or pgbouncer for pooling.
Error: QueuePool limit of size 10 overflow 5 reached
Error: FATAL: too many connections for role "user"
Error: Timeout waiting for connection from pool
# SQLAlchemy: Check pool status
print(engine.pool.status())
# Output: Pool size: 10 Connections in pool: 2 Current Overflow: 0 Current Checked out connections: 8
# Log pool events
engine = create_engine("...", echo_pool=True)
1. Increase pool size:
engine = create_engine("...", pool_size=20, max_overflow=10)
2. Fix connection leaks:
# ❌ BAD: Connection leak
conn = engine.connect()
result = conn.execute(query)
# Connection never returned!
# ✅ GOOD: Use context manager
with engine.connect() as conn:
result = conn.execute(query)
# Connection automatically returned
3. Optimize slow queries: Use postgres-query-optimization.md
4. Use external pooler: PgBouncer, AWS RDS Proxy
What: External connection pooler for PostgreSQL.
Use case: Multiple applications share database, need more than 100 connections.
# pgbouncer.ini
[databases]
mydb = host=localhost dbname=mydb
[pgbouncer]
pool_mode = transaction # Or session, statement
max_client_conn = 1000 # Client connections
default_pool_size = 20 # Connections to database per user/database pair
reserve_pool_size = 5
Pool modes:
Recommended: transaction mode for web apps.
What: Managed connection pooler for RDS/Aurora.
Benefits:
Configuration:
Max connections: 100
Idle timeout: 1800 seconds
| Metric | What to Monitor | Threshold | |--------|----------------|-----------| | Pool size | Current active connections | < pool_size × 0.8 | | Wait time | Time waiting for connection | < 100ms | | Timeout errors | Connection timeout rate | 0% | | Connection age | Average connection lifetime | < pool_recycle |
PostgreSQL:
-- Check current connections
SELECT count(*), state
FROM pg_stat_activity
WHERE datname = 'mydb'
GROUP BY state;
-- Check connection age
SELECT now() - backend_start AS age, query
FROM pg_stat_activity
ORDER BY age DESC;
MySQL:
SHOW PROCESSLIST;
SHOW STATUS LIKE 'Threads_connected';
✅ Start with conservative pool size (num_workers) ✅ Enable pool_pre_ping for reliability ✅ Set pool_recycle < database timeout ✅ Use context managers to return connections ✅ Monitor pool metrics in production ✅ Use external pooler for high concurrency
❌ Don't set pool_size = max_connections (leave headroom) ❌ Don't leak connections (always close/return) ❌ Don't ignore timeout errors (sign of undersized pool) ❌ Don't use same pool across processes (each process needs its own)
Workload Pool Size
────────────────────────────────────────
Web app (10 workers) 10-20
API (20 workers) 20-40
Background jobs (5 workers) 5-10
Single-threaded app 1-5
High-concurrency async 30-50
Database limit < max_connections - 10
from sqlalchemy import create_engine
engine = create_engine(
"postgresql://user:pass@host/db",
pool_size=20, # Base pool
max_overflow=10, # Burst capacity
pool_timeout=30, # Wait timeout
pool_recycle=3600, # Recycle after 1h
pool_pre_ping=True, # Test before use
)
❌ Pool size too large - Database overload, memory waste ✅ Calculate based on workers/threads, not arbitrary large number
❌ No pool recycling - Stale connections, timeout errors ✅ Set pool_recycle < database timeout
❌ Connection leaks - Pool exhaustion ✅ Always use context managers or explicit close
❌ One pool for all apps - Contention, hard to debug ✅ Each application instance has its own pool
postgres-query-optimization.md - Optimize queries to reduce connection timedatabase-selection.md - Connection pooling considerations per databaseorm-patterns.md - ORM best practices for connection managementLast Updated: 2025-10-18 Format Version: 1.0 (Atomic)