use cano::prelude::*;
use cano::task::run_with_retries;
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
fn fast_policy() -> CircuitPolicy {
CircuitPolicy {
failure_threshold: u32::MAX / 2,
reset_timeout: Duration::from_secs(60),
half_open_max_calls: 1,
}
}
fn bench_acquire_record_closed(c: &mut Criterion) {
let mut group = c.benchmark_group("circuit_acquire_record_closed");
let breaker = Arc::new(CircuitBreaker::new(fast_policy()));
group.bench_function("single_thread", |b| {
b.iter(|| {
let permit = breaker.try_acquire().unwrap();
breaker.record_success(permit);
});
});
group.finish();
}
fn bench_try_acquire_open(c: &mut Criterion) {
let mut group = c.benchmark_group("circuit_try_acquire_open");
let breaker = Arc::new(CircuitBreaker::new(CircuitPolicy {
failure_threshold: 1,
reset_timeout: Duration::from_secs(3600),
half_open_max_calls: 1,
}));
let p = breaker.try_acquire().unwrap();
breaker.record_failure(p);
group.bench_function("rejected", |b| {
b.iter(|| {
let _ = breaker.try_acquire().unwrap_err();
});
});
group.finish();
}
fn bench_run_with_retries_overhead(c: &mut Criterion) {
let mut group = c.benchmark_group("circuit_run_with_retries");
let rt = tokio::runtime::Runtime::new().unwrap();
let no_breaker = TaskConfig::minimal();
group.bench_function("no_breaker", |b| {
b.to_async(&rt).iter(|| async {
let _ = run_with_retries::<u32, _, _>(&no_breaker, || async { Ok(42u32) }).await;
});
});
let breaker = Arc::new(CircuitBreaker::new(fast_policy()));
let with_breaker = TaskConfig::minimal().with_circuit_breaker(Arc::clone(&breaker));
group.bench_function("with_breaker", |b| {
b.to_async(&rt).iter(|| async {
let _ = run_with_retries::<u32, _, _>(&with_breaker, || async { Ok(42u32) }).await;
});
});
group.finish();
}
fn bench_shared_breaker_concurrent(c: &mut Criterion) {
let mut group = c.benchmark_group("circuit_shared_concurrent");
let rt = tokio::runtime::Runtime::new().unwrap();
for &n in &[16usize, 64, 256] {
let breaker = Arc::new(CircuitBreaker::new(fast_policy()));
group.bench_with_input(BenchmarkId::new("tasks", n), &n, |b, &n| {
b.to_async(&rt).iter(|| {
let breaker = Arc::clone(&breaker);
async move {
let mut handles = Vec::with_capacity(n);
for _ in 0..n {
let breaker = Arc::clone(&breaker);
handles.push(tokio::spawn(async move {
let permit = breaker.try_acquire().unwrap();
breaker.record_success(permit);
}));
}
for h in handles {
let _ = h.await;
}
}
});
});
}
group.finish();
}
fn bench_attempt_timeout_error_path(c: &mut Criterion) {
let mut group = c.benchmark_group("circuit_timeout_error_path");
let rt = tokio::runtime::Runtime::new().unwrap();
let config = TaskConfig::minimal().with_attempt_timeout(Duration::from_millis(1));
group.bench_function("single_timeout", |b| {
b.to_async(&rt).iter(|| async {
let _ = run_with_retries::<u32, _, _>(&config, || async {
tokio::time::sleep(Duration::from_millis(5)).await;
Ok::<u32, CanoError>(0)
})
.await;
});
});
group.finish();
}
fn bench_timeout_error_construction(c: &mut Criterion) {
let mut group = c.benchmark_group("circuit_timeout_error_construction");
group.bench_function("static_str", |b| {
b.iter(|| {
let err = CanoError::timeout("task attempt exceeded attempt_timeout");
std::hint::black_box(err);
});
});
group.finish();
}
fn bench_run_with_retries_open_short_circuit(c: &mut Criterion) {
let mut group = c.benchmark_group("circuit_run_with_retries_open");
let rt = tokio::runtime::Runtime::new().unwrap();
let breaker = Arc::new(CircuitBreaker::new(CircuitPolicy {
failure_threshold: 1,
reset_timeout: Duration::from_secs(3600),
half_open_max_calls: 1,
}));
let p = breaker.try_acquire().unwrap();
breaker.record_failure(p);
let config = TaskConfig::minimal().with_circuit_breaker(Arc::clone(&breaker));
let calls = AtomicUsize::new(0);
group.bench_function("rejected", |b| {
b.to_async(&rt).iter(|| async {
let _ = run_with_retries::<u32, _, _>(&config, || async {
calls.fetch_add(1, Ordering::Relaxed);
Ok::<u32, CanoError>(0)
})
.await;
});
});
assert_eq!(calls.load(Ordering::Relaxed), 0);
group.finish();
}
criterion_group!(
benches,
bench_acquire_record_closed,
bench_try_acquire_open,
bench_run_with_retries_overhead,
bench_shared_breaker_concurrent,
bench_attempt_timeout_error_path,
bench_run_with_retries_open_short_circuit,
bench_timeout_error_construction,
);
criterion_main!(benches);