Stage 3 · Build
Database Reliability Engineering
Database SLOs
Latency p99, connection saturation, replication lag — setting meaningful SLOs.
What Are Database SLOs?
Service Level Objectives (SLOs) define the target reliability for your database. They translate abstract reliability goals into measurable metrics with specific thresholds. SLOs prevent both over-engineering (wasting money on unnecessary redundancy) and under-engineering (unacceptable downtime).
database_slos:
postgresql:
availability: 99.95% # ~22 min downtime per month
latency_p99: 50ms # 99% of queries under 50ms
latency_p999: 200ms # 99.9% of queries under 200ms
replication_lag: 5s # replicas within 5 seconds of primary
connection_saturation: 80% # never exceed 80% of max_connections
error_rate: 0.1% # fewer than 0.1% of queries fail
redis:
availability: 99.99%
latency_p99: 5ms
hit_rate: 99% # cache hit rate
memory_usage: 80% # never exceed 80% of maxmemoryThese SLOs define what 'good enough' means. They are not aspirational targets — they are the minimum acceptable performance. Falling below them triggers an incident response.
An SLO is the minimum acceptable reliability level. If you are meeting 99.99% but your SLO is 99.9%, you are over-investing in reliability. SLOs should reflect business requirements, not engineering ambition.
Latency SLOs
-- PostgreSQL: query latency via pg_stat_statements
SELECT
LEFT(query, 60) AS query,
calls,
ROUND(total_exec_time::numeric, 1) AS total_ms,
ROUND(mean_exec_time::numeric, 1) AS avg_ms,
ROUND(stddev_exec_time::numeric, 1) AS stddev_ms
FROM pg_stat_statements
WHERE calls > 1000
ORDER BY mean_exec_time DESC
LIMIT 10;
-- Redis latency monitoring
redis-cli --latency-history -i 5
-- Check slow queries log
SELECT * FROM pg_stat_statements
WHERE mean_exec_time > 100 -- queries averaging over 100ms
ORDER BY mean_exec_time DESC;Measure latency at the application level (end-to-end) and database level (query execution time). Application-level latency includes network, connection pool wait time, and query execution. Database-level latency isolates the database itself.
Availability SLOs
| SLO | Monthly Downtime | Annual Downtime |
|---|---|---|
| 99% | 7.3 hours | 3.65 days |
| 99.9% | 43.8 minutes | 8.76 hours |
| 99.95% | 21.9 minutes | 4.38 hours |
| 99.99% | 4.38 minutes | 52.6 minutes |
| 99.999% | 26.3 seconds | 5.26 minutes |
-- Track downtime incidents
CREATE TABLE downtime_incidents (
id SERIAL PRIMARY KEY,
started_at TIMESTAMP NOT NULL,
ended_at TIMESTAMP,
duration_seconds INTEGER,
cause TEXT,
impact TEXT
);
-- Calculate monthly availability
SELECT
date_trunc('month', started_at) AS month,
SUM(duration_seconds) AS total_downtime_seconds,
ROUND((1 - SUM(duration_seconds)::numeric /
EXTRACT(EPOCH FROM (date_trunc('month', started_at) +
INTERVAL '1 month' - date_trunc('month', started_at)))
) * 100, 4) AS availability_pct
FROM downtime_incidents
WHERE started_at > NOW() - INTERVAL '12 months'
GROUP BY 1
ORDER BY 1 DESC;Availability is calculated as (total_time - downtime) / total_time. Track every incident that affects database availability, including maintenance windows, failovers, and connection exhaustion events.
Connection SLOs
-- Current connection usage
SELECT
count(*) AS current_connections,
current_setting('max_connections')::int AS max_connections,
ROUND(count(*)::numeric / current_setting('max_connections')::int * 100, 1) AS usage_pct
FROM pg_stat_activity
WHERE backend_type = 'client backend';
-- Connection count over time (requires pg_stat_statements or logging)
SELECT
date_trunc('hour', query_start) AS hour,
COUNT(DISTINCT pid) AS unique_connections
FROM pg_stat_activity
WHERE backend_type = 'client backend'
AND query_start > NOW() - INTERVAL '24 hours'
GROUP BY 1
ORDER BY 1;Connection SLOs prevent the database from becoming unreachable due to connection exhaustion. Alert when usage exceeds 80% of max_connections. This gives headroom for emergency connections and burst traffic.
Replication SLOs
-- Primary: replication lag per replica
SELECT
client_addr,
state,
replay_lag,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes,
pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS lag_pretty
FROM pg_stat_replication;
-- Alert if lag exceeds threshold
-- Application: check replication health
SELECT
CASE
WHEN replay_lag > INTERVAL '5 minutes' THEN 'CRITICAL'
WHEN replay_lag > INTERVAL '1 minute' THEN 'WARNING'
ELSE 'HEALTHY'
END AS status,
replay_lag
FROM pg_stat_replication;Replication lag SLOs ensure read replicas serve fresh data. A lag of 5 minutes means the replica is missing recent writes. For read-after-write consistency, route reads to the primary or verify replica freshness.
Error Budget Policy
An error budget is the inverse of your SLO. If your SLO is 99.95%, your error budget is 0.05% — the maximum acceptable failure rate. The error budget determines how many changes you can safely make.
error_budget_policy:
- name: "Healthy"
condition: "budget_remaining > 50%"
action: "Normal development velocity"
deployment_frequency: "Normal"
- name: "Caution"
condition: "budget_remaining 20-50%"
action: "Review deployment practices"
deployment_frequency: "Reduced"
- name: "At Risk"
condition: "budget_remaining 5-20%"
action: "Freeze non-critical changes"
deployment_frequency: "Emergency only"
- name: "Exhausted"
condition: "budget_remaining < 5%"
action: "Full freeze — focus on reliability"
deployment_frequency: "Reliability fixes only"The error budget creates a feedback loop: reliable systems allow faster development, unreliable systems force reliability investment. This aligns engineering velocity with reliability goals.
SLOs should be agreed upon by engineering, product, and leadership. They represent the business's tolerance for database unreliability. Document them, review them quarterly, and use them to guide investment decisions.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.