use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::time::Duration;
pub const MIN_CALIBRATION_SAMPLES: usize = 8;
const MIN_CALIBRATION_WINDOW: usize = MIN_CALIBRATION_SAMPLES;
const MAX_CALIBRATION_WINDOW: usize = 4096;
pub const DEFAULT_CALIBRATION_WINDOW: usize = 256;
pub const DEFAULT_ALPHA: f64 = 0.05;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum QuantileCache {
Dirty,
Unavailable,
Bound(u64),
}
#[derive(Debug)]
pub struct ConformalRetryBudget {
slo_ms: u64,
alpha: f64,
calibration_window: usize,
latencies_ns: VecDeque<u64>,
quantile_cache: Cell<QuantileCache>,
}
impl Default for ConformalRetryBudget {
fn default() -> Self {
Self {
slo_ms: 0,
alpha: DEFAULT_ALPHA,
calibration_window: DEFAULT_CALIBRATION_WINDOW,
latencies_ns: VecDeque::with_capacity(DEFAULT_CALIBRATION_WINDOW),
quantile_cache: Cell::new(QuantileCache::Dirty),
}
}
}
impl ConformalRetryBudget {
pub const fn slo_ms(&self) -> Option<u64> {
if self.slo_ms == 0 {
None
} else {
Some(self.slo_ms)
}
}
pub const fn alpha(&self) -> f64 {
self.alpha
}
pub const fn calibration_window(&self) -> usize {
self.calibration_window
}
#[cfg(test)]
pub fn sample_count(&self) -> usize {
self.latencies_ns.len()
}
pub const fn set_slo_ms(&mut self, slo_ms: u64) {
self.slo_ms = slo_ms;
}
pub fn set_alpha(&mut self, alpha: f64) {
let clamped = if alpha.is_nan() {
DEFAULT_ALPHA
} else if alpha <= 0.0 {
f64::EPSILON
} else if alpha >= 1.0 {
1.0 - f64::EPSILON
} else {
alpha
};
self.alpha = clamped;
self.quantile_cache.set(QuantileCache::Dirty);
}
pub fn set_calibration_window(&mut self, window: usize) {
let window = window.clamp(MIN_CALIBRATION_WINDOW, MAX_CALIBRATION_WINDOW);
self.calibration_window = window;
while self.latencies_ns.len() > window {
self.latencies_ns.pop_front();
}
if self.latencies_ns.capacity() > window.saturating_mul(2) {
self.latencies_ns.shrink_to(window);
}
self.quantile_cache.set(QuantileCache::Dirty);
}
pub fn record_success(&mut self, latency: Duration) {
let ns = u64::try_from(latency.as_nanos()).unwrap_or(u64::MAX);
if self.latencies_ns.len() == self.calibration_window {
self.latencies_ns.pop_front();
}
self.latencies_ns.push_back(ns);
self.quantile_cache.set(QuantileCache::Dirty);
}
pub fn quantile_bound(&self) -> Option<Duration> {
match self.quantile_cache.get() {
QuantileCache::Bound(ns) => return Some(Duration::from_nanos(ns)),
QuantileCache::Unavailable => return None,
QuantileCache::Dirty => {}
}
let bound = self.compute_quantile_bound_ns();
self.quantile_cache.set(match bound {
Some(ns) => QuantileCache::Bound(ns),
None => QuantileCache::Unavailable,
});
bound.map(Duration::from_nanos)
}
fn compute_quantile_bound_ns(&self) -> Option<u64> {
let k = self.latencies_ns.len();
if k < MIN_CALIBRATION_SAMPLES {
return None;
}
let rank = conformal_rank(k, self.alpha)?;
let mut scratch: Vec<u64> = self.latencies_ns.iter().copied().collect();
let (_, pivot, _) = scratch.select_nth_unstable(rank - 1);
Some(*pivot)
}
pub fn slo_budget(&self) -> Option<Duration> {
self.slo_ms().map(Duration::from_millis)
}
pub fn retry_allowed(&self, elapsed: Duration) -> bool {
let Some(budget) = self.slo_budget() else {
return true;
};
let Some(predicted_tail) = self.quantile_bound() else {
return elapsed < budget;
};
let projected = elapsed.saturating_add(predicted_tail);
projected < budget
}
}
fn conformal_rank(k: usize, alpha: f64) -> Option<usize> {
let n = k.checked_add(1)?;
let bits = alpha.clamp(f64::EPSILON, 1.0 - f64::EPSILON).to_bits();
let exponent = (bits >> 52) & 0x7ff;
let significand = (1_u128 << 52) | u128::from(bits & ((1_u64 << 52) - 1));
let scaled = significand * u128::try_from(n).ok()?;
let excluded = usize::try_from(scaled >> (1075 - exponent)).ok()?;
let rank = n.checked_sub(excluded)?;
(rank > 0 && rank <= k).then_some(rank)
}
pub type ConformalRetryBudgetCell = RefCell<ConformalRetryBudget>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_budget_is_disabled() {
let b = ConformalRetryBudget::default();
assert!(b.slo_ms().is_none());
assert!(b.retry_allowed(Duration::from_secs(3600)));
assert_eq!(b.quantile_cache.get(), QuantileCache::Dirty);
}
#[test]
fn quantile_requires_minimum_samples() {
let mut b = ConformalRetryBudget::default();
b.set_alpha(0.2);
for _ in 0..(MIN_CALIBRATION_SAMPLES - 1) {
b.record_success(Duration::from_millis(1));
}
assert!(b.quantile_bound().is_none());
b.record_success(Duration::from_millis(1));
assert!(b.quantile_bound().is_some());
}
#[test]
fn default_confidence_requires_nineteen_samples() {
let mut b = ConformalRetryBudget::default();
b.set_slo_ms(100);
for _ in 0..18 {
b.record_success(Duration::from_millis(50));
assert!(b.quantile_bound().is_none());
assert!(b.retry_allowed(Duration::from_millis(60)));
assert!(!b.retry_allowed(Duration::from_millis(100)));
}
b.record_success(Duration::from_millis(50));
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(50)));
assert!(!b.retry_allowed(Duration::from_millis(60)));
}
#[test]
fn quantile_picks_correct_order_statistic() {
let mut b = ConformalRetryBudget::default();
b.set_alpha(0.2);
for ms in 1u64..=10 {
b.record_success(Duration::from_millis(ms));
}
let q = b.quantile_bound().expect("quantile with K=10");
assert_eq!(q, Duration::from_millis(9));
}
#[test]
fn finite_sample_rank_does_not_round_across_confidence_boundaries() {
let alpha = 0.25_f64;
let below = f64::from_bits(alpha.to_bits() - 1);
let above = f64::from_bits(alpha.to_bits() + 1);
assert_eq!(conformal_rank(15, below), Some(13));
assert_eq!(conformal_rank(15, alpha), Some(12));
assert_eq!(conformal_rank(15, above), Some(12));
let minimum = 0.0625_f64;
assert_eq!(conformal_rank(15, minimum), Some(15));
assert_eq!(conformal_rank(15, f64::from_bits(minimum.to_bits() - 1)), None);
assert_eq!(conformal_rank(MAX_CALIBRATION_WINDOW, f64::EPSILON), None);
}
#[test]
fn finite_sample_ranks_match_exact_binary_fraction_oracle() {
for k in [8_usize, 15, 19, 32, 255, 256, 4096] {
for numerator in 1_u32..1024 {
let alpha = f64::from(numerator) / 1024.0;
let numerator = usize::try_from(numerator).unwrap();
let rank = (k + 1) - (numerator * (k + 1) / 1024);
let expected = (rank <= k).then_some(rank);
assert_eq!(conformal_rank(k, alpha), expected, "k={k} alpha={alpha}");
}
}
}
#[test]
fn set_calibration_window_truncates_oldest() {
let mut b = ConformalRetryBudget::default();
for ms in 1u64..=100 {
b.record_success(Duration::from_millis(ms));
}
assert!(b.quantile_bound().is_some());
b.set_calibration_window(16);
assert_eq!(b.sample_count(), 16);
assert!(b.quantile_bound().is_none(), "16 samples cannot support 95% coverage");
b.set_alpha(0.1);
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(100)));
}
#[test]
fn cached_quantile_is_invalidated_by_samples_alpha_and_window() {
let mut b = ConformalRetryBudget::default();
b.set_alpha(0.2);
b.set_calibration_window(8);
assert!(b.quantile_bound().is_none());
assert_eq!(b.quantile_cache.get(), QuantileCache::Unavailable);
for ms in 1u64..=8 {
b.record_success(Duration::from_millis(ms));
}
assert_eq!(b.quantile_cache.get(), QuantileCache::Dirty);
for _ in 0..100 {
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(8)));
assert_eq!(b.quantile_cache.get(), QuantileCache::Bound(8_000_000));
}
b.record_success(Duration::from_millis(100));
assert_eq!(b.sample_count(), 8);
assert_eq!(b.quantile_cache.get(), QuantileCache::Dirty);
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(100)));
b.set_alpha(0.5);
assert_eq!(b.quantile_cache.get(), QuantileCache::Dirty);
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(6)));
b.set_calibration_window(16);
assert_eq!(b.quantile_cache.get(), QuantileCache::Dirty);
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(6)));
for _ in 0..8 {
b.record_success(Duration::from_millis(200));
}
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(200)));
b.set_calibration_window(8);
assert_eq!(b.quantile_cache.get(), QuantileCache::Dirty);
assert_eq!(b.quantile_bound(), Some(Duration::from_millis(200)));
}
#[test]
fn calibration_window_is_bounded() {
let mut b = ConformalRetryBudget::default();
b.set_calibration_window(0);
assert_eq!(b.calibration_window(), MIN_CALIBRATION_WINDOW);
b.set_calibration_window(usize::MAX);
assert_eq!(b.calibration_window(), MAX_CALIBRATION_WINDOW);
}
#[test]
fn retry_disallowed_when_projected_exceeds_slo() {
let mut b = ConformalRetryBudget::default();
b.set_slo_ms(100);
b.set_alpha(0.1);
for _ in 0..20 {
b.record_success(Duration::from_millis(50));
}
assert!(!b.retry_allowed(Duration::from_millis(60)));
assert!(b.retry_allowed(Duration::from_millis(10)));
}
#[test]
fn alpha_bounds_are_enforced() {
let mut b = ConformalRetryBudget::default();
b.set_alpha(-1.0);
assert!(b.alpha() > 0.0);
b.set_alpha(2.0);
assert!(b.alpha() < 1.0);
b.set_alpha(f64::NAN);
assert!(b.alpha() > 0.0 && b.alpha() < 1.0);
}
#[test]
fn extreme_latencies_and_confidence_remain_bounded() {
let mut b = ConformalRetryBudget::default();
b.set_slo_ms(u64::MAX);
for _ in 0..32 {
b.record_success(Duration::MAX);
}
assert_eq!(b.quantile_bound(), Some(Duration::from_nanos(u64::MAX)));
assert!(!b.retry_allowed(Duration::MAX));
b.set_alpha(f64::NEG_INFINITY);
assert!(b.quantile_bound().is_none());
b.set_alpha(f64::INFINITY);
assert_eq!(b.quantile_bound(), Some(Duration::from_nanos(u64::MAX)));
b.set_alpha(f64::NAN);
assert_eq!(b.quantile_bound(), Some(Duration::from_nanos(u64::MAX)));
b.set_slo_ms(0);
assert!(b.retry_allowed(Duration::MAX));
}
#[test]
fn retry_allowed_when_slo_disabled() {
let mut b = ConformalRetryBudget::default();
for _ in 0..32 {
b.record_success(Duration::from_secs(10));
}
assert!(b.retry_allowed(Duration::from_hours(24)));
}
}