Recommended resources
The freely available resources this lesson will direct you to.
- Anthropic’s Claude Outage Explained | Rate Limiting, Autoscaling & Load Sheddingwww.youtube.com
- Server Throttler & Rate Limiter | System Design Ladder 🪜 | HLD 101www.youtube.com
- Enhancing Netflix Reliability with Service-Level Prioritized ...netflixtechblog.com
- Using load shedding to avoid overload - AWS - Amazon.comaws.amazon.com
Hello! Welcome to the final lesson in our module on "Rate Limiting and Traffic Shaping."
In our last session, we built an adaptive rate limiter that proactively adjusts concurrency based on system health, primarily latency. This is a powerful, proactive technique for preventing overload. However, even the best proactive systems can be overwhelmed by sudden, massive traffic spikes, unexpected capacity loss, or cascading failures from dependencies.
Today, we address the critical question: what does a service do when it's already overloaded? Our learning outcome is to design and implement degraded response strategies, such as queueing and load shedding, for managing system overload. This is your last line of defense—a survival mechanism. Given your deep experience building and operating Rudi at AWS, you're already familiar with concepts like load shedding and QoS. This lesson will focus on structuring that knowledge for an interview setting, exploring the nuanced trade-offs and citing industry-best practices to build a comprehensive answer.
1. The Anatomy of Overload: Why "Trying Harder" Fails
When a system receives more requests than it can handle, its first response is to queue the work. This leads to increased resource contention (CPU context switching, memory pressure), and latency for all requests begins to climb.
This is where a dangerous feedback loop, often called a "death spiral," can start.
Anthropic’s Claude Outage Explained | Rate Limiting, Autoscaling & Load Shedding
First, let's watch a quick explanation of this cascading failure scenario. It clearly sets up the problem that load shedding is designed to solve.
Please watch the segment from 02:25 to 03:05. It describes how a traffic surge leads to timeouts and retries, creating a self-amplifying feedback loop.
As latency increases past the client's timeout threshold, requests begin to fail. From the client's perspective, the service is unavailable. The server, meanwhile, might still be wasting precious cycles working on requests that have already been abandoned by the client.
To understand this visually, we distinguish between throughput (the total requests offered to the system) and goodput (requests that succeed within the client's timeout).

The core goal of a degraded response strategy is to protect the system's goodput and maintain predictable, low latency for the requests it chooses to accept.
2. The Strategy: Load Shedding as a Survival Mechanism
Load shedding is the act of proactively and cheaply rejecting excess work to protect a service's health and ensure it can continue to process a subset of requests effectively. It's a deliberate choice to serve some users well rather than all users poorly.
It's crucial to distinguish this from rate limiting:
- Rate Limiting: Asks "Who are you and have you exceeded your quota?" It's client-focused and about fairness or preventing abuse.
- Load Shedding: Asks "Am I healthy enough to take on more work?" It's server-focused and about survival.
Server Throttler & Rate Limiter | System Design Ladder 🪜 | HLD 101
This video provides a very clear distinction between these two concepts, which is a common point of confusion.
Watch the introduction from 00:04 to 00:40 and the summary from 26:03 to 28:09. This will solidify the mental model of rate limiting as a quota check and load shedding as a health check.
By shedding load, we keep latency for accepted requests low and stable, ensuring the work we do perform isn't wasted. This transforms a catastrophic availability collapse into a more graceful degradation.

3. Designing a Load Shedding Strategy
An effective load shedding strategy requires answering two questions: When do we shed load, and what do we shed?
3.1. When to Shed: Health Metrics as Triggers
A service needs to monitor its own health to know when it's approaching its limit. Key signals include:
- CPU Utilization: This is a direct and common metric. A service might autoscale at 70% CPU but begin shedding load at 90% to create a buffer and protect itself from tipping over.
- In-flight Requests: The number of requests currently being processed is a strong proxy for memory usage and concurrency pressure. You can calculate this using Little's Law: .
- Queue Depth / Time-in-Queue: A growing request queue is a clear sign that processing capacity is saturated. It's often better to measure the time a request has spent in a queue and drop it if it's too old, as it's likely to fail anyway.
- Dependency Health: Latency or error rates from downstream services are crucial indicators. Shedding can protect your service from being overwhelmed by a slow dependency.
Server Throttler & Rate Limiter | System Design Ladder 🪜 | HLD 101
Let's review the primary health signals a server can monitor to decide when to shed load.
Watch the segment from 15:50 to 21:29. It provides a good explanation of using CPU utilization and the number of in-flight requests as key indicators of server health.
3.2. What to Shed: The Art of Prioritization
Dropping requests randomly is better than crashing, but we can be far more intelligent. Prioritization is what makes a load shedding strategy robust. This is where you can demonstrate deep system design thinking.
A foundational resource for this is the AWS Builders' Library article on load shedding. It's dense but full of production-wisdom.
Using load shedding to avoid overload
The article 'Using load shedding to avoid overload' is a masterclass in designing for resilience. We'll focus on the section that details specific prioritization mechanisms.
Please read the entire section titled 'Load shedding mechanisms'. It's long, but it covers a wealth of crucial strategies. Pay special attention to: Prioritizing requests: The difference between crawler traffic and human traffic. Keeping an eye on the clock: Using client timeout hints to drop doomed requests. Finishing what was started: Prioritizing end() calls over start() calls, or subsequent pagination pages over the first page. Watching out for queues: The dangers of hidden queues and the benefits of LIFO behavior.
Let's now look at a concrete, real-world example of prioritization from Netflix. Their approach to service-level load shedding is a perfect case study.
Enhancing Netflix Reliability with Service-Level Prioritized Load ...
This Netflix TechBlog post, 'Enhancing Netflix Reliability with Service-Level Prioritized Load Shedding', provides an excellent, practical walkthrough of implementing this concept.
Read from 'Introducing Service-Level Prioritized Load Shedding' through 'Generic CPU based load-shedding'. Focus on: How they classify requests into critical (user-initiated) and non-critical (pre-fetch). Their solution using a partitioned concurrency limiter to guarantee capacity for critical requests. How they define priority buckets (CRITICAL, DEGRADED, BEST_EFFORT, BULK). The graph showing how they progressively shed traffic from lower-priority buckets as CPU utilization increases.
The Netflix example is powerful because it demonstrates a system that doesn't just have a single "shedding" threshold, but multiple, tiered thresholds based on request priority. This allows for graceful degradation, where non-essential features are shed first, preserving the core user experience for as long as possible.
4. Implementation Sketch: A Prioritized Load Shedding Middleware
In an interview, you might be asked to sketch out an implementation. Drawing from the Netflix example, a common pattern is a request middleware or filter that makes an admission decision.
Here is a Python sketch of what that could look like. It uses a simplified, CPU-based trigger and priority levels.
import time
import psutil # For getting CPU utilization
from enum import Enum
class RequestPriority(Enum):
CRITICAL = 0
DEGRADED = 1
BEST_EFFORT = 2
BULK = 3
class PrioritizedLoadShedder:
def __init__(self):
# Shedding starts when CPU exceeds these thresholds for a given priority.
self.cpu_shed_thresholds = {
RequestPriority.CRITICAL: 95,
RequestPriority.DEGRADED: 85,
RequestPriority.BEST_EFFORT: 70,
RequestPriority.BULK: 50,
}
# Health checks are never shed.
self.health_check_path = "/health"
def _get_request_priority(self, request_headers, request_path):
"""Determines request priority from headers or path."""
if request_path == self.health_check_path:
return RequestPriority.CRITICAL # Special handling for health checks
priority_header = request_headers.get("X-Request-Priority", "best_effort").upper()
if priority_header == "CRITICAL":
return RequestPriority.CRITICAL
if priority_header == "DEGRADED":
return RequestPriority.DEGRADED
if priority_header == "BULK":
return RequestPriority.BULK
return RequestPriority.BEST_EFFORT
def should_admit(self, request_headers, request_path) -> (bool, str):
"""
The core admission control logic.
Returns (True, "") if admitted, or (False, "reason") if shed.
"""
priority = self._get_request_priority(request_headers, request_path)
# Never shed health checks from the load balancer.
if request_path == self.health_check_path:
return True, ""
current_cpu = psutil.cpu_percent()
shed_threshold = self.cpu_shed_thresholds[priority]
if current_cpu > shed_threshold:
reason = (f"Shedding {priority.name} request. "
f"CPU ({current_cpu}%) > Threshold ({shed_threshold}%)")
print(reason)
return False, reason
return True, ""
# Example usage in a web framework middleware
def middleware(request):
shedder = PrioritizedLoadShedder()
should_admit, reason = shedder.should_admit(request.headers, request.path)
if not should_admit:
```grasp
{
"type": "exercise",
"id": "e1cf0cbb-31da-41b5-9c32-a2397e7d0e9a"
}
# Return a 503 Service Unavailable response
return http_response(status_code=503, body=reason)
# Process the request normally
return process_request(request)
This code demonstrates the key design principles:
1. **Centralized Decision:** A single `should_admit` point of control.
2. **Priority Extraction:** Logic to map an incoming request to a priority level.
3. **Health Check:** A monitor of system health (here, CPU).
4. **Tiered Thresholds:** Different rules for different priorities, enabling graceful degradation.
5. **Fail Fast:** Returning a `503` immediately is cheap and signals to the client that the server is busy, inviting a (hopefully backed-off) retry later.
---
### Conclusion
Today we explored the final layer of defense against system overload. While rate limiting and autoscaling are essential, load shedding is the critical survival mechanism that keeps a service alive and performing predictably during extreme stress.
**Key Takeaways:**
* **Overload is a feedback loop:** Increased load leads to increased latency, which causes timeouts and retries, further increasing load.
* **Load shedding protects goodput:** By intentionally rejecting excess requests, a service can maintain low latency and high availability for the requests it accepts.
* **Shedding is triggered by health, not quotas:** It's a server-side decision based on metrics like CPU utilization, queue lengths, or in-flight requests.
* **Prioritization is paramount:** An effective strategy doesn't shed randomly. It protects critical requests (health checks, core user functions) by dropping less important work first.
* **Implementation is often a middleware:** A request filter inspects incoming traffic, determines its priority, checks system health, and makes an admission decision.
### Next Lesson Preview
We have now completed our deep dive into traffic management and system resilience. You've designed everything from basic rate limiters to sophisticated adaptive and degraded response systems.
In the next module, we will shift gears to a new topic: **Graph Algorithms and Applications**. We'll begin by learning how to model real-world problems, such as the task dependencies in a complex CI/CD pipeline or resource allocation in a distributed system, as graphs. This will open up a new set of powerful algorithmic tools for solving complex scheduling and relationship-based problems.