Expert · SRE: reliability and incidents · topic 45 of 53 · 35 min
Resilience patterns, capacity and chaos engineering
Explanation
Failure is normal
In a system of many services, something is always a little broken: a slow database, a network blip, a pod being replaced. Resilience means the system keeps serving users, perhaps with less functionality, when parts fail. The danger is a cascading failure: one slow service makes its callers wait, they run out of threads, their callers wait too, and soon the whole site is down.
Timeouts
A timeout is the longest you will wait for a reply. Without one, a hung dependency keeps your threads or connections busy forever. Every network call needs a timeout. Choose it from the dependency's normal p99 latency plus a margin, and keep it shorter than the timeout of whoever is calling you, or your work is wasted.
Retries with exponential backoff and jitter
Many failures are brief, so one more try often works. But careless retries make outages worse: if a struggling service is hit by every client retrying at once, it never recovers. Rules:
- Retry only temporary errors (timeouts, 503, 429 Too Many Requests) and only operations that are safe to repeat.
- Use exponential backoff: wait base x factor to the power of the attempt, with a cap. For base 1 s, factor 2 and cap 30 s: 1, 2, 4, 8, 16, 30 seconds.
- Add jitter: a random part of the wait, so thousands of clients do not retry at the same moment.
- Limit attempts (often 3) and retry at one layer only. If three layers each try 3 times, the bottom service can get 27 calls for one click.
import random
import time
def call_with_retries(call, attempts=4, base=0.5, factor=2, cap=8):
for attempt in range(attempts):
try:
return call()
except TimeoutError:
if attempt == attempts - 1:
raise
delay = min(cap, base * factor ** attempt)
time.sleep(random.uniform(0, delay)) # "full jitter"Idempotency keys
An operation is idempotent if doing it twice has the same effect as doing it once. Reading a balance is idempotent; "debit 500 rupees" is not. If the reply to a payment request is lost and the app retries, the customer could be charged twice. The fix is an idempotency key: the client creates a unique ID for each payment and sends it with every attempt, often in an Idempotency-Key header. The server stores the result for that key and, on a repeat, returns the saved result instead of charging again.
Circuit breakers
A circuit breaker wraps calls to a dependency and stops calling it when it keeps failing. It has three states:
| State | Behaviour |
|---|---|
| Closed (normal) | Calls go through. Consecutive failures are counted; at a threshold the breaker opens. |
| Open | Calls fail at once without touching the dependency, giving it time to recover. After a cool-down it goes half-open. |
| Half-open | One trial call is allowed. Success closes the breaker; failure opens it again. |
Bulkheads, rate limits, load shedding, graceful degradation
- Bulkheads: separate pools of threads or connections per dependency, like the watertight compartments of a ship. A slow recommendations service can then use up only its own pool, not the threads that payments need.
- Rate limiting: cap how many requests each client may send, for example 100 per minute per API key, and answer 429 Too Many Requests above that.
- Load shedding: when the server is overloaded, reject low-priority work early (with 503) so the important work stays fast, instead of everything becoming slow.
- Graceful degradation: when a dependency fails, serve a reduced version. Show cached prices, hide "people also bought", but keep checkout working.
Capacity planning
Capacity planning means making sure you have enough resources for future demand. Combine normal growth with known peaks: a sale day, an IPL final, tatkal opening at 10 am, the last day for filing tax returns. Load test to find where the system breaks, keep headroom (many teams aim to run below about 60 to 70% of capacity at peak), keep spare capacity so one zone can fail (N+1), and scale up before a known event, because autoscaling takes minutes to react.
Chaos engineering
Chaos engineering is running careful experiments that inject failures on purpose, to learn how the system behaves before a real failure teaches you. Netflix made it popular with Chaos Monkey, which randomly stopped servers.
- Define the steady state: a metric that shows normal behaviour, such as payment success rate.
- Write a hypothesis: "If one of three payment pods is killed, success stays above 99.5%."
- Inject a realistic failure: kill a pod, add 300 ms of network delay, fill a disk.
- Keep the blast radius small: start in staging, then a small slice of production, with a clear abort switch.
- Compare results with the hypothesis, fix weaknesses, and repeat.
Tools include Chaos Mesh and LitmusChaos (both CNCF projects for Kubernetes), AWS Fault Injection Service, Azure Chaos Studio and Gremlin.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Resilience patterns, capacity and chaos engineering · 1/8
In this lesson you will learn design patterns that keep a system running when parts of it fail.
Prefer a video by a teacher? Find free videos on “resilience patterns circuit breaker retry backoff jitter chaos engineering” on YouTube ↗
In real life: Sale day at an online store
An Indian e-commerce company prepares for its biggest sale. Weeks before, it load tests checkout at three times last year's peak and finds the coupon service fails first. The team adds a 300 ms timeout and a circuit breaker around coupons: if coupons fail, checkout continues without the discount and shows a message, instead of failing. Payment calls carry idempotency keys so app retries never charge twice. A chaos experiment in staging kills a payment pod every 10 minutes to confirm the success rate holds. Capacity is scaled up the night before. On sale day the coupon service does fall over for 6 minutes, and customers can still pay.
Practice (0/3 done)
Write backoff_schedule(base, factor, cap, retries) that returns a list of the wait times before each retry: base x factor to the power i for i = 0, 1, 2 ... retries - 1, but never more than cap. No randomness here, so it is easy to test. Example: backoff_schedule(1, 2, 30, 6) returns [1, 2, 4, 8, 16, 30].
Write a CircuitBreaker class. __init__(self, failure_threshold, reset_timeout, clock) where clock is a function returning the current time in seconds. Keep self.state as "closed", "open" or "half_open". allow_request(): in "closed" or "half_open" return True; in "open", if clock() - opened_at >= reset_timeout, switch to "half_open" and return True, otherwise return False. record_success(): reset the failure count and set "closed". record_failure(): in "half_open", open again at once; in "closed", count consecutive failures and open when the count reaches failure_threshold. Opening sets opened_at = clock() and resets the count.
Plan a first chaos experiment for an "orders" service with 3 pods on Kubernetes that calls a "payments" service. Write the steady state, the hypothesis, the failure you will inject, where you will run it, how you will limit the blast radius, and when you will abort.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Incident managementSRE: reliability and incidents · same module
- Background jobs and queuesBackend Development · Performance and reliability
- Loading, errors and slow networksFull-Stack Development · Connecting frontend and backend
- Promise.all, allSettled, race and anyJavaScript Programming (language in depth) · Asynchronous JavaScript
- Calling other APIs safely with the HTTP clientGo Programming (backend and cloud) · Standard library essentials
- Designing for high availabilityCloud Computing (AWS, Azure, Google Cloud) · High availability, scaling and disaster recovery
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why add jitter to retry delays?
Answer: So many clients do not retry at the same moment and overload the service again
Jitter spreads retries out in time and prevents synchronised retry storms.
A mobile app retries a payment after its reply was lost. What prevents a double charge?
Answer: An idempotency key stored by the server
The server sees the same key and returns the saved result instead of charging again.
A circuit breaker is open. What happens to a new call before the cool-down ends?
Answer: It fails immediately without calling the dependency
Failing fast protects both the caller and the struggling dependency.
What are bulkheads?
Answer: Separate resource pools per dependency so one slow dependency cannot use up everything
Like a ship's compartments, they contain the damage to one part.
Which is the best first chaos experiment?
Answer: Kill one pod in staging with a written hypothesis and an abort condition
Start small, in staging, hypothesis-driven, with a way to stop.
With exponential backoff and a 1 second base, how long is the wait after the 3rd failure?Related
Answer: 4 seconds
The waits double: 1, 2, 4. After the 3rd failure the wait is 4 seconds.
Which request is safe to retry automatically after a network error?Related
Answer: GET /api/notes
Reading notes again changes nothing. A POST may already have worked, so repeating it can create duplicates or double payments.
Why can retrying a payment be dangerous?Related
Answer: The first attempt may have succeeded, causing a double charge
Only retry idempotent operations, or use idempotency keys.
When is it safe to retry a request automatically?Related
Answer: For idempotent requests, such as GET or a POST with an idempotency key
Retrying a non-idempotent request can repeat its effect, like charging twice.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “SRE: reliability and incidents”. The module exam comes next.
- Module 10 exam: SRE, reliability and incidents · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, DevSecOps. 53 topics left in the course before the final exam.