How Uber Protects Against Retry Storms(uber.com) |
How Uber Protects Against Retry Storms(uber.com) |
This is quite slever. I also really like the concept of an "Error Budget", inspired by SRE and SLO(s) no doubt :)
None of these were even expected to fail. But the code was buggy as well, so after D being retried 10000 times, it’d eventually give up. Managed to convert a sub-second operation into a half hour affair.
First thing is, if your service has 7 call layers, maybe it's just too deep.
Second thing is, I found that retry strategy works the best if you define it based on what the nodes are actually doing (instead of treating them as generic nodes). For example, if node D is a database failing a transaction, you may just configure it to retry the transaction instead of doing an application-initialized request resubmit, because the database probably knows better about why the transaction has failed than the application connected to it.
Third thing is, retry is worth it only when progress has been and/or can still be made. If the resources is no longer available forever, then there's no point of retrying.
1: https://www.uber.com/us/en/blog/cinnamon-using-century-old-t...
I'm thankful that, at my $WORK, we're all software engineers by title, from the lowly interns to the CTO. Still, everybody knows who's who and whom to talk to, depending on the gravity of the issue.
All engineers are equal, however some engineers are more equal than others?
CTOs work is totally different from SE, so why s/he is SE?
Lets make your janitor can be SE too
In academic world, I hear, folks simply add the name of their professors due to their reputation. Of course, professors are putting their reputation at stake by giving a chance to the right students.
It's not exactly conducive to knowing that if you're a new starter though, is it. I imagine it takes quite a while to get to know, especially outside of your immediate remit.
You know if they are your boss, not how big of a boss they are. That's way more important unless your organization is a chaotic mess where managers feel like they can order around people from a different team. And then you got bigger problems.
If you can afford that, with all the coordination costs that it comes from it, good.
An alternative is just to let the downstream service own the retry logic. Too many requests? Just error out as soon as possible.
Each team manages its budget and each other team adapts.
It's not AI that is going to replace you, folks. Btw is it a coincide or has Uber become a family business now?
1. Your service that retries should have some retry budget. This is a good place to be "smart", because you can reason entirely locally instead of turning it into a distributed systems problem. The best library I've seen for this was doing Exponential Moving Average of requests per second sent down that pipe (not counting retries) and only allowing 20% more requests per second as retries, total. Each individual request could be retried 3 times. This was critical as it bounds the additional load from retries.
2. Whenever a service retries but has to give up, the error it sends to its callers should never be retried. There has to be some agreement that that HTTP code will never be retried. This prevents the multiplicative factor of retry on top of retry, which is why those storms can generate so much load.
Everything else is nice-to-have, but those two alone should bound the total requests you get in a retry storm.
This can't really be tolerated in practice, though, because it means that one bad component somewhere in your stack, one that is able to accept and respond to requests but for whatever reason isn't able to make requests to its backends, poisons the whole stack. You can't take one backend's word for it that the failure is not localized and therefore retryable.
Ooh, I like the idea of propagating "no retries" hints in the responses back upstream. Have you seen it implemented in the wild, or in public discussions about the practice?
The reason why I brought up the exception propagation is cause within a large enough service with multiple layers of depth the exception hierarchy provides with similar context.
The hard part is not implementing something like this, its about maintaining it consistently across every new change. With small product teams this architecture concept/convention/constraint can easily get lost/forgotten and what you are left with is a theoretical system which does not works as desired when the storm comes
https://builder.aws.com/content/3EumjoZascWd1oZiEgL8ORlv3qE/... (originally published 2020, republished 2026)
https://docs.aws.amazon.com/sdkref/latest/guide/feature-retr...
https://aws.amazon.com/blogs/developer/announcing-updated-re... (2026)
BTW: do keep track of priority. It’s like having a database that gets flooded with connections and won’t allow new ones in—but will for admin users (btw, it did not used to be that way in the early days of MySQL).
We did that because otherwise we would get 2x30 second timeouts to a dead service on every user interaction and it made for a terrible user experience. Keeping track and handling it smartly made the average user experience a lot better.
This can be done via an HTTP header and enforced by the middleware.
Also, a simple signal status server or queue system helps to keep global state such that everyone doesn’t retry all at once.
If you have a central error rate server you can skip your retry based on the error rate (100% error rate, don’t retry, etc).
Like if the title is Tech Lead, Staff, Principal or whatever there's an (often) unstated focus area. You're saying if you also state that, that's helpful. Yes it is! But if you only state that – Software Engineer, Mobile UX – it's helpful but you're back to having no idea of relative experience or decision-making power.
(If the org is truly flat and the intention is no inequality in 'decision-making power' etc. then fine, I suppose, but that's kind of a different conversation, pretty unusual, and I'm not sure how much I really believe it works (as in, is borne that way) in practice.)
When we implement retries where I work, the general rule is that if a request is suitable for retry, it should include the RetryInfo in the error status and use it as the base delay for the exponential backoff. The absence of that detail means don’t retry, and we have a client interceptor that parses the response status and retries according to that logic.
1: https://github.com/googleapis/googleapis/blob/bba4c646b1f85a...
Microsoft breaks all Old New Thing links every few years so it's necessary to post the title so the right post can still be found.
I don't follow how being cautious about avoiding multiplicative layers of backoffs is trading a good developer experience for a bad user experience. The described situation is an awful user experience. Simply adding a retry and calling it a day sounds like the easy developer experience at the expense of the user experience
Sure, the worst case scenario is. 99.9999% of the time, a transient error will actually just work on the first or second auto-retry and save your users the effort of paying attention and manually retrying things. This is especially prudent for background tasks where the failure may not be noticed right away; coming back to something fire-and-forget 30m later to see it never tried to finish is not a good user experience.
> My point was that just throwing exponential backoffs at the retry problem is not a magic solution
Nobody said it was. In fact, I suggested the exact opposite - a proper solution takes dev effort. Adhering to an iron rule of "just make them manually retry" is throwing your hands up and not even trying to solve the problem because laziness is convenient.
I responded to a post that merely linked to the Wikipedia article for exponential backoff (in response to "I'd be interested to hear other strategies in [protecting against retry storms]")
The submitted article is precisely about the degenerate case and the difficult work of dealing with it
> This approach works for transient or low-rate failures. However, during moderate or severe degradation, it becomes counterproductive. Aggressively retrying against an already struggling service increases load, accelerates failure, and amplifies retry traffic across upstream dependencies. What begins as a localized outage can quickly escalate into a stack-wide incident—ultimately degrading, or in the worst case, completely breaking, the end user experience.