Database Observability and Monitoring¶
Comprehensive monitoring setup for Blacklake's database infrastructure with Prometheus metrics, Grafana dashboards, and alerting.
Architecture¶
graph TB
subgraph "Database Layer"
PG0[Patroni PG0]
PG1[Patroni PG1]
PGB[PgBouncer]
end
subgraph "Metrics Collection"
PGE[PostgreSQL Exporter]
PBME[PgBouncer Exporter]
HAE[HAProxy Exporter]
end
subgraph "Monitoring Stack"
PROM[Prometheus]
GRAF[Grafana]
ALERT[AlertManager]
end
subgraph "Storage"
TS[Time Series DB]
LOGS[Log Storage]
end
PG0 --> PGE
PG1 --> PGE
PGB --> PBME
PGE --> PROM
PBME --> PROM
HAE --> PROM
PROM --> TS
PROM --> GRAF
PROM --> ALERT Metrics Collection¶
PostgreSQL Metrics¶
- Connection Metrics: Active connections, connection limits
- Query Performance: Query duration, slow queries, locks
- Replication: Lag, replication slots, WAL generation
- Database Size: Database size, table sizes, index usage
- Cache Performance: Buffer hit ratio, cache efficiency
PgBouncer Metrics¶
- Pool Metrics: Active connections, pool utilization
- Query Metrics: Queries per second, average query time
- Client Metrics: Client connections, wait time
- Server Metrics: Server connections, server utilization
HAProxy Metrics¶
- Backend Health: Server status, health checks
- Connection Metrics: Active connections, connection rate
- Response Time: Average response time, error rate
- Load Balancing: Distribution across backends
Prometheus Configuration¶
postgres-exporter¶
# PostgreSQL exporter configuration
DATA_SOURCE_NAME: "postgresql://exporter:exporter123@haproxy:5432/blacklake?sslmode=require"
Key Metrics: - pg_stat_database_* - Database statistics - pg_stat_user_tables_* - Table statistics - pg_stat_user_indexes_* - Index statistics - pg_replication_* - Replication metrics - pg_stat_activity_* - Active connections
pgbouncer-exporter¶
# PgBouncer exporter configuration
PGBOUNCER_EXPORTER_PGBOUNCER_CONNECTION_STRING: "postgres://pgbouncer_admin:pgbouncer123@pgbouncer:6432/pgbouncer"
Key Metrics: - pgbouncer_pools_* - Pool statistics - pgbouncer_clients_* - Client connections - pgbouncer_servers_* - Server connections - pgbouncer_stats_* - General statistics
Grafana Dashboards¶
PostgreSQL Overview Dashboard¶
{
"dashboard": {
"title": "PostgreSQL Overview",
"panels": [
{
"title": "Database Connections",
"type": "stat",
"targets": [
{
"expr": "pg_stat_database_numbackends",
"legendFormat": "Active Connections"
}
]
},
{
"title": "Query Performance",
"type": "graph",
"targets": [
{
"expr": "rate(pg_stat_database_tup_returned[5m])",
"legendFormat": "Tuples Returned/sec"
}
]
}
]
}
}
PgBouncer Dashboard¶
{
"dashboard": {
"title": "PgBouncer Pooling",
"panels": [
{
"title": "Pool Utilization",
"type": "gauge",
"targets": [
{
"expr": "pgbouncer_pools_cl_active / pgbouncer_pools_maxwait",
"legendFormat": "Pool Utilization %"
}
]
}
]
}
}
Alerting Rules¶
Critical Alerts¶
groups:
- name: database-critical
rules:
- alert: DatabaseDown
expr: up{job="postgres-exporter"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "PostgreSQL database is down"
- alert: HighConnectionCount
expr: pg_stat_database_numbackends / pg_settings_max_connections > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "High connection count: {{ $value }}%"
- alert: ReplicationLag
expr: pg_replication_lag > 60
for: 2m
labels:
severity: critical
annotations:
summary: "Replication lag is {{ $value }} seconds"
Performance Alerts¶
- name: database-performance
rules:
- alert: SlowQueries
expr: rate(pg_stat_database_tup_returned[5m]) < 100
for: 10m
labels:
severity: warning
annotations:
summary: "Slow query performance detected"
- alert: HighCacheMissRatio
expr: (1 - (pg_stat_database_blks_hit / (pg_stat_database_blks_hit + pg_stat_database_blks_read))) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "High cache miss ratio: {{ $value }}%"
Log Management¶
PostgreSQL Logs¶
-- Enable comprehensive logging
ALTER SYSTEM SET log_statement = 'all';
ALTER SYSTEM SET log_min_duration_statement = 1000;
ALTER SYSTEM SET log_connections = on;
ALTER SYSTEM SET log_disconnections = on;
ALTER SYSTEM SET log_lock_waits = on;
ALTER SYSTEM SET log_checkpoints = on;
PgBouncer Logs¶
# pgbouncer.ini logging configuration
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1
log_stats = 1
Log Rotation¶
# /etc/logrotate.d/postgresql
/var/log/postgresql/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 644 postgres postgres
postrotate
/bin/kill -HUP `cat /var/run/postgresql/postgresql.pid 2> /dev/null` 2> /dev/null || true
endscript
}
Health Checks¶
Application Health Endpoints¶
# Database health check
@app.get("/health/db")
async def health_database():
try:
with engine.connect() as conn:
result = conn.execute(text("SELECT 1"))
return {"status": "healthy", "database": "connected"}
except Exception as e:
return {"status": "unhealthy", "database": "error", "error": str(e)}
# HA cluster health check
@app.get("/health/ha")
async def health_ha():
try:
# Check Patroni cluster health
response = requests.get("http://patroni-pg0:8008/health")
return {"status": "healthy", "ha": "cluster_healthy"}
except Exception as e:
return {"status": "unhealthy", "ha": "error", "error": str(e)}
External Health Checks¶
# Database connectivity
curl -f http://localhost:8000/health/db
# HA cluster health
curl -f http://localhost:8000/health/ha
# Patroni cluster status
curl -f http://localhost:8008/health
curl -f http://localhost:8009/health
Monitoring Setup¶
Docker Compose Monitoring Stack¶
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-storage:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
volumes:
grafana-storage:
Prometheus Configuration¶
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'postgres-exporter'
static_configs:
- targets: ['postgres-exporter:9187']
scrape_interval: 10s
- job_name: 'pgbouncer-exporter'
static_configs:
- targets: ['pgbouncer-exporter:9128']
scrape_interval: 10s
- job_name: 'haproxy'
static_configs:
- targets: ['haproxy:8404']
scrape_interval: 10s
- job_name: 'blacklake-app'
static_configs:
- targets: ['blacklake-app:8000']
scrape_interval: 30s
metrics_path: '/metrics'
Key Metrics to Monitor¶
Database Performance¶
- Connection Count:
pg_stat_database_numbackends - Query Rate:
rate(pg_stat_database_tup_returned[5m]) - Cache Hit Ratio:
pg_stat_database_blks_hit / (pg_stat_database_blks_hit + pg_stat_database_blks_read) - Replication Lag:
pg_replication_lag
Pooling Performance¶
- Pool Utilization:
pgbouncer_pools_cl_active / pgbouncer_pools_maxwait - Wait Time:
pgbouncer_stats_avg_wait - Query Rate:
rate(pgbouncer_stats_total_requests[5m])
System Resources¶
- CPU Usage:
rate(process_cpu_seconds_total[5m]) - Memory Usage:
process_resident_memory_bytes - Disk I/O:
rate(node_disk_io_time_seconds_total[5m])
Troubleshooting¶
Common Issues¶
1. High Connection Count¶
Symptoms: Connection limit warnings Resolution:
-- Check current connections
SELECT count(*) FROM pg_stat_activity;
-- Check connection limits
SHOW max_connections;
-- Adjust PgBouncer pool size
ALTER DATABASE blacklake SET pool_size = 100;
2. Replication Lag¶
Symptoms: Replica falling behind Resolution:
# Check replication status
curl http://patroni-pg1:8008/replica
# Check WAL generation
SELECT pg_current_wal_lsn();
3. Slow Queries¶
Symptoms: High query duration Resolution:
-- Identify slow queries
SELECT query, mean_time, calls
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
-- Check for locks
SELECT * FROM pg_locks WHERE NOT granted;
Performance Tuning¶
Database Tuning¶
-- Adjust shared_buffers
ALTER SYSTEM SET shared_buffers = '1GB';
-- Enable query statistics
ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements';
-- Set work memory
ALTER SYSTEM SET work_mem = '64MB';
PgBouncer Tuning¶
# Adjust pool sizes
default_pool_size = 50
min_pool_size = 5
reserve_pool_size = 20
# Optimize timeouts
server_idle_timeout = 60
query_timeout = 0
Best Practices¶
Monitoring Strategy¶
- Comprehensive Coverage: Monitor all database components
- Proactive Alerting: Set up alerts before issues occur
- Performance Baselines: Establish normal performance baselines
- Regular Reviews: Review metrics and adjust thresholds
Alert Management¶
- Severity Levels: Define clear severity levels
- Escalation Procedures: Define escalation paths
- Runbook Integration: Link alerts to runbooks
- False Positive Reduction: Tune alert thresholds
Documentation¶
- Runbook Updates: Keep runbooks current
- Incident Postmortems: Document and learn from incidents
- Performance Reviews: Regular performance analysis
- Capacity Planning: Plan for growth
Next Steps¶
- PostgreSQL HA - High availability setup
- Connection Pooling - PgBouncer configuration
- Backups - Backup and recovery procedures