use std::time::{Duration, Instant};
use reliakit::backoff::Backoff;
use reliakit::circuit::{CircuitBreaker, State};
use reliakit::ratelimit::RateLimiter;
use reliakit::timeout::Timeout;
fn call_dependency(attempt: u32) -> Result<(), &'static str> {
if attempt < 4 {
Err("upstream unavailable")
} else {
Ok(())
}
}
fn main() {
let deadline = Timeout::new(3_000).start(0);
let mut limiter = RateLimiter::new(5, 1, 200);
let mut breaker = CircuitBreaker::new(3, 500);
let backoff =
Backoff::exponential(Duration::from_millis(100), 2).with_max_delay(Duration::from_secs(1));
let start = Instant::now();
let mut attempt = 0u32;
let mut retries = 0u32;
loop {
let now = start.elapsed().as_millis() as u64;
match deadline.check(now) {
Some(remaining) => {
if remaining == 0 {
println!("[{now:>4}ms] deadline reached; giving up");
break;
}
}
None => {
println!("[{now:>4}ms] deadline expired; giving up");
break;
}
}
if !limiter.try_acquire_one(now) {
let wait = limiter.retry_after(now, 1).unwrap_or(0);
println!("[{now:>4}ms] rate limited; waiting {wait}ms");
sleep_within(wait.max(50), &deadline, start);
continue;
}
if !breaker.allow(now) {
println!("[{now:>4}ms] circuit open; skipping call");
sleep_within(120, &deadline, start);
continue;
}
match call_dependency(attempt) {
Ok(()) => {
breaker.on_success();
println!("[{now:>4}ms] call ok (circuit {:?})", breaker.state());
if breaker.state() == State::Closed {
break;
}
}
Err(e) => {
breaker.on_failure(now);
let wait = backoff.delay(retries).unwrap_or_default();
retries += 1;
attempt += 1;
println!(
"[{now:>4}ms] call failed ({e}); circuit {:?}; backing off {}ms",
breaker.state(),
wait.as_millis()
);
sleep_within(wait.as_millis() as u64, &deadline, start);
}
}
}
let now = start.elapsed().as_millis() as u64;
println!(
"\nfinal circuit state: {:?}; {}ms of budget left",
breaker.state(),
deadline.remaining(now)
);
}
fn sleep_within(ms: u64, deadline: &reliakit::timeout::Deadline, start: Instant) {
let now = start.elapsed().as_millis() as u64;
let capped = ms.min(deadline.remaining(now)).min(300);
std::thread::sleep(Duration::from_millis(capped));
}