Module 6: Evaluation & Production Ops · 5h
01 · UNDERSTAND
Day 32 Theory — Reliability: Retry Budgets, Circuit Breakers, Queues and Backpressure
Why retries are no longer enough
Earlier we learned how an individual HTTP call can retry a transient failure. That is useful at small scale.
Now imagine 1,000 Service Desk requests are active when the model provider starts failing.
If every request immediately retries several times, the system may transform:
1,000 user requests
into:
3,000–5,000 dependency calls
exactly when the dependency is least able to handle them.
This is called failure amplification.
Reliability engineering is therefore not “add more retries.” It is about controlling how the whole system behaves when capacity or dependencies degrade.
Reliability is an end-to-end property
A user request may cross several components:
Client
-> API
-> Agent runtime
-> Model provider
-> Vector store
-> Tool API
-> Database
Each dependency can fail independently.
If every layer has its own aggressive retry policy, the combined behavior may be much worse than any single component intended.
Ask:
Which layer owns the retry, and how much additional load is the system allowed to create?
Retry budgets
A retry budget limits retry amplification.
Instead of treating retries as free, the service gives them bounded capacity.
Possible policies include:
- maximum attempts per operation,
- maximum percentage of traffic that may be retries,
- shared token/budget across a dependency,
- deadline-aware retries that stop when useful time is exhausted.
The exact policy depends on the service. There is no universal retry count.
The mental model is:
original traffic ---------> dependency
|
+-- limited retry budget
|
+-----------> dependency
not:
original failure -> unlimited retry storm
A retry budget answers:
How much extra load are we willing to create in exchange for recovering transient failures?
Retries need time budgets too
Suppose a user-facing request has only a few seconds of useful latency budget remaining.
Starting another long retry after most of that budget is consumed may guarantee a timeout without improving the user experience.
A robust runtime considers:
- caller deadline,
- per-attempt timeout,
- backoff delay,
- remaining useful time.
Retries should stop when success would arrive too late to matter.
Circuit breakers
A circuit breaker prevents a service from repeatedly calling a dependency that appears unhealthy.
The classic mental model has three states:
CLOSED
calls flow normally
|
| failure policy opens circuit
v
OPEN
calls fail fast / use fallback
|
| recovery interval / probe policy
v
HALF-OPEN
allow limited probes
|
+-- healthy --> CLOSED
|
+-- failing --> OPEN
Why fail fast?
If the model provider is clearly unavailable, waiting several seconds on every request may consume:
- worker capacity,
- connection pools,
- memory,
- user latency budget.
Failing quickly can preserve capacity for work that can still succeed.
What a circuit breaker does not do
It does not repair the dependency.
It only changes caller behavior while the dependency is unhealthy.
You still need monitoring, incident response and recovery.
Choosing circuit conditions
Do not copy a magic rule such as “open after five failures.”
Consider signals such as:
- error rate over a meaningful window,
- consecutive failures where appropriate,
- timeout rate,
- latency degradation,
- dependency-specific failure categories.
A policy should avoid opening because of one isolated request while also avoiding long periods of destructive retry traffic.
Queues separate arrival rate from processing rate
Sometimes work does not need to finish inside the original HTTP request.
A queue allows producers to submit work while workers process it at controlled concurrency.
Requests ---> Queue ---> Worker pool ---> Dependency
|
+-- absorbs temporary burst
This can smooth bursts, but a queue does not create infinite capacity.
If work arrives at 1,000 jobs/minute and workers can permanently process only 500 jobs/minute, the backlog grows forever.
That is overload hidden behind storage.
The metrics that make a queue meaningful
Queue depth alone is not enough.
Also watch:
- oldest message age — how long users are actually waiting,
- arrival rate,
- completion rate,
- worker concurrency,
- failure/retry rate,
- dead-letter volume where used.
A queue of 10,000 items may be fine for one workload and catastrophic for another. Age and service objective provide context.
Backpressure
Backpressure means downstream capacity influences how much upstream work is accepted or allowed to proceed.
Without backpressure:
users -> API -> unbounded work -> exhausted workers -> dependency collapse
With backpressure:
users
|
v
admission / limits
|
+-- capacity available -> process
|
+-- capacity exhausted -> queue bounded work / reject / shed
Mechanisms can include:
- bounded queues,
- concurrency semaphores,
- rate limiting,
- admission control,
- load shedding.
The important idea is that the system refuses to promise work it cannot safely carry.
Load shedding is sometimes the reliable choice
Returning a controlled 503 or “try again later” can be healthier than accepting every request and timing all of them out slowly.
A degraded system that serves 80% of requests correctly may be more useful than one that accepts 100% and completes almost none.
The product must decide which work is most important.
Graceful degradation
Dependencies have different criticality.
For the Service Desk:
Knowledge recommendation unavailable
-> maybe answer with a limited fallback
Authorization unavailable
-> do NOT execute privileged action
This illustrates a key rule:
Availability must never override a safety invariant.
A fallback that weakens authorization is not graceful degradation; it is a security bug.
Bulkheads and isolation
Another reliability idea is to isolate capacity so one workload cannot consume everything.
For example:
password-reset workers: bounded pool
knowledge-search workers: bounded pool
security-escalation workers: reserved capacity
If one path becomes overloaded, the entire system does not necessarily collapse with it.
This is similar to watertight compartments in a ship: failure is contained rather than allowed to flood every section.
Retry + circuit breaker + queue + backpressure are complementary
These mechanisms solve different problems.
| Mechanism | Primary purpose |
|---|---|
| Retry | Recover a transient failure |
| Retry budget | Bound retry amplification |
| Circuit breaker | Stop repeatedly calling an unhealthy dependency |
| Queue | Decouple temporary arrival bursts from worker execution |
| Backpressure | Prevent accepting more work than the system can safely carry |
| Load shedding | Preserve useful service during overload |
Using one does not eliminate the need for the others.
Service Desk failure walkthrough
Imagine the certificate API slows dramatically.
A weak design:
request
-> timeout
-> retry
-> retry
-> retry
-> worker remains occupied
-> more users arrive
-> all workers fill
-> entire API becomes slow
A controlled design might be:
request
-> bounded call
-> retry allowed only within budget
-> breaker observes sustained failure
-> circuit opens
-> new certificate requests fail fast / enter approved queue
-> unrelated ticket paths keep working
-> limited probes detect recovery
-> circuit closes
The second design does not guarantee zero failures. It prevents one failure from turning into a system-wide collapse.
What to debug during an incident
Ask in order:
- Which dependency is saturated or failing?
- How much traffic is original versus retry traffic?
- Are callers respecting deadlines?
- Is the circuit state behaving as expected?
- Is queue age growing?
- Is concurrency bounded?
- Are we shedding the correct class of work?
- Are safety-critical paths failing closed?
This is much more useful than simply increasing replica count without understanding the bottleneck.
Service Desk connection
Today our Service Desk learns how to remain useful when parts of the system are unhealthy.
The architectural change is:
Before
------
individual calls with retry logic
After
-----
system-level reliability policy:
retry budgets
+ circuit breakers
+ bounded concurrency/queues
+ backpressure
+ deliberate degradation
The principle is:
Reliable systems bound failure amplification. Retry only within budget, stop hammering unhealthy dependencies, accept only work you can carry, and degrade availability before you degrade correctness or security.
02 · APPLY
Lesson Overview
This is the applied companion for Day 32. Read DAY_32_THEORY.md first for the beginner-first teaching of Reliability: Retry Budgets, Circuit Breakers, Queues and Backpressure. Then use the real service-desk-day-32/ project to trace, run, debug, and explain the concept.
Service Desk Alignment
Day 32 adds Reliability: Retry Budgets, Circuit Breakers, Queues and Backpressure to the running Service Desk. Start with reliability/bulkhead.py, reliability/retry_budget.py, reliability/circuit_breaker.py, reliability/gateway.py, reliability/backoff.py, reliability/deadline.py, then follow imports and tests to identify the actual runtime path.
Why This Topic Matters
The theory chapter explains why Reliability: Retry Budgets, Circuit Breakers, Queues and Backpressure is needed. Here the goal is evidence: identify where the capability is implemented, what observable behavior changes, and how the repository proves both success and failure behavior.
System Architecture
This is a repository surface map, not a claim that modules call each other in the displayed order. The modules are ranked by relevance to today's theory.
graph LR
T[Day 32: Reliability - Retry Budgets, Circuit Breakers, Queues and Backpressure]
T --> M1[reliability/bulkhead.py]
T --> M2[reliability/retry_budget.py]
T --> M3[reliability/circuit_breaker.py]
T --> M4[reliability/gateway.py]
T --> M5[reliability/backoff.py]
T --> M6[reliability/deadline.py]
T --> M7[reliability/fallbacks.py]
T --> M8[security/authz.py]
Follow imports and tests to discover the actual runtime flow.
Repository Implementation Map
Use the real Day 32 repository, not a fabricated sample, to connect theory to implementation.
Theory concepts to locate:
- Why retries are no longer enough
- Reliability is an end-to-end property
- Retry budgets
- Retries need time budgets too
Most relevant implementation modules first:
service_desk/reliability/bulkhead.pyservice_desk/reliability/retry_budget.pyservice_desk/reliability/circuit_breaker.pyservice_desk/reliability/gateway.pyservice_desk/reliability/backoff.pyservice_desk/reliability/deadline.pyservice_desk/reliability/fallbacks.pyservice_desk/security/authz.pyservice_desk/rag/tenant_rag.pyservice_desk/security/context.pyservice_desk/privacy/redactor.pyservice_desk/agent/mock_model.pyservice_desk/tools/ticket_tools.pyservice_desk/agent/resilient_agent.pyservice_desk/privacy/telemetry_scrubber.pyservice_desk/models.py
Follow imports/calls from the relevant module and confirm behavior in tests. Record input → mechanism → observable output/state → failure evidence.
Code Walkthrough & Mechanics
Read reliability/bulkhead.py, reliability/retry_budget.py, reliability/circuit_breaker.py, reliability/gateway.py, reliability/backoff.py, reliability/deadline.py, reliability/fallbacks.py, security/authz.py, rag/tenant_rag.py, security/context.py with these theory sections beside you:
- Why retries are no longer enough — locate its implementation and evidence.
- Reliability is an end-to-end property — locate its implementation and evidence.
- Retry budgets — locate its implementation and evidence.
- Retries need time budgets too — locate its implementation and evidence.
- Circuit breakers — locate its implementation and evidence.
For each concept identify the real function/class/protocol boundary, its input/state, its observable result, and the assertion in tests/test_reliability.py that proves the behavior. If a concept has no implementation or evidence, record that as a gap rather than inventing one.
Common Mistakes & Debugging Guidance
Use the theory—not generic timeout or .env advice—to decide what can fail today.
Failure lens: revisit Retries need time budgets too, Circuit breakers.
- Reproduce the smallest case that violates one of those expectations.
- Trace the real Day 32 modules until you find the first incorrect state/output/decision.
- Use
tests/test_reliability.pyas executable evidence. - Add a regression test if the failure is not already represented.
- Fix the smallest responsible boundary and rerun the relevant test before the full suite.
Your debugging explanation must name the topic-specific invariant that failed, not merely say “an exception occurred.”
Practical Lab Instructions
- Summarize these theory ideas before opening the implementation:
- Why retries are no longer enough
- Reliability is an end-to-end property
- Retry budgets
- Retries need time budgets too
- Inspect the most relevant real Day 32 modules first:
service_desk/reliability/bulkhead.pyservice_desk/reliability/retry_budget.pyservice_desk/reliability/circuit_breaker.pyservice_desk/reliability/gateway.pyservice_desk/reliability/backoff.pyservice_desk/reliability/deadline.pyservice_desk/reliability/fallbacks.pyservice_desk/security/authz.pyservice_desk/rag/tenant_rag.pyservice_desk/security/context.pyservice_desk/privacy/redactor.pyservice_desk/agent/mock_model.py
- Inspect the automated evidence:
tests/test_reliability.py
- Establish the baseline:
cd service-desk-day-32 PYTHONPATH=. pytest tests/test_reliability.py -q - Trace one theory concept through the actual nested modules and tests.
- Run one success case and record input → mechanism → observable result.
- Exercise one topic-specific failure/boundary case and name the invariant that protects the system.
- Re-run the relevant tests and explain theory → implementation → evidence.
Done when: another student can reproduce your trace without relying on an invented sample.
Key Takeaways
- Be able to explain Why retries are no longer enough and point to its implementation/evidence in Day 32.
- Be able to explain Reliability is an end-to-end property and point to its implementation/evidence in Day 32.
- Be able to explain Retry budgets and point to its implementation/evidence in Day 32.
Knowledge Check & Scenario Questions
- Concept: Using Why retries are no longer enough, explain the engineering problem Day 32 is solving without naming a framework as the answer.
- Mechanism: How does Reliability is an end-to-end property appear in the real project? Start from
service_desk/reliability/bulkhead.pyand name the observable state/output/event that changes. - Failure: For Retry budgets, describe one incorrect implementation or boundary condition and the evidence you would expect in
tests/test_reliability.py. - Design review: Which assumption in today's design would you verify before reusing this implementation in a different production system?
Official References
- Google SRE Book - Circuit Breakers & Retries: https://sre.google/sre-book/handling-overload/
- Python Tenacity Retry Library: https://tenacity.readthedocs.io/
03 · EXPLAIN
Interview checkpoint
Explain one design decision from this lesson, the alternative you rejected, and the failure mode or evidence that justified your choice.