use std::sync::atomic::{AtomicU32, AtomicU64, AtomicU8, Ordering};
use crate::limiter::window::SlidingWindow;
pub(crate) const STATE_HEALTHY: u8 = 0;
pub(crate) const STATE_DEAD: u8 = 1;
pub(crate) struct KeyInner {
pub(crate) key: String,
pub(crate) label: String,
pub(crate) state: AtomicU8,
pub(crate) cool_down_until: AtomicU64,
pub(crate) consecutive_failures: AtomicU32,
pub(crate) failure_cool_down_until: AtomicU64,
pub(crate) tpm_inflight: AtomicU32,
pub(crate) tpm_limit: u32,
pub(crate) rpm_window: SlidingWindow,
}
impl KeyInner {
pub(crate) fn new(key: String, label: String, tpm_limit: u32, rpm_limit: u32) -> Self {
Self {
key,
label,
state: AtomicU8::new(STATE_HEALTHY),
cool_down_until: AtomicU64::new(0),
consecutive_failures: AtomicU32::new(0),
failure_cool_down_until: AtomicU64::new(0),
tpm_inflight: AtomicU32::new(0),
tpm_limit,
rpm_window: SlidingWindow::new(rpm_limit, std::time::Duration::from_secs(60)),
}
}
pub(crate) fn is_available(&self) -> bool {
if self.state.load(Ordering::Acquire) == STATE_DEAD {
return false;
}
let now = now_millis();
let rl_until = self.cool_down_until.load(Ordering::Acquire);
if rl_until != 0 && now < rl_until {
return false;
}
if rl_until != 0 {
let _ = self.cool_down_until.compare_exchange(
rl_until,
0,
Ordering::AcqRel,
Ordering::Relaxed,
);
}
let cb_until = self.failure_cool_down_until.load(Ordering::Acquire);
if cb_until != 0 && now < cb_until {
return false;
}
if cb_until != 0 {
let _ = self.failure_cool_down_until.compare_exchange(
cb_until,
0,
Ordering::AcqRel,
Ordering::Relaxed,
);
}
true
}
}
#[inline]
pub(crate) fn now_millis() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}