SLO and SLA
Defining service level objectives for a database, and using error budgets to decide when to stop shipping.
- SLI — a service level indicator: a measured quantity, such as the fraction of queries completing under 100 ms.
- SLO — a service level objective: an internal target for that indicator.
- SLA — a service level agreement: a contractual commitment with consequences, usually set well below the SLO so there is margin.
Indicators worth using
Freshness deserves particular attention for databases with replicas — it is the SLI that captures what replica reads actually promise, and it is rarely defined.
Writing one
An SLO must be specific enough to be measured without argument:
99.9% of read queries against the orders service complete in under 50 ms,
measured at the application's database client, over a rolling 30-day window.Every clause matters: which queries, what threshold, where measured, over what window. "The database is 99.9% available" is not measurable, because nobody has said what available means.
Error budgets
A 99.9% objective over 30 days permits about 43 minutes of failure. That is the error budget, and it is a decision-making tool:
- Budget remaining — ship features, take calculated risks, run the migration.
- Budget exhausted — stop feature work and spend the effort on reliability until it recovers.
# Fraction of the 30-day budget consumed, for a 99.9% objective.
(
1 - (
sum(rate(db_queries_success_total[30d]))
/ sum(rate(db_queries_total[30d]))
)
) / (1 - 0.999)The value of the budget is that it converts "should we do the risky migration this week" from an argument into a number.
Burn-rate alerting
Alert on how fast the budget is being consumed, not on individual failures:
# Fast burn: at this rate the 30-day budget is gone in about two days.
- alert: ErrorBudgetFastBurn
expr: |
(1 - (sum(rate(db_queries_success_total[1h])) / sum(rate(db_queries_total[1h])))) > 14.4 * 0.001
for: 5m
labels: { severity: page }
# Slow burn: sustained, lower-rate consumption. A ticket, not a page.
- alert: ErrorBudgetSlowBurn
expr: |
(1 - (sum(rate(db_queries_success_total[6h])) / sum(rate(db_queries_total[6h])))) > 6 * 0.001
for: 30m
labels: { severity: ticket }This is how you get one page for a real degradation instead of dozens for individual errors.
SLA versus SLO
Set the SLA below the SLO — for example an SLO of 99.9% and an SLA of 99.5%. The gap is the room to detect and fix a degradation before it becomes a contractual matter. Committing externally to the same number you target internally leaves no margin for the ordinary.