An SLO of 99.9% availability is really a budget: over the window you are allowed to fail 0.1% of requests. Spending that budget slowly is normal operation; spending it fast is an incident. Compute how much budget a service has left and how fast it is burning.
For an availability SLO over one window:
budget_rate = 1 - slo_target # fraction you may fail
allowed_failures = budget_rate * total_requests
error_rate = failed_requests / total_requests
burn_rate = error_rate / budget_rate
budget_consumed_pct = 100 * failed_requests / allowed_failures
budget_remaining_pct= 100 - budget_consumed_pct
Burn rate is the number that pages someone. A burn rate of 1.0 means you will spend exactly the whole budget over exactly the window — sustainable. A burn rate of 10 means the month's budget is gone in three days, which is why fast-burn alerts fire on burn rate rather than on raw error rate.
Projecting forward at the current burn rate:
hours_to_exhaustion = (budget_remaining_pct / 100) * window_hours / burn_rate
Two clamps: once the budget is exhausted (budget_remaining_pct <= 0), report 0.0 for both the remaining percentage and the hours; and with a burn rate of 0 the budget never runs out, so report the full window_hours.
Implement:
def error_budget(slo_target, total_requests, failed_requests, window_hours):
Return a dict with keys "allowed_failures", "burn_rate", "budget_consumed_pct", "budget_remaining_pct", "hours_to_exhaustion", in that order, each rounded to 2 decimal places.
print(error_budget(0.999, 2000000, 1200, 720))
Output:
{'allowed_failures': 2000.0, 'burn_rate': 0.6, 'budget_consumed_pct': 60.0, 'budget_remaining_pct': 40.0, 'hours_to_exhaustion': 480.0}
A 99.9% SLO over 2M requests buys 2000 failures. 1200 are spent, so 40% of the budget is left and the service is burning at 0.6x — under 1.0, so it will finish the window inside budget.
print(error_budget(0.999, 2000000, 1200, 720))
{'allowed_failures': 2000.0, 'burn_rate': 0.6, 'budget_consumed_pct': 60.0, 'budget_remaining_pct': 40.0, 'hours_to_exhaustion': 480.0}budget_rate = 1 - 0.999 = 0.001, so 0.001 * 2,000,000 = 2000 failures are allowed. The observed error rate is 1200 / 2,000,000 = 0.0006, giving burn_rate = 0.0006 / 0.001 = 0.6. 1200 of 2000 failures is 60% consumed, 40% left, and projecting 0.4 * 720 / 0.6 gives 480 hours before the budget is gone.
budget_remaining_pct and hours_to_exhaustion as 0.0 (never negative)hours_to_exhaustion is the full window_hours