| ▲ | aftbit 3 hours ago | |||||||||||||||||||||||||||||||||||||||||||||||||
I'd be interested to hear other strategies in this space. I've done the naive thing of allowing retries everywhere, and gotten into retry storms. When I was next presented with the problem, I tried the other naive thing of only allowing retries from the very top level service, which led me to redoing absolutely tons of work for each failure. What's a nice middle path that doesn't add too much complexity? | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | CBLT 2 hours ago | parent | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
There's a good amount of literature about this (check the other comments), but you can vastly simplify this into two things you need to do: 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. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | mandevil 39 minutes ago | parent | prev | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
At $previousJob we implemented circuit breakers: centralize all requests to the foreign service (every call to service theta went through the service theta client which had some shared state so everything so we could keep track of requests) and then monitor, when error % got above a certain limit start to dump requests to a text file for sending in the future instead of now. And the centralized caller will send one message every time gap (we started at 30s) and as long as that errors out we keep writing. 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. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | otterley 3 hours ago | parent | prev | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
https://aws.amazon.com/blogs/developer/introducing-retry-thr... (2016 -- 10 years ago!) 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) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | sroussey 3 hours ago | parent | prev | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
So many variables, but the simple thing is to set things up like normal rate limiting (which you would want to do anyways). The one generating the errors passes back a retry time. You can add jitter here, tell low priority requests to wait longer, etc. 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). | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | konaraddi 28 minutes ago | parent | prev | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
Depending on the context, circuit breakers | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | tregoning 3 hours ago | parent | prev | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | cyberax 30 minutes ago | parent | prev | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
One good option that is not (yet?) mentioned here is a deadline for retries. You can cap the request duration by, say, 500ms and pass the remaining time budget to downstream services. This can be done via an HTTP header and enforced by the middleware. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | sfraxo 2 hours ago | parent | prev [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
[flagged] | ||||||||||||||||||||||||||||||||||||||||||||||||||