lean_ctx/core/
proxy_cache_attribution.rs1use std::sync::atomic::{AtomicU64, Ordering};
7
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum CacheOutcome {
14 ColdStart,
17 WarmReuse,
20 TtlLapse,
23 PrefixChange,
26}
27
28static COLD_STARTS: AtomicU64 = AtomicU64::new(0);
29static WARM_REUSES: AtomicU64 = AtomicU64::new(0);
30static TTL_LAPSES: AtomicU64 = AtomicU64::new(0);
31static PREFIX_CHANGES: AtomicU64 = AtomicU64::new(0);
32
33pub fn record_outcome(outcome: CacheOutcome) {
35 let counter = match outcome {
36 CacheOutcome::ColdStart => &COLD_STARTS,
37 CacheOutcome::WarmReuse => &WARM_REUSES,
38 CacheOutcome::TtlLapse => &TTL_LAPSES,
39 CacheOutcome::PrefixChange => &PREFIX_CHANGES,
40 };
41 counter.fetch_add(1, Ordering::Relaxed);
42}
43
44#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
46pub struct CacheAttribution {
47 pub cold_starts: u64,
49 pub warm_reuses: u64,
51 pub ttl_lapses: u64,
53 pub prefix_changes: u64,
55}
56
57impl CacheAttribution {
58 #[must_use]
62 pub fn provider_reuse_rate(&self) -> f64 {
63 let eligible = self
64 .warm_reuses
65 .saturating_add(self.ttl_lapses)
66 .saturating_add(self.prefix_changes);
67 if eligible == 0 {
68 0.0
69 } else {
70 self.warm_reuses as f64 * 100.0 / eligible as f64
71 }
72 }
73
74 #[must_use]
75 pub fn eligible_provider_requests(&self) -> u64 {
76 self.warm_reuses
77 .saturating_add(self.ttl_lapses)
78 .saturating_add(self.prefix_changes)
79 }
80}
81
82#[must_use]
83pub fn snapshot() -> CacheAttribution {
84 CacheAttribution {
85 cold_starts: COLD_STARTS.load(Ordering::Relaxed),
86 warm_reuses: WARM_REUSES.load(Ordering::Relaxed),
87 ttl_lapses: TTL_LAPSES.load(Ordering::Relaxed),
88 prefix_changes: PREFIX_CHANGES.load(Ordering::Relaxed),
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn provider_reuse_excludes_cold_starts_from_denominator() {
98 let attribution = CacheAttribution {
99 cold_starts: 99,
100 warm_reuses: 6,
101 ttl_lapses: 2,
102 prefix_changes: 4,
103 };
104
105 assert_eq!(attribution.eligible_provider_requests(), 12);
106 assert_eq!(attribution.provider_reuse_rate(), 50.0);
107 }
108}