Connection Pooling
Connection Pooling
Section titled “Connection Pooling”Optimize database connections for performance and scalability.
Overview
Section titled “Overview”Why Connection Pooling?
Section titled “Why Connection Pooling?”Database connections are expensive resources:
| Problem | Impact |
|---|---|
| Connection overhead | ~100-300ms per new connection |
| Memory usage | ~10MB per PostgreSQL connection |
| Connection limits | PostgreSQL default: 100 connections |
Connection pooling solves these by maintaining reusable connections.
Pooling Architecture
Section titled “Pooling Architecture”Application-Level Pooling
Section titled “Application-Level Pooling”Mantis components include built-in connection pooling.
Mandible Pool Configuration
Section titled “Mandible Pool Configuration”Mandible is the only component with a database pool. The [database] fields are
url, max_connections, min_idle, and connection_timeout_secs:
[database]url = "postgres://mantis:password@localhost:5432/mantis"max_connections = 20 # Maximum pool sizemin_idle = 5 # Minimum idle connectionsconnection_timeout_secs = 30 # Connection acquisition timeoutEnvironment Variables
Section titled “Environment Variables”# Mandible (the connection URL; layered overrides use the MANDIBLE__DATABASE__ prefix)DATABASE_URL=postgres://mantis:password@localhost:5432/mantisMANDIBLE__DATABASE__MAX_CONNECTIONS=20MANDIBLE__DATABASE__MIN_IDLE=5MANDIBLE__DATABASE__CONNECTION_TIMEOUT_SECS=30Sizing Guidelines
Section titled “Sizing Guidelines”Pool Size Calculation
Section titled “Pool Size Calculation”Formula:
pool_size = (core_count * 2) + spinning_disk_countFor SSD storage:
pool_size = core_count * 2Example for 4-core server with SSD:
pool_size = 4 * 2 = 8Recommended Settings
Section titled “Recommended Settings”| Deployment | Mandible pool |
|---|---|
| Development | 5 |
| Small (< 100 targets) | 10 |
| Medium (< 1000 targets) | 20 |
| Large (1000+ targets) | 50 |
(Only Mandible connects to PostgreSQL, so it is the only pool to size.)
PostgreSQL max_connections
Section titled “PostgreSQL max_connections”Ensure PostgreSQL can handle every Mandible instance’s pool:
max_connections >= (mandible_pool * mandible_instances) + overhead
Example:max_connections >= (20 * 2) + 20 = 60# postgresql.confmax_connections = 200PgBouncer Setup
Section titled “PgBouncer Setup”For large deployments, use PgBouncer as an external connection pooler.
Installation
Section titled “Installation”# Ubuntu/Debiansudo apt install pgbouncer
# RHEL/Rocky/Almasudo dnf install pgbouncerConfiguration
Section titled “Configuration”[databases]mantis = host=localhost port=5432 dbname=mantis
[pgbouncer]listen_addr = 0.0.0.0listen_port = 6432auth_type = scram-sha-256auth_file = /etc/pgbouncer/userlist.txt
# Pool settingspool_mode = transaction # transaction | session | statementdefault_pool_size = 50 # Connections per database/usermax_client_conn = 1000 # Max client connectionsmin_pool_size = 10 # Minimum pool sizereserve_pool_size = 5 # Emergency connections
# Connection limitsmax_db_connections = 100 # Max connections to database
# Timeoutsserver_connect_timeout = 15server_idle_timeout = 600server_lifetime = 3600client_idle_timeout = 0 # 0 = disabled
# Logginglog_connections = 1log_disconnections = 1stats_period = 60User Authentication
Section titled “User Authentication”Create userlist:
# Generate password hashpsql -U postgres -c "SELECT concat('\"mantis\" \"', passwd, '\"') FROM pg_shadow WHERE usename='mantis'"
# Add to userlist.txtecho '"mantis" "SCRAM-SHA-256$..."' >> /etc/pgbouncer/userlist.txtPool Modes
Section titled “Pool Modes”| Mode | Description | Use Case |
|---|---|---|
| session | Connection per client session | Legacy apps |
| transaction | Connection per transaction | Most applications |
| statement | Connection per statement | Simple queries |
Start PgBouncer
Section titled “Start PgBouncer”sudo systemctl enable pgbouncersudo systemctl start pgbouncerConnect Through PgBouncer
Section titled “Connect Through PgBouncer”Update application configuration:
[database]url = "postgres://mantis:password@localhost:6432/mantis"max_connections = 50 # Can be higher with PgBouncerMonitoring Connections
Section titled “Monitoring Connections”Application Pool Metrics
Section titled “Application Pool Metrics”The /api/v1/health endpoint performs a database connectivity check and
returns {"status":"ok"} (200) when healthy or {"status":"unavailable"}
(503) when the database is unreachable. It does not expose pool internals.
For a pure process-liveness check that never gates on external dependencies,
use /api/v1/health/live instead (always 200 while the process is running).
Monitor pool saturation from the PostgreSQL side:
-- Active vs idle connections for the Mandible DB userSELECT state, count(*) FROM pg_stat_activityWHERE usename = 'mantis' GROUP BY state;PostgreSQL Connections
Section titled “PostgreSQL Connections”-- Current connectionsSELECT count(*), stateFROM pg_stat_activityWHERE datname = 'mantis'GROUP BY state;
-- Connections by applicationSELECT application_name, count(*)FROM pg_stat_activityWHERE datname = 'mantis'GROUP BY application_name;
-- Long-running connectionsSELECT pid, usename, application_name, now() - backend_start as connection_age, state, queryFROM pg_stat_activityWHERE datname = 'mantis'ORDER BY backend_start;PgBouncer Statistics
Section titled “PgBouncer Statistics”# Connect to PgBouncer admin consolepsql -p 6432 -U pgbouncer pgbouncer
# Show poolsSHOW POOLS;
# Show clientsSHOW CLIENTS;
# Show servers (actual PostgreSQL connections)SHOW SERVERS;
# Show statisticsSHOW STATS;Troubleshooting
Section titled “Troubleshooting”Connection Exhaustion
Section titled “Connection Exhaustion”Symptoms:
- “too many connections” errors
- Slow response times
- Connection timeouts
Diagnosis:
SELECT count(*) FROM pg_stat_activity;SELECT * FROM pg_stat_activity WHERE state = 'idle' ORDER BY backend_start;Solutions:
- Increase
max_connectionsin PostgreSQL - Reduce pool sizes
- Use PgBouncer
- Identify connection leaks
Connection Leaks
Section titled “Connection Leaks”Symptoms:
- Pool exhaustion over time
- Idle connections accumulating
Diagnosis:
-- Find idle connectionsSELECT pid, usename, application_name, now() - state_change as idle_timeFROM pg_stat_activityWHERE state = 'idle'ORDER BY idle_time DESC;Solutions:
- Lower
min_idleso fewer idle connections are held open, or use PgBouncer’sserver_idle_timeoutto recycle idle server connections - Check application code for unclosed connections
- Enable connection lifecycle logging
Slow Connection Acquisition
Section titled “Slow Connection Acquisition”Symptoms:
- High latency on first requests
- Timeout during connection
Diagnosis:
# Test connection timetime psql -h localhost -U mantis -c "SELECT 1"Solutions:
- Increase
min_idleto pre-warm the pool - Check network latency to database
- Reduce SSL negotiation overhead (connection reuse)
PgBouncer Issues
Section titled “PgBouncer Issues”Client authentication failed:
# Check userlist.txt formatcat /etc/pgbouncer/userlist.txt
# Verify auth type matches PostgreSQLgrep auth_type /etc/pgbouncer/pgbouncer.iniNo free connections:
# Check pool statuspsql -p 6432 pgbouncer -c "SHOW POOLS"
# Increase pool size or max_db_connectionsBest Practices
Section titled “Best Practices”1. Size Pools Appropriately
Section titled “1. Size Pools Appropriately”- Don’t over-provision (wastes memory)
- Don’t under-provision (causes contention)
- Monitor and adjust based on metrics
2. Use Connection Timeouts
Section titled “2. Use Connection Timeouts”The [database] section accepts only url, max_connections, min_idle, and
connection_timeout_secs. Set the acquisition timeout here; for idle-recycling and
max-lifetime behavior use an external pooler (PgBouncer).
[database]connection_timeout_secs = 30 # Connection acquisition timeout3. Enable Health Checks
Section titled “3. Enable Health Checks”Built-in health checks verify database connectivity:
curl https://localhost:3000/api/v1/health/ready4. Monitor Pool Utilization
Section titled “4. Monitor Pool Utilization”Mantis does not currently export pool-utilization gauges to Prometheus.
Monitor pool saturation from the PostgreSQL side using pg_stat_activity:
-- Count active connections for the Mantis DB userSELECT state, count(*) FROM pg_stat_activityWHERE usename = 'mantis' GROUP BY state;Alert when the active connection count approaches the configured
max_connections value in your PostgreSQL instance.
5. Use Transaction Mode
Section titled “5. Use Transaction Mode”For PgBouncer, transaction mode is most efficient:
pool_mode = transactionNext Steps
Section titled “Next Steps”- Maintenance - Database maintenance
- PostgreSQL Setup - PostgreSQL configuration
- Monitoring - Metrics and alerts
