ЁЯПл The SchoolтА║ЁЯЫая╕П SREтА║ЁЯЪж рдзрдбрд╛ 10 тАФ Overload: priority рдиреБрд╕рд╛рд░ shed рдХрд░рд╛, рдиреАрдЯ degrade рдХрд░рд╛, retries рдирд╛ budget рджреНрдпрд╛
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯЪж рдзрдбрд╛ 10 тАФ Overload: priority рдиреБрд╕рд╛рд░ shed рдХрд░рд╛, рдиреАрдЯ degrade рдХрд░рд╛, retries рдирд╛ budget рджреНрдпрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 10 ┬╖ рдорд╛рдЧреЗ: lesson-09-reliability-math ┬╖ рдкреБрдвреЗ: lesson-11-chaos-engineering


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

рдзрдбрд╛ 09, рдЕрдзрд┐рдХ рдЗрдорд╛рд░рдд рдХрд░реВ рд╢рдХреЗрд▓ рддреНрдпрд╛рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдХрд╛рдо рдЖрд▓реНрдпрд╛рд╡рд░ рдХрд╛рдп рдХрд░рд╛рдпрдЪреЗ рддреЗ. рд╕рдЧрд│реЗ рдХрд╛рдо рд╕рд╛рд░рдЦреЗрдЪ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдирд╕рддреЗ: рд╕рд░реНрд╡рд╛рдд рдХрдореА рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрдзреА shed рдХрд░рд╛, рдкреНрд░рддреНрдпреЗрдХ request рд╕реНрд╡рд╕реНрдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА features degrade рдХрд░рд╛, рдЖрдгрд┐ retry budget рдиреЗ clients рдирд╛ overload рдЪреЗ рд╡рд╛рджрд│ рдмрдирд╡рдгреНрдпрд╛рдкрд╛рд╕реВрди рдерд╛рдВрдмрд╡рд╛. (рдорд░реНрдпрд╛рджрд┐рдд queues рдЖрдгрд┐ backpressure Distributed Systems school, рдзрдбрд╛ 12 рдордзреНрдпреЗ рдЖрд╣реЗрдд; backoff рдЖрдгрд┐ circuit breakers Scaling school, рдзрдбрд╛ 13 рдордзреНрдпреЗ.) sre/design.py рдордзреАрд▓ Shedder рдЖрдгрд┐ retry_load рдЖрдгрд┐ sre/demo.py рдордзреАрд▓ overload().

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

рд╢рд╛рд│реЗрдЪреЗ рдЙрдкрд╛рд╣рд╛рд░рдЧреГрд╣ рдорд┐рдирд┐рдЯрд╛рд▓рд╛ 1,000 рддрд╛рдЯреЗ рд╡рд╛рдвреВ рд╢рдХрддреЗ. рдЖрдЬ рд░рд╛рдВрдЧреЗрдд 1,500 рдЬрдг рдЖрд╣реЗрдд. ЁЯН╜я╕П

рдХреЛрдгрддрд╛рд╣реА plan рдирд╛рд╣реА: рд╕рдЧрд│реЗ рд╕рд╛рд░рдЦреЗрдЪ рдерд╛рдВрдмрддрд╛рдд. рддреАрдирдкреИрдХреА рдлрдХреНрдд рджреЛрдШрд╛рдВрдирд╛ рдЬреЗрд╡рдг рдорд┐рд│рддреЗ тАФ рдЖрдгрд┐ рджрд╣рд╛ рдорд┐рдирд┐рдЯрд╛рдВрдд рдкрд░реАрдХреНрд╖рд╛ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреАрдВрдирд╛ (critical) рдЙрдкрд╛рд╢реА рд░рд╛рд╣рдгреНрдпрд╛рдЪреА рд╢рдХреНрдпрддрд╛, рджреБрд╕рд▒реНрдпрд╛рдВрджрд╛ рдЧреЛрдб рдЦрд╛рдпрд▓рд╛ рдЖрд▓реЗрд▓реНрдпрд╛ рдкрд╛рд╣реБрдгреНрдпрд╛рдВрдЗрддрдХреАрдЪ рдЕрд╕рддреЗ.

рдкрд╣рд┐рд▓рд╛ plan тАФ priority: рдЖрдзреА рдкрд░реАрдХреНрд╖реЗрдЪреНрдпрд╛ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреА, рдордЧ рдиреЗрд╣рдореАрдЪреА рдЬреЗрд╡рдгрд╛рдЪреА рд░рд╛рдВрдЧ, рдордЧ рджреБрд╕рд▒реНрдпрд╛рдВрджрд╛ рдЧреЛрдб. рдкрд░реАрдХреНрд╖реЗрдЪреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреАрд▓рд╛ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЬреЗрд╡рдгрд╛рд▓рд╛ рд╡рд╛рдврд▓реЗ рдЬрд╛рддреЗ; рджреБрд╕рд▒реНрдпрд╛рдВрджрд╛ рдЧреЛрдбрд╛рдЪреНрдпрд╛ 600 рдкреИрдХреА рдлрдХреНрдд 100.

рджреБрд╕рд░рд╛ plan тАФ рдкреНрд░рддреНрдпреЗрдХ рддрд╛рдЯ рд╕реНрд╡рд╕реНрдд рдХрд░рд╛: рдиреЗрд╣рдореАрдЪреНрдпрд╛ рд░рд╛рдВрдЧреЗрд╕рд╛рдареА рдЖрдгрд┐ рдЧреЛрдбрд╛рд╕рд╛рдареА рд╕рдЬрд╛рд╡рдЯ рд╡рдЧрд│рд╛. рдкреНрд░рддреНрдпреЗрдХ рддрд╛рдЯрд╛рд▓рд╛ рдХрдореА рд╡реЗрд│ рд▓рд╛рдЧрддреЛ, рдореНрд╣рдгреВрди 100 рдРрд╡рдЬреА 357 рдЧреЛрдб рд╡рд╛рдврд▓реЗ рдЬрд╛рддрд╛рдд.

рдЖрдгрд┐ retry рдЪрд╛ рдирд┐рдпрдо: рдкрд░рдд рдкрд╛рдард╡рд▓реЗрд▓реНрдпрд╛ рд╕рдЧрд│реНрдпрд╛рдВрдиреА рдкреБрдиреНрд╣рд╛ рдпреЗрдКрди рдкреНрд░рддреНрдпреЗрдХреА рддреАрдирджрд╛ рдШреБрд╕реВ рдирдпреЗ тАФ рддреНрдпрд╛рдореБрд│реЗ рд░рд╛рдВрдЧ рджреБрдкреНрдкрдЯ рд▓рд╛рдВрдм рд╣реЛрддреЗ. рдлрдХреНрдд рдереЛрдбреНрдпрд╛рдВрдирд╛рдЪ рдкреБрдиреНрд╣рд╛ рдкреНрд░рдпрддреНрди рдХрд░рддрд╛ рдпреЗрддреЛ.

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

flowchart LR
    in["ЁЯУе 1,500 req/s offered<br/>capacity 1,000"] --> f["no priorities<br/>critical 267 of 400 тЬЧ"]
    in --> p["shed by priority<br/>critical 400 ┬╖ normal 500 ┬╖ sheddable 100"]
    p --> g["+ degrade (cost 0.7)<br/>sheddable 357"]
    in --> r["3 retries each<br/>3,000 attempts/s"]
    in --> b["10% retry budget<br/>1,650 attempts/s"]

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг overload рд╣реАрдЪ рддреА рд╡реЗрд│ рдЬреЗрд╡реНрд╣рд╛ рдЫреЛрдЯреА рдЕрдбрдЪрдг рд╕рдВрдкреВрд░реНрдг outage рдмрдирддреЗ: рд╣рд│реВ dependency (рдзрдбрд╛ 09) capacity рдХрдореА рдХрд░рддреЗ, queues рд╡рд╛рдврддрд╛рдд, clients retry рдХрд░рддрд╛рдд, load рджреБрдкреНрдкрдЯ рд╣реЛрддреЛ, рдЖрдгрд┐ users рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧ рдПрдХрддреНрд░ рдЕрдпрд╢рд╕реНрд╡реА рд╣реЛрддреЛ. Priority рдиреБрд╕рд╛рд░ shedding рд╕рд░реНрд╡рд╛рдд рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдХрд╛рдо рдкреВрд░реНрдг рдареЗрд╡рддреЗ, degradation capacity рддрд╛рдгрддреЗ, рдЖрдгрд┐ retry budget clients рдирд╛ рдкрд░рд┐рд╕реНрдерд┐рддреА рдЖрдгрдЦреА рдмрд┐рдШрдбрд╡рдгреНрдпрд╛рдкрд╛рд╕реВрди рдерд╛рдВрдмрд╡рддреЗ.

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

sre/design.py рдордзреАрд▓ Shedder(capacity) рдЖрд▓реЗрд▓рд╛ load priority рдЪреНрдпрд╛ рдХреНрд░рдорд╛рдиреЗ (class, requests/s, cost per request) рдЕрд╕рд╛ рдШреЗрддреЗ. fifo() рдкреНрд░рддреНрдпреЗрдХ class рд▓рд╛ рддреЛрдЪ рд╡рд╛рдЯрд╛ рджреЗрддреЗ; by_priority() capacity рд╡рд░рдЪреНрдпрд╛ class рдкрд╛рд╕реВрди рдЦрд╛рд▓реА рднрд░рддреЗ. retry_load(offered, capacity, max_retries, budget) рд╕реЗрдХрдВрджрд╛рд▓рд╛ рдХрд┐рддреА рдкреНрд░рдпрддреНрди рд╣реЛрддрд╛рдд рддреЗ рдкрд░рдд рджреЗрддреЗ, рдЬреЗрд╡реНрд╣рд╛ рдирд╛рдХрд╛рд░рд▓реЗрд▓реА рдкреНрд░рддреНрдпреЗрдХ request retry рд╣реЛрддреЗ рдЖрдгрд┐ overload рдЯрд┐рдХреЗрдкрд░реНрдпрдВрдд рдкреНрд░рддреНрдпреЗрдХ retry рд╕реБрджреНрдзрд╛ рдирд╛рдХрд╛рд░рд▓рд╛ рдЬрд╛рддреЛ (рд╕рд░реНрд╡рд╛рдд рд╡рд╛рдИрдЯ рд╕реНрдерд┐рддреА).

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

python3 sre/demo.py overload
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from design import Shedder, retry_load
s = Shedder(1000)
for crit in (400, 900, 1200):
    print(f"critical {crit:>4}/s тЖТ", s.by_priority([("critical", crit, 1.0), ("normal", 500, 1.0), ("sheddable", 600, 1.0)]))
for offered in (1000, 1200, 2000):
    print(f"offered {offered}/s тЖТ attempts with 3 retries {retry_load(offered, 1000, 3):>5,.0f} ┬╖ with a 10% budget {retry_load(offered, 1000, 3, 0.1):>5,.0f}")
EOF

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

overload рд╣реЗ рдЫрд╛рдкрддреЗ:

   no priorities (everyone gets 1000/1500): {'critical': 267, 'normal': 333, 'sheddable': 400}
   shed by priority:                        {'critical': 400, 'normal': 500, 'sheddable': 100}
   + graceful degradation (normal and sheddable skip photo thumbnails: cost 0.7 each): {'critical': 400, 'normal': 500, 'sheddable': 357}
   3 retries per request           тЖТ 3,000 attempts/s arrive at a server that can do 1,000
   3 retries + a 10% retry budget  тЖТ 1,650 attempts/s arrive at a server that can do 1,000

рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:

critical  400/s тЖТ {'critical': 400, 'normal': 500, 'sheddable': 100}
critical  900/s тЖТ {'critical': 900, 'normal': 100, 'sheddable': 0}
critical 1200/s тЖТ {'critical': 1000, 'normal': 0, 'sheddable': 0}
offered 1000/s тЖТ attempts with 3 retries 1,000 ┬╖ with a 10% budget 1,000
offered 1200/s тЖТ attempts with 3 retries 1,800 ┬╖ with a 10% budget 1,320
offered 2000/s тЖТ attempts with 3 retries 5,000 ┬╖ with a 10% budget 2,200

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

Priorities рдирд╕рддрд╛рдирд╛ рдПрдХ рддреГрддреАрдпрд╛рдВрд╢ critical requests рдЕрдпрд╢рд╕реНрд╡реА рдЭрд╛рд▓реНрдпрд╛ (400 рдкреИрдХреА 267 рдЪ рдорд┐рд│рд╛рд▓реНрдпрд╛), рддрд░ 400 рджреБрд╕рд▒реНрдпрд╛рдВрджрд╛-рдЧреЛрдбрд╛рдЪреНрдпрд╛ requests рдирд╛ рд╕реЗрд╡рд╛ рдорд┐рд│рд╛рд▓реА. Priority рдиреБрд╕рд╛рд░ shedding рдиреЗ рдкреНрд░рддреНрдпреЗрдХ critical рдЖрдгрд┐ normal request рдЯрд┐рдХрд╡рд▓реА, рдЖрдгрд┐ degradation рдиреЗ рддреНрдпрд╛рдЪ capacity рд╡рд░ sheddable class 100 рд╡рд░реВрди 357 рд╡рд░ рдиреЗрд▓рд╛. рдкрдг рдлрдХреНрдд critical traffic рдЪ 1,200/s рдЕрд╕реЗрд▓, рддрд░ shedding рдорджрдд рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА тАФ рддреА capacity рдЪреА рдЕрдбрдЪрдг рдЖрд╣реЗ (рдзрдбрд╛ 07). рдЖрдгрд┐ retries: 2├Ч overload рд╡рд░ рдкреНрд░рддреНрдпреЗрдХреА 3 retries рдореБрд│реЗ рд╕реЗрдХрдВрджрд╛рд▓рд╛ 2,000 рдЪреЗ 5,000 рдкреНрд░рдпрддреНрди рд╣реЛрддрд╛рдд; 10% budget рддреЗ 2,200 рд╡рд░ рд░реЛрдЦрддреЗ.

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

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

Service config рдордзреАрд▓ gRPC retry throttling: рдкреНрд░рддреНрдпреЗрдХ рдЕрдкрдпрд╢ рдПрдХ token рдЦрд░реНрдЪ рдХрд░рддреЗ, рдкреНрд░рддреНрдпреЗрдХ рдпрд╢ tokenRatio рдкрд░рдд рдорд┐рд│рд╡рддреЗ; tokens рдЕрд░реНрдзреНрдпрд╛рдЦрд╛рд▓реА рдЧреЗрд▓реЗ рдХреА retries рдерд╛рдВрдмрддрд╛рдд. рдЦрд▒реНрдпрд╛ account рд╡рд░:

{
  "methodConfig": [{
    "name": [{"service": "timetable.Timetable"}],
    "retryPolicy": {"maxAttempts": 3, "initialBackoff": "0.1s", "maxBackoff": "1s",
                    "backoffMultiplier": 2, "retryableStatusCodes": ["UNAVAILABLE"]}
  }],
  "retryThrottling": {"maxTokens": 10, "tokenRatio": 0.1}
}

Envoy retry budget тАФ рдЪрд╛рд▓реВ retries cluster рдХрдбреЗ рдЬрд╛рдгрд╛рд▒реНрдпрд╛ рдЪрд╛рд▓реВ requests рдЪреНрдпрд╛ рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд рдПрдХрд╛ рдЯрдХреНрдХреЗрд╡рд╛рд░реАрдЗрддрдХреНрдпрд╛рдЪ рдЕрд╕реВ рд╢рдХрддрд╛рдд:

clusters:
  - name: timetable
    circuit_breakers:
      thresholds:
        - priority: DEFAULT
          retry_budget:
            budget_percent: {value: 10.0}
            min_retry_concurrency: 3

Kubernetes рд╡рд░ API server рд╕реНрд╡рддрдГ API Priority and Fairness рдиреЗ priority рдиреБрд╕рд╛рд░ shed рдХрд░рддреЛ (FlowSchema рдЖрдгрд┐ PriorityLevelConfiguration objects) рдЖрдгрд┐ рдПрдЦрд╛рджреНрдпрд╛ level рдЪреНрдпрд╛ queues рднрд░рд▓реНрдпрд╛ рдХреА 429 рдЙрддреНрддрд░ рджреЗрддреЛ.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ: рддреБрдордЪреНрдпрд╛ рдкрд╣рд┐рд▓реНрдпрд╛ рдкрд╛рдЪ request рдкреНрд░рдХрд╛рд░рд╛рдВрдирд╛ critical рдХрд┐рдВрд╡рд╛ sheddable рдЕрд╕реЗ рд▓реЗрдмрд▓ рд▓рд╛рд╡рд╛, рдЖрдгрд┐ retry рд╣реЛрдгрд╛рд░реА рдкреНрд░рддреНрдпреЗрдХ рдЬрд╛рдЧрд╛ рд╢реЛрдзрд╛. рдордЧ 1.5 ├Ч capacity рд╡рд░ load-test рдХрд░рд╛ рдЖрдгрд┐ рдХреЛрдгрддрд╛ class рдЖрдзреА рдЕрдпрд╢рд╕реНрд╡реА рд╣реЛрддреЛ рддреЗ рддрдкрд╛рд╕рд╛. рддреЗ logins рдЕрд╕рддреАрд▓, рддрд░ рдЦрд░рд╛ overload рдпреЗрдгреНрдпрд╛рдЖрдзреА рдХреНрд░рдо рджреБрд░реБрд╕реНрдд рдХрд░рд╛.

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

рдЕрдкрдпрд╢рд╛рд╕рд╛рдареАрдЪреЗ designs рддреБрдордЪреНрдпрд╛рдХрдбреЗ рдХрд╛рдЧрджрд╛рд╡рд░ рдЖрд╣реЗрдд. рддреЗ рдЪрд╛рд▓рддрд╛рдд рдХрд╛? рд╣реЗ рд╢реЛрдзрдгреНрдпрд╛рд╕рд╛рдареА рдкреБрдврдЪрд╛ рдзрдбрд╛ рдХрд╛рд│рдЬреАрдкреВрд░реНрд╡рдХ, рдореБрджреНрджрд╛рдо рдЧреЛрд╖реНрдЯреА рдмрд┐рдШрдбрд╡рддреЛ.

git checkout lesson-11-chaos-engineering

ЁЯЪж Lesson 10 тАФ Overload: shed by priority, degrade gracefully, budget the retries

ЁЯУН You are here: Lesson 10 of 12 ┬╖ Previous: lesson-09-reliability-math ┬╖ Next: lesson-11-chaos-engineering


ЁЯУж What's in this branch

Lesson 09, plus what to do when more work arrives than the building can do. Not everything is equally important: shed the least important first, degrade features to make each request cheaper, and stop clients from turning an overload into a storm with a retry budget. (Bounded queues and backpressure are in the Distributed Systems school, lesson 12; backoff and circuit breakers in the Scaling school, lesson 13.) Shedder and retry_load in sre/design.py and overload() in sre/demo.py.

ЁЯзТ Explain like I'm 5

The school canteen can serve 1,000 plates a minute. Today 1,500 people are in line. ЁЯН╜я╕П

No plan: everybody waits the same. Only two in three get fed тАФ and the pupils who have an exam in ten minutes (the critical ones) are just as likely to go hungry as the visitors who came for a second dessert.

Plan one тАФ priority: exam pupils first, then the normal lunch line, then second desserts. Every exam pupil and every normal lunch is served; only 100 of 600 second desserts.

Plan two тАФ make each plate cheaper: for the normal line and the desserts, skip the fancy garnish. Each plate takes less time, so 357 desserts get served instead of 100.

And the retry rule: people turned away must not all come back and push in again three times each тАФ that makes the line twice as long. Only a few may try again.

ЁЯЧ║я╕П Diagram

flowchart LR
    in["ЁЯУе 1,500 req/s offered<br/>capacity 1,000"] --> f["no priorities<br/>critical 267 of 400 тЬЧ"]
    in --> p["shed by priority<br/>critical 400 ┬╖ normal 500 ┬╖ sheddable 100"]
    p --> g["+ degrade (cost 0.7)<br/>sheddable 357"]
    in --> r["3 retries each<br/>3,000 attempts/s"]
    in --> b["10% retry budget<br/>1,650 attempts/s"]

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

тЭУ What

ЁЯдФ Why

Because overload is when a small problem becomes a total outage: a slow dependency (lesson 09) cuts capacity, queues grow, clients retry, the load doubles, and every class of user fails together. Priority shedding keeps the most important work whole, degradation stretches capacity, and a retry budget stops clients from making it worse.

ЁЯФз How (in this repo)

Shedder(capacity) in sre/design.py takes the offered load as (class, requests/s, cost per request) in priority order. fifo() serves every class the same fraction; by_priority() fills capacity from the top class down. retry_load(offered, capacity, max_retries, budget) returns attempts per second when every rejected request is retried and, while the overload lasts, every retry is rejected too (a worst case).

ЁЯзк Try it

python3 sre/demo.py overload
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from design import Shedder, retry_load
s = Shedder(1000)
for crit in (400, 900, 1200):
    print(f"critical {crit:>4}/s тЖТ", s.by_priority([("critical", crit, 1.0), ("normal", 500, 1.0), ("sheddable", 600, 1.0)]))
for offered in (1000, 1200, 2000):
    print(f"offered {offered}/s тЖТ attempts with 3 retries {retry_load(offered, 1000, 3):>5,.0f} ┬╖ with a 10% budget {retry_load(offered, 1000, 3, 0.1):>5,.0f}")
EOF

тЬЕ Verify тАФ what you should see

overload prints:

   no priorities (everyone gets 1000/1500): {'critical': 267, 'normal': 333, 'sheddable': 400}
   shed by priority:                        {'critical': 400, 'normal': 500, 'sheddable': 100}
   + graceful degradation (normal and sheddable skip photo thumbnails: cost 0.7 each): {'critical': 400, 'normal': 500, 'sheddable': 357}
   3 retries per request           тЖТ 3,000 attempts/s arrive at a server that can do 1,000
   3 retries + a 10% retry budget  тЖТ 1,650 attempts/s arrive at a server that can do 1,000

Your snippet prints:

critical  400/s тЖТ {'critical': 400, 'normal': 500, 'sheddable': 100}
critical  900/s тЖТ {'critical': 900, 'normal': 100, 'sheddable': 0}
critical 1200/s тЖТ {'critical': 1000, 'normal': 0, 'sheddable': 0}
offered 1000/s тЖТ attempts with 3 retries 1,000 ┬╖ with a 10% budget 1,000
offered 1200/s тЖТ attempts with 3 retries 1,800 ┬╖ with a 10% budget 1,320
offered 2000/s тЖТ attempts with 3 retries 5,000 ┬╖ with a 10% budget 2,200

ЁЯПБ What you just proved

With no priorities, a third of the critical requests failed (267 of 400) while 400 second-dessert requests were served. Shedding by priority kept every critical and normal request, and degradation raised the sheddable class from 100 to 357 on the same capacity. But when critical traffic alone is 1,200/s, shedding cannot help тАФ that is a capacity problem (lesson 07). And retries: at 2├Ч overload, 3 retries each turn 2,000 into 5,000 attempts a second; a 10% budget holds it at 2,200.

тЪая╕П Common mistakes

ЁЯПн In production

gRPC retry throttling in the service config: each failure spends a token, each success earns back tokenRatio; below half the tokens, retries stop. On a real account:

{
  "methodConfig": [{
    "name": [{"service": "timetable.Timetable"}],
    "retryPolicy": {"maxAttempts": 3, "initialBackoff": "0.1s", "maxBackoff": "1s",
                    "backoffMultiplier": 2, "retryableStatusCodes": ["UNAVAILABLE"]}
  }],
  "retryThrottling": {"maxTokens": 10, "tokenRatio": 0.1}
}

Envoy retry budget тАФ active retries may be at most a percentage of active requests to the cluster:

clusters:
  - name: timetable
    circuit_breakers:
      thresholds:
        - priority: DEFAULT
          retry_budget:
            budget_percent: {value: 10.0}
            min_retry_concurrency: 3

On Kubernetes, the API server itself sheds by priority with API Priority and Fairness (FlowSchema and PriorityLevelConfiguration objects) and answers 429 when a level's queues are full.

ЁЯПн Why this matters in production: label your top five request types critical or sheddable, and find every place that retries. Then load-test at 1.5 ├Ч capacity and check which class fails first. If it is logins, fix the order before the real overload does.

тПня╕П Next

You have designs for failure on paper. Do they work? The next lesson breaks things on purpose, carefully, to find out.

git checkout lesson-11-chaos-engineering
тЖР Previousreliability mathNext тЖТchaos engineering

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