Publish harness and TUI open-source
initial sync from the monorepo
This commit is contained in:
commit
c68e39f604
2734 changed files with 1437016 additions and 0 deletions
76
crates/common/xai-circuit-breaker/src/window.rs
Normal file
76
crates/common/xai-circuit-breaker/src/window.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//! Bounded sliding window over `(timestamp, is_failure)` samples.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// Safety cap on sliding window entries to bound memory under sustained
|
||||
/// high load (e.g. 10K req/s * 60s window would otherwise reach 600K
|
||||
/// entries).
|
||||
pub(crate) const MAX_WINDOW_ENTRIES: usize = 10_000;
|
||||
|
||||
pub(crate) struct SlidingWindow {
|
||||
entries: VecDeque<(Instant, bool)>,
|
||||
/// Incremental count of `is_failure = true` entries currently in
|
||||
/// `entries`. Maintained on push/pop so `error_rate()` is O(1)
|
||||
/// instead of O(n) — avoids a per-request hot-path scan under
|
||||
/// the breaker mutex.
|
||||
failures: usize,
|
||||
}
|
||||
|
||||
impl SlidingWindow {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
entries: VecDeque::new(),
|
||||
failures: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn push(&mut self, is_failure: bool, at: Instant) {
|
||||
if self.entries.len() >= MAX_WINDOW_ENTRIES
|
||||
&& let Some((_, was_failure)) = self.entries.pop_front()
|
||||
&& was_failure
|
||||
{
|
||||
self.failures -= 1;
|
||||
}
|
||||
self.entries.push_back((at, is_failure));
|
||||
if is_failure {
|
||||
self.failures += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn evict(&mut self, window: Duration, now: Instant) {
|
||||
let Some(cutoff) = now.checked_sub(window) else {
|
||||
return;
|
||||
};
|
||||
while let Some(&(ts, was_failure)) = self.entries.front() {
|
||||
if ts < cutoff {
|
||||
self.entries.pop_front();
|
||||
if was_failure {
|
||||
self.failures -= 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn error_rate(&self) -> f64 {
|
||||
if self.entries.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
self.failures as f64 / self.entries.len() as f64
|
||||
}
|
||||
|
||||
pub(crate) fn sample_count(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.entries.clear();
|
||||
self.failures = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "window_tests.rs"]
|
||||
mod tests;
|
||||
Loading…
Reference in a new issue