ЁЯПл The SchoolтА║ЁЯЫая╕П SREтА║ЁЯУЬ рдзрдбрд╛ 02 тАФ рдХрд░рд╛рд░ рдореНрд╣рдгреВрди SLOs: error budget POLICY
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУЬ рдзрдбрд╛ 02 тАФ рдХрд░рд╛рд░ рдореНрд╣рдгреВрди SLOs: error budget POLICY

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 02 ┬╖ рдорд╛рдЧреЗ: lesson-01-what-is-sre ┬╖ рдкреБрдвреЗ: lesson-03-toil


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбрд╛ 01, рдЕрдзрд┐рдХ рддреЛ рдХрд░рд╛рд░ рдЬреЛ рдард░рд╡рддреЛ рдХреА рд╢рд╛рд│рд╛ рдЖрдкрд▓реНрдпрд╛ рдЗрдорд╛рд░рддреАрдВрдордзреНрдпреЗ рдХрд┐рддреА рд╡реЗрдЧрд╛рдиреЗ рдмрджрд▓ рдХрд░реВ рд╢рдХрддреЗ. Observability school SLIs, SLOs рдЖрдгрд┐ budget рдЪреЗ рдЧрдгрд┐рдд рд╢рд┐рдХрд╡рддреЗ. рд╣рд╛ рдзрдбрд╛ policy рдмрджреНрджрд▓ рдЖрд╣реЗ: рдХрд╛рд╣реАрд╣реА рдмрд┐рдШрдбрдгреНрдпрд╛рдЪреНрдпрд╛ рдЖрдзреА рд╕рд╣реА рдХреЗрд▓реЗрд▓рд╛ рдПрдХ рджрд╕реНрддрдРрд╡рдЬ, рдЬреЛ рд╕рд╛рдВрдЧрддреЛ рдХреА budget рдЦрд░реНрдЪ рд╣реЛрдд рдЬрд╛рдИрд▓ рддрд╕реЗ product, developers рдЖрдгрд┐ рдкрдердХ рдХрд╛рдп рдХрд░рддреАрд▓. sre/crew.py рдордзреАрд▓ BudgetPolicy рдЖрдгрд┐ sre/demo.py рдордзреАрд▓ budget().

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд╕рддреНрд░рд╛рдЪреНрдпрд╛ рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛ рдкрдердХ рдЖрдгрд┐ рдореБрдЦреНрдпрд╛рдзреНрдпрд╛рдкрдХ рдПрдХ рдХрд░рд╛рд░ рдХрд░рддрд╛рдд. тЬНя╕П

"Timetable рдЗрдорд╛рд░рдд рдереЛрдбреАрд╢реА рдмрд┐рдШрдбрд▓реЗрд▓реА рд░рд╛рд╣реВ рд╢рдХрддреЗ тАФ 1,000 рдкреИрдХреА 1 request. рддреА рдереЛрдбреАрд╢реА рдореБрднрд╛ рдореНрд╣рдгрдЬреЗ рджреБрд░реБрд╕реНрддреАрдЪреЗ рдмрдЬреЗрдЯ. рдмрдЬреЗрдЯ рд╢рд┐рд▓реНрд▓рдХ рдЕрд╕реЗрдкрд░реНрдпрдВрдд рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рд╣рд╡реЗ рддрд┐рддрдХреНрдпрд╛ рд╡реЗрдЧрд╛рдиреЗ рдмрджрд▓ рдХрд░реВ рд╢рдХрддрд╛рдд: рдирд╡реНрдпрд╛ рдЦреЛрд▓реНрдпрд╛, рдирд╡реЗ timetables, рдирд╡реА features."

"рдмрдЬреЗрдЯ рдХрдореА рдЭрд╛рд▓реЗ, рддрд░ рдЖрдореНрд╣реА рд╡реЗрдЧ рдХрдореА рдХрд░рддреЛ рдЖрдгрд┐ рдЖрдзреА рдЧреЛрд╖реНрдЯреА рджреБрд░реБрд╕реНрдд рдХрд░рддреЛ."

"рдмрдЬреЗрдЯ рдкреВрд░реНрдг рд╕рдВрдкрд▓реЗ, рддрд░ рдЖрдореНрд╣реА рдмрджрд▓ рдХрд░рдгреЗ рдерд╛рдВрдмрд╡рддреЛ тАФ рдлрдХреНрдд рддрд╛рддрдбреАрдЪреНрдпрд╛ рджреБрд░реБрд╕реНрддреНрдпрд╛ тАФ рдЬреЛрдкрд░реНрдпрдВрдд рдЗрдорд╛рд░рдд рдкреБрдиреНрд╣рд╛ рдирд┐рд░реЛрдЧреА рд╣реЛрдд рдирд╛рд╣реА."

рдорд╣рддреНрддреНрд╡рд╛рдЪрд╛ рднрд╛рдЧ: рддреНрдпрд╛рдВрдиреА рд╣реЗ рдЧрд│рддреА рд╕реБрд░реВ рд╣реЛрдгреНрдпрд╛рдЪреНрдпрд╛ рдЖрдзреА рд▓рд┐рд╣реВрди рдареЗрд╡рд▓реЗ рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ рд╕рд╣реА рдХреЗрд▓реА. рдореНрд╣рдгреВрди рджрд┐рд╡рд╕ 24 рд▓рд╛ рдмрдЬреЗрдЯ рд╕рдВрдкрддреЗ рддреЗрд╡реНрд╣рд╛ рдХреЛрдгреАрдЪ рд╡рд╛рдж рдШрд╛рд▓рдд рдирд╛рд╣реА. рдХрд╛рдп рд╣реЛрддреЗ рддреЗ рдХрд╛рдЧрджрд╛рд╡рд░ рдЖрдзреАрдЪ рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдЖрд╣реЗ.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    n["ЁЯЯв normal<br/>under 75% used<br/>release freely"] -->|"day 20: 83.9%"| c["ЁЯЯб caution<br/>75тАУ100%<br/>reliability first"]
    c -->|"day 24: 101.8%"| f["ЁЯФ┤ freeze<br/>100% or more<br/>P0 + security only"]
    f -->|"day 34: day-6 incident<br/>leaves the window тЖТ 80.4%"| c
    c -->|"day 43: 57.1%"| n
    big["ЁЯТе one incident > 20%<br/>тЖТ P0 action in its postmortem"]

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/sre/lesson-diagrams.html#l02

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдирд╛рд╣реАрддрд░ "reliability рд╡рд┐рд░реБрджреНрдз features" рд╣рд╛ рдирд┐рд░реНрдгрдп рдорддреЗ рдЖрдгрд┐ рдЬреНрдпреЗрд╖реНрдарддреЗрд╡рд░реВрди рдкреБрдиреНрд╣рд╛ рдкреБрдиреНрд╣рд╛ рдШреЗрддрд▓рд╛ рдЬрд╛рддреЛ, рдмрд╣реБрддреЗрдХ рд╡реЗрд│рд╛ рдПрдЦрд╛рджреНрдпрд╛ outage рдЪреНрдпрд╛ рдордзреНрдпрд╛рдд. Budget рддреНрдпрд╛рд▓рд╛ data рдмрдирд╡рддреЗ, рдЖрдгрд┐ policy рддреНрдпрд╛ data рд▓рд╛ рдХреГрддреАрдд рдмрджрд▓рддреЗ. рддреЗ рд╡реЗрдЧрд╛рдЪреЗрд╣реА рд░рдХреНрд╖рдг рдХрд░рддреЗ: budget рд╢рд┐рд▓реНрд▓рдХ рдЕрд╕рддрд╛рдирд╛ рдХреЛрдгреАрд╣реА "рд╕реБрд░рдХреНрд╖рд┐рддрддреЗрд╕рд╛рдареА рдореНрд╣рдгреВрди" release рдЕрдбрд╡реВ рд╢рдХрдд рдирд╛рд╣реА. 100% рд╣реЗ рдЪреБрдХреАрдЪреЗ рд▓рдХреНрд╖реНрдп рдЖрд╣реЗ: SRE рдкреБрд╕реНрддрдХрд╛рддрд▓реЗ рдЙрджрд╛рд╣рд░рдг рдЕрд╕реЗ user рдЪреЗ рдЖрд╣реЗ рдЬреНрдпрд╛рдЪрд╛ рд╕реНрд╡рддрдГрдЪрд╛ phone рдЖрдгрд┐ network 99% рд╡реЗрд│ рдЪрд╛рд▓рддреЛ тАФ рддреЛ 99.99% service рдЖрдгрд┐ 99.999% service рдпрд╛рдВрддреАрд▓ рдлрд░рдХ рдУрд│рдЦреВ рд╢рдХрдд рдирд╛рд╣реА, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рдЬрд╛рд╕реНрддреАрдЪрд╛ nine рдЬрд╛рд╕реНрдд рдорд╣рд╛рдЧ рдкрдбрддреЛ (рдзрдбрд╛ 09).

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

sre/crew.py рдордзреАрд▓ BudgetPolicy(slo, window_days, daily_requests, slow=0.75, single=0.20) budget рдореЛрдЬрддреЗ. run(days, baseline, incidents) рджрд░рд░реЛрдЬ baseline failures рдЖрдгрд┐ рддреНрдпрд╛ рджрд┐рд╡рд╕рд╛рдЪрд╛ incident рдЬреЛрдбрддреЗ, рджрд░рд░реЛрдЬ rolling window рдордзрд▓рд╛ рдЦрд░реНрдЪ рдореЛрдЬрддреЗ, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ state change рдЖрдгрд┐ рдПрдХрд╛рдЪ-incident рдЪреНрдпрд╛ рд░реЗрд╖реЗрд╡рд░рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ incident рдкрд░рдд рджреЗрддреЗ. budget() рдЪрд╛рд░ incidents рд╕рд╣ (рджрд┐рд╡рд╕ 6, 15, 20, 24) 45 рджрд┐рд╡рд╕ рдЪрд╛рд▓рд╡рддреЗ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 sre/demo.py budget
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from crew import BudgetPolicy
for slo in (0.999, 0.9995, 0.99):
    pol = BudgetPolicy(slo=slo)
    changes, big = pol.run(45, baseline=250, incidents={6: 7000, 15: 6500, 20: 5000, 24: 4000})
    print(f"SLO {slo:.2%} тЖТ budget {pol.budget:>7,} ┬╖ states: " + " тЖТ ".join(f"{s}@{d}" for d, _, s in changes) + f" ┬╖ P0 postmortems: {len(big)}")
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

budget рд╣реЗ print рдХрд░рддреЗ:

тФАтФА timetable service ┬╖ SLO 99.9% over a rolling 28 days ┬╖ 1,000,000 requests a day тЖТ budget 28,000 failed requests
   day  1:   0.9% of the budget used тЖТ normal
   day 20:  83.9% of the budget used тЖТ caution
   day 24: 101.8% of the budget used тЖТ freeze
   day 34:  80.4% of the budget used тЖТ caution
   day 43:  57.1% of the budget used тЖТ normal
   day  6 incident burned 7,000 = 25.0% (> 20%) тЖТ its postmortem must carry a P0 action item
   day 15 incident burned 6,500 = 23.2% (> 20%) тЖТ its postmortem must carry a P0 action item

рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ:

SLO 99.90% тЖТ budget  28,000 ┬╖ states: normal@1 тЖТ caution@20 тЖТ freeze@24 тЖТ caution@34 тЖТ normal@43 ┬╖ P0 postmortems: 2
SLO 99.95% тЖТ budget  14,000 ┬╖ states: normal@1 тЖТ caution@14 тЖТ freeze@15 ┬╖ P0 postmortems: 4
SLO 99.00% тЖТ budget 280,000 ┬╖ states: normal@1 ┬╖ P0 postmortems: 0

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреЗрдЪ рдЪрд╛рд░ incidents рддреАрди рдЕрдЧрджреА рд╡реЗрдЧрд│реНрдпрд╛ рддрд┐рдорд╛рд╣реА рджреЗрддрд╛рдд. 99.9% рд╡рд░ policy рджрд┐рд╡рд╕ 24 рд▓рд╛ releases freeze рдХрд░рддреЗ рдЖрдгрд┐ рджрд┐рд╡рд╕ 34 рд▓рд╛ рддреЛ рдЙрдард╡рддреЗ тАФ рдХреЛрдгреА рд╡рд╛рдж рдШрд╛рддрд▓рд╛ рдореНрд╣рдгреВрди рдирд╛рд╣реА, рддрд░ рджрд┐рд╡рд╕ 6 рдЪрд╛ incident 28 рджрд┐рд╡рд╕рд╛рдВрдЪреНрдпрд╛ window рдордзреВрди рдмрд╛рд╣реЗрд░ рдкрдбрд▓рд╛ рдореНрд╣рдгреВрди. 99.95% рд╡рд░ service рджрд┐рд╡рд╕ 15 рд▓рд╛ freeze рд╣реЛрддреЗ рдЖрдгрд┐ рджрд┐рд╡рд╕ 45 рд▓рд╛ рдЕрдЬреВрдирд╣реА freeze рдЕрд╕рддреЗ тАФ рдкреНрд░рддреНрдпреЗрдХ incident рдЪреЗ P0 postmortem рд╣реЛрддреЗ. 99% рд╡рд░ policy рдХрдзреАрдЪ рдХрд╛рд╣реАрдЪ рдХрд░рдд рдирд╛рд╣реА. рдореНрд╣рдгреВрди SLO рд╣рд╛ рджрд╛рдд рдЕрд╕рд▓реЗрд▓рд╛ рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рдирд┐рд░реНрдгрдп рдЖрд╣реЗ: users рдирд╛ рдХрд╛рдп рд╣рд╡реЗ рддреНрдпрд╛рд╡рд░реВрди рддреЛ рдирд┐рд╡рдбрд╛, рдЖрдгрд┐ рдордЧ policy рд▓рд╛ рдХреГрддреА рдХрд░реВ рджреНрдпрд╛.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

Policy SLO рд╢реЗрдЬрд╛рд░реАрдЪ, service repo рдордзреНрдпреЗ рдареЗрд╡рд╛, рдирд╛рд╡рд╛рдирд┐рд╢реА рдард░рд▓реЗрд▓реНрдпрд╛ рд▓реЛрдХрд╛рдВрдЪреНрдпрд╛ рд╕рд╣реАрд╕рд╣. рдПрдХ рдЫреЛрдЯреЗ рдЙрджрд╛рд╣рд░рдг (рд╕рд╛рдзреА YAML, рдЬреА рдорд╛рдгрд╕реЗ рдЖрдгрд┐ release gate рджреЛрдШреЗрд╣реА рд╡рд╛рдЪрддрд╛рдд):

service: timetable
slo: {sli: "successful requests / all requests", target: 99.9, window: 28d}
policy:
  - when: "budget_used < 75%"
    do: "release normally"
  - when: "75% <= budget_used < 100%"
    do: "reliability items first; second reviewer on every release"
  - when: "budget_used >= 100%"
    do: "freeze: only P0 fixes and security patches until back inside the SLO"
  - when: "one incident > 20% of the budget"
    do: "postmortem with at least one P0 action item"
escalation: "disagreements go to the head of engineering"
signed: [product: aishwarya, dev: timetable-team, sre: katrina]

рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ CI рдордзреАрд▓ release gate deploy рдЪреНрдпрд╛ рдЖрдзреА Prometheus рдордзреВрди budget рд╡рд╛рдЪреВ рд╢рдХрддреЗ. рдЗрдерд▓реЗ recording rule рдЪреЗ рдирд╛рд╡ рддреБрдореНрд╣реА рд╕реНрд╡рддрдГ рдард░рд╡рд╛рдпрдЪреЗ рдЖрд╣реЗ (Observability school рддреЗ рддрдпрд╛рд░ рдХрд░рддреЗ):

used=$(curl -s "http://prometheus:9090/api/v1/query" \
  --data-urlencode 'query=slo:timetable:budget_used_ratio:28d' | jq -r '.data.result[0].value[1]')
awk -v u="$used" 'BEGIN { exit (u >= 1.0) }' || { echo "error budget spent тАФ release frozen"; exit 1; }

рддреБрдордЪреНрдпрд╛рд╕рд╛рдареА SLOs рдЖрдгрд┐ budgets рдЪрд╛ рдорд╛рдЧреЛрд╡рд╛ рдШреЗрдгрд╛рд▒реНрдпрд╛ tools рдордзреНрдпреЗ Sloth рдЖрдгрд┐ Pyrra (open source, Prometheus rules рддрдпрд╛рд░ рдХрд░рддрд╛рдд), Google Cloud рдЖрдгрд┐ Datadog рдЪреА SLO features, рдЖрдгрд┐ Nobl9 рдпрд╛рдВрдЪрд╛ рд╕рдорд╛рд╡реЗрд╢ рдЖрд╣реЗ.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ: рддреБрдордЪреНрдпрд╛ рд╕рд░реНрд╡рд╛рдд рдорд╣рддреНрддреНрд╡рд╛рдЪреНрдпрд╛ service рдЪрд╛ SLO рд╢реЛрдзрд╛. рдордЧ рддреНрдпрд╛рдЪреЗ budget рд╕рдВрдкрд▓реНрдпрд╛рд╡рд░ рдХрд╛рдп рд╣реЛрддреЗ рд╣реЗ рд╕рд╛рдВрдЧрдгрд╛рд░реЗ рдкрд╛рди рд╢реЛрдзрд╛. рдЕрд╕реЗ рдкрд╛рди рдирд╕реЗрд▓, рддрд░ рддреЛ SLO рдлрдХреНрдд рд╕рдЬрд╛рд╡рдЯ рдЖрд╣реЗ тАФ policy рд▓рд┐рд╣рд╛ рдЖрдгрд┐ рдпрд╛ рдорд╣рд┐рдиреНрдпрд╛рддрдЪ рддрд┐рдЪреНрдпрд╛рд╡рд░ рд╕рд╣реА рдШреНрдпрд╛.

тПня╕П рдкреБрдвреЗ

Policy рдореНрд╣рдгрддреЗ "рдЖрдзреА рдЧреЛрд╖реНрдЯреА рджреБрд░реБрд╕реНрдд рдХрд░рд╛". рдкрдг рдкрдердХ рдмрд╛рджрд▓реНрдпрд╛ рд╡рд╛рд╣рдгреНрдпрд╛рдд рд╡реНрдпрд╕реНрдд рдЖрд╣реЗ. рдкреБрдврдЪрд╛ рдзрдбрд╛ рддреНрдпрд╛ рдмрд╛рджрд▓реНрдпрд╛ рдореЛрдЬрддреЛ.

git checkout lesson-03-toil

ЁЯУЬ Lesson 02 тАФ SLOs as an agreement: the error budget POLICY

ЁЯУН You are here: Lesson 02 of 12 ┬╖ Previous: lesson-01-what-is-sre ┬╖ Next: lesson-03-toil


ЁЯУж What's in this branch

Lesson 01, plus the agreement that decides how fast the school may change its buildings. The Observability school teaches SLIs, SLOs and the budget arithmetic. This lesson is about the policy: a document, signed before anything breaks, that says what product, developers and the crew will do as the budget is spent. BudgetPolicy in sre/crew.py and budget() in sre/demo.py.

ЁЯзТ Explain like I'm 5

The crew and the head teacher make a deal at the start of term. тЬНя╕П

"The timetable building may be broken for a little bit тАФ 1 request in 1,000. That little bit is the repair budget. While there is budget left, the teachers can change things as fast as they like: new rooms, new timetables, new features."

"If the budget gets low, we slow down and fix things first."

"If the budget is all used up, we stop changing things тАФ only urgent fixes тАФ until the building is healthy again."

The important part: they wrote this down and signed it before the leaks. So when the budget runs out on day 24, nobody argues. The paper already says what happens.

ЁЯЧ║я╕П Diagram

flowchart LR
    n["ЁЯЯв normal<br/>under 75% used<br/>release freely"] -->|"day 20: 83.9%"| c["ЁЯЯб caution<br/>75тАУ100%<br/>reliability first"]
    c -->|"day 24: 101.8%"| f["ЁЯФ┤ freeze<br/>100% or more<br/>P0 + security only"]
    f -->|"day 34: day-6 incident<br/>leaves the window тЖТ 80.4%"| c
    c -->|"day 43: 57.1%"| n
    big["ЁЯТе one incident > 20%<br/>тЖТ P0 action in its postmortem"]

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/sre/lesson-diagrams.html#l02

тЭУ What

ЁЯдФ Why

Because "reliability vs features" is otherwise decided by opinion and seniority, again and again, usually in the middle of an outage. A budget turns it into data, and a policy turns the data into action. It also protects speed: while budget is left, nobody may block a release "just to be safe". 100% is the wrong target: the SRE book's example is a user whose own phone and network work 99% of the time тАФ they cannot tell a 99.99% service from a 99.999% one, and every extra nine costs more (lesson 09).

ЁЯФз How (in this repo)

BudgetPolicy(slo, window_days, daily_requests, slow=0.75, single=0.20) in sre/crew.py computes the budget. run(days, baseline, incidents) adds baseline failures every day plus the incident on its day, measures the spend over the rolling window each day, and returns every state change and every incident above the single-incident line. budget() runs 45 days with four incidents (days 6, 15, 20, 24).

ЁЯзк Try it

python3 sre/demo.py budget
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from crew import BudgetPolicy
for slo in (0.999, 0.9995, 0.99):
    pol = BudgetPolicy(slo=slo)
    changes, big = pol.run(45, baseline=250, incidents={6: 7000, 15: 6500, 20: 5000, 24: 4000})
    print(f"SLO {slo:.2%} тЖТ budget {pol.budget:>7,} ┬╖ states: " + " тЖТ ".join(f"{s}@{d}" for d, _, s in changes) + f" ┬╖ P0 postmortems: {len(big)}")
EOF

тЬЕ Verify тАФ what you should see

budget prints:

тФАтФА timetable service ┬╖ SLO 99.9% over a rolling 28 days ┬╖ 1,000,000 requests a day тЖТ budget 28,000 failed requests
   day  1:   0.9% of the budget used тЖТ normal
   day 20:  83.9% of the budget used тЖТ caution
   day 24: 101.8% of the budget used тЖТ freeze
   day 34:  80.4% of the budget used тЖТ caution
   day 43:  57.1% of the budget used тЖТ normal
   day  6 incident burned 7,000 = 25.0% (> 20%) тЖТ its postmortem must carry a P0 action item
   day 15 incident burned 6,500 = 23.2% (> 20%) тЖТ its postmortem must carry a P0 action item

Your snippet prints:

SLO 99.90% тЖТ budget  28,000 ┬╖ states: normal@1 тЖТ caution@20 тЖТ freeze@24 тЖТ caution@34 тЖТ normal@43 ┬╖ P0 postmortems: 2
SLO 99.95% тЖТ budget  14,000 ┬╖ states: normal@1 тЖТ caution@14 тЖТ freeze@15 ┬╖ P0 postmortems: 4
SLO 99.00% тЖТ budget 280,000 ┬╖ states: normal@1 ┬╖ P0 postmortems: 0

ЁЯПБ What you just proved

The same four incidents give three very different quarters. At 99.9%, the policy freezes releases on day 24 and lifts it on day 34 тАФ not because anyone argued, but because the day-6 incident left the 28-day window. At 99.95%, the service freezes on day 15 and is still frozen on day 45 тАФ every incident is a P0 postmortem. At 99%, the policy never does anything. So the SLO is a business decision with teeth: pick it from what users need, then let the policy act.

тЪая╕П Common mistakes

ЁЯПн In production

Keep the policy next to the SLO, in the service repo, signed by named people. A short example (plain YAML, read by humans and by a release gate):

service: timetable
slo: {sli: "successful requests / all requests", target: 99.9, window: 28d}
policy:
  - when: "budget_used < 75%"
    do: "release normally"
  - when: "75% <= budget_used < 100%"
    do: "reliability items first; second reviewer on every release"
  - when: "budget_used >= 100%"
    do: "freeze: only P0 fixes and security patches until back inside the SLO"
  - when: "one incident > 20% of the budget"
    do: "postmortem with at least one P0 action item"
escalation: "disagreements go to the head of engineering"
signed: [product: aishwarya, dev: timetable-team, sre: katrina]

On a real account тАФ a CI release gate can read the budget from Prometheus before a deploy. The recording rule name here is yours to define (the Observability school builds it):

used=$(curl -s "http://prometheus:9090/api/v1/query" \
  --data-urlencode 'query=slo:timetable:budget_used_ratio:28d' | jq -r '.data.result[0].value[1]')
awk -v u="$used" 'BEGIN { exit (u >= 1.0) }' || { echo "error budget spent тАФ release frozen"; exit 1; }

Tools that track SLOs and budgets for you include Sloth and Pyrra (open source, generate Prometheus rules), Google Cloud's and Datadog's SLO features, and Nobl9.

ЁЯПн Why this matters in production: find your most important service's SLO. Then find the page that says what happens when its budget is spent. If there is no such page, the SLO is a decoration тАФ write the policy and get it signed this month.

тПня╕П Next

The policy says "fix things first". But the crew is busy carrying buckets. The next lesson measures those buckets.

git checkout lesson-03-toil
тЖР Previouswhat is sreNext тЖТtoil

This page is the lesson's README from the lesson-02-error-budget-policy branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.