Understanding the Retry Storm Threat
In distributed systems, a 'retry storm' is a dangerous phenomenon where a temporary service outage or latency spike triggers a cascade of client-side retries. Instead of resolving the issue, these aggressive retries overwhelm the already struggling service, exacerbating the problem and potentially leading to a complete system collapse. For a platform like Uber, which handles millions of requests per second for critical functions like ride-hailing, payments, and dispatch, such storms represent a significant threat to reliability and user experience.
The core problem lies in the naive implementation of retry logic. When a client receives an error response (e.g., a timeout or a 5xx server error), its immediate reaction is often to retry the operation. If multiple clients experience this simultaneously, and the service remains degraded, each retry attempt adds further load. This creates a feedback loop: the service becomes slower due to the retries, causing more errors, which in turn triggers more retries. It's akin to a fire spreading in dry brush – one spark ignites a conflagration.

Uber's Multi-Layered Defense Strategy
Uber's approach to combating retry storms is not a single silver bullet but a comprehensive, multi-layered strategy designed to detect, contain, and recover from such events. This involves both client-side and server-side mechanisms, as well as robust monitoring and alerting.
Client-Side Controls
The first line of defense is implemented directly within Uber's client applications and backend services. These controls aim to prevent the excessive retries from even starting or to manage them intelligently.
Exponential Backoff with Jitter
This is a standard technique, but crucial for retry storm mitigation. Instead of retrying immediately, clients wait for an exponentially increasing period between retries. For example, the first retry might be after 100ms, the second after 200ms, the third after 400ms, and so on. 'Jitter' is then added to this delay – a small random variation. This prevents multiple clients, all experiencing the same outage, from retrying at precisely the same interval, which could otherwise create synchronized retry bursts. Uber ensures this is implemented consistently across its vast codebase.
Circuit Breaking
Inspired by the circuit breaker pattern in electrical engineering, this mechanism prevents a client from repeatedly attempting an operation that is likely to fail. If a client detects that a particular service is consistently returning errors (e.g., exceeding a threshold of failed requests within a given time window), it 'opens the circuit'. For a configurable period, the client will not even attempt to call the failing service, instead immediately returning an error or a fallback response. This gives the downstream service breathing room to recover without additional load.
Rate Limiting
While often implemented server-side, clients can also participate in rate limiting. Clients can be configured with per-service or per-endpoint rate limits, restricting the number of requests they can send within a specific time frame. This acts as a governor, ensuring that even if a client is programmed to retry, it cannot overwhelm a service beyond its capacity.
Server-Side Defenses
While client-side controls are essential, they are not sufficient. Uber also employs server-side strategies to protect its services from being overloaded.
Adaptive Concurrency Limits
Instead of fixed concurrency limits, Uber utilizes adaptive limits. These limits dynamically adjust based on the service's current capacity and health. If a service is experiencing high load or latency, its adaptive concurrency limit will decrease, rejecting new requests early rather than accepting them and failing later. This is often implemented using algorithms that monitor queue lengths, processing times, and error rates.
Request Prioritization and Queuing
Not all requests are created equal. Uber employs sophisticated queuing mechanisms that can prioritize critical requests (e.g., a user confirming a ride) over less time-sensitive ones (e.g., analytics reporting). When a service is under duress, it can serve high-priority requests from its queue first, ensuring that core functionality remains available even if some non-essential operations are delayed or dropped.
Graceful Degradation
In severe overload scenarios, services can be configured to gracefully degrade their functionality. This might mean disabling non-essential features, returning cached or stale data where appropriate, or responding with simplified error messages that indicate overload without requiring complex processing. The goal is to keep the most critical paths operational.
Observability and Alerting
Underpinning all these defenses is a robust observability strategy. Uber relies heavily on metrics, logging, and tracing to detect the early signs of a retry storm and to understand its impact.
Real-time Metrics and Anomaly Detection
Key metrics like request latency, error rates (especially 5xx errors), and queue depths are monitored in real-time. Uber employs anomaly detection algorithms that can flag unusual spikes in these metrics, potentially indicating the start of a retry storm, even before it causes widespread user impact. This allows for proactive intervention.
Distributed Tracing
When a problem does occur, distributed tracing is invaluable. It allows engineers to follow a single request as it traverses multiple services. This helps identify which service is the bottleneck and, critically, whether a surge in requests to that service is due to legitimate traffic or a retry storm originating from a specific upstream client or service.
Automated Alerting and Incident Response
Automated alerts are configured for the detected anomalies. These alerts trigger Uber's incident response protocols, enabling engineers to quickly diagnose the situation, identify the source of the storm, and take corrective actions. This might involve adjusting concurrency limits, temporarily disabling specific client retry logic, or manually intervening to stabilize the affected service.
The Human Element: A Culture of Resilience
Beyond the technical controls, Uber emphasizes a culture of resilience. This involves rigorous testing of failure scenarios, including simulated retry storms, during development and deployment. Post-mortems are conducted for any significant incident, with a focus on identifying how retry storm defenses could be improved. The surprising detail here is not the complexity of the technical solutions, but the consistent, company-wide focus on proactively engineering for failure. It's about embedding resilience into the DNA of their systems and their engineers.
What nobody has addressed yet is the long-term impact of these sophisticated defenses on the development velocity of new features. While essential for stability, advanced retry management and circuit breaking can add complexity to client development. Balancing this stability with rapid iteration remains an ongoing challenge.
