tower
tower copied to clipboard
Retry middleware improvements
This issue scopes out the changes we are proposing to the retry middleware to improve its ergonomics. Currently, the retry middleware is quite hard to use and requires implementing a custom Policy. Writing this policy is non-trivial and is more work than it should be.
These improvements are aimed at setting up retries with tower to be easier and more user friendly. As well as providing good defaults that work out of the box.
List of improvements to tower and tower-http:
- [x] Simplify
Policy(https://github.com/tower-rs/tower/pull/681).- Change trait fn to take
&mut self. - Change
Futureoutput to(). - Alllow
Policyto be object safe.
- Change trait fn to take
- [x] Add generic backoff utilities. https://github.com/tower-rs/tower/pull/685
- [x] Add some
trait Backoffthat has an associated future type that allows others to use this utility without being tied totokio::time. - [x] Add a
ExponentialBackofftype that implementsBackoff, ported fromlinkerd2-proxy. - [x] Add
Rngutilities https://github.com/tower-rs/tower/pull/686
- [x] Add some
- [ ] Budget improvements.
- [x] Merge budget docs PR (https://github.com/tower-rs/tower/pull/613).
- [ ] Implement simpler non-time based token bucket budget.
- [ ] Add some
Budgettrait to allow users to choose which implementation to use.
- [ ] Add a new batteries included standard retry policy https://github.com/tower-rs/tower/pull/698
- [ ] New
StandardRetryPolicycombiningimpl Backoffandimpl Budget. - [ ] Add
StandardRetryPolicyBuilderthat accept closures (?) foris_retryable(&mut Req, &mut Result<Res, E>) -> booland aclone_request(&Req) -> Option<Req>.
- [ ] New
- [ ]
tower-httpimprovements.- [ ] Add new
retrymodule - [ ] Implement
ReplayBodysimilar to the one implemented inlinkerd2-proxy. - [ ] Add new
HttpRetrylayer that accepts higher level constructs for retrying, likeClassifyResponse, and will wrap http request bodies withReplayBody.
- [ ] Add new
- [ ] Documentation
- [ ] Blog post on how to setup retries with
towerandtower-http. - [ ] Examples for thick clients with retries in both
towerandtower-http.
- [ ] Blog post on how to setup retries with
Example code
tower examples with no http:
let policy = StandardRetryPolicy::builder()
.should_retry(|res| match res {
Ok(_) => false,
Err(_) => true,
})
.clone_request(|r| Some(*r))
.build();
let mut svc = ServiceBuilder::new()
.retry(policy)
.buffer(10)
.timeout(Duration::from_secs(10))
.service(svc);
tower-http examples:
let make_classifier = ServerErrorsAsFailures::make_classifier();
let mut svc = ServiceBuilder::new()
.set_request_id("Request-Id".try_into().unwrap(), MakeRequestUuid)
.retry(StandardHttpPolicy::new(
make_classifier,
ExponentialBackoff::default(),
Budget::default(),
|e| match e {
ServerErrorsFailureClass::Status(s) => true,
ServerErrorsFailureClass::Error(s) => false,
},
))
.timeout(Duration::from_secs(5))
.service(client);
cc @rcoh @hawkw @jonhoo @seanmonstar @davidpdrsn
It would be cool if this could integrate with 429/Retry-After header out-of-the-box.
@lovesegfault yeah, good idea, this seems like a config option that could live in HttpRetry.
+1 to lovesegfault, I found this crate will looking for a way to handle Retry-After easily
so this issue delay ?