Skip to main content

lean_ctx/proxy/
prefix_cache_stats.rs

1//! Structured prefix-cache statistics for the `/status` endpoint.
2//!
3//! Aggregates signals from cache modules into a single `prefix_cache` block:
4//! hit/miss counts, alignment score, frozen message counts, delta compression
5//! ratios, replay metrics, and Headroom-compat counts.
6
7use std::sync::atomic::{AtomicU64, Ordering};
8
9use serde::{Deserialize, Serialize};
10
11static PREFIX_HITS: AtomicU64 = AtomicU64::new(0);
12static PREFIX_MISSES: AtomicU64 = AtomicU64::new(0);
13static FROZEN_MSG_SUM: AtomicU64 = AtomicU64::new(0);
14static FROZEN_MSG_COUNT: AtomicU64 = AtomicU64::new(0);
15static DELTA_BYTES_ORIGINAL: AtomicU64 = AtomicU64::new(0);
16static DELTA_BYTES_COMPRESSED: AtomicU64 = AtomicU64::new(0);
17static REPLAY_HITS: AtomicU64 = AtomicU64::new(0);
18static REPLAY_MISSES: AtomicU64 = AtomicU64::new(0);
19static STICKY_INJECTIONS: AtomicU64 = AtomicU64::new(0);
20static HEADROOM_COMPAT_REQUESTS: AtomicU64 = AtomicU64::new(0);
21
22pub fn record_hit() {
23    PREFIX_HITS.fetch_add(1, Ordering::Relaxed);
24}
25
26pub fn record_miss() {
27    PREFIX_MISSES.fetch_add(1, Ordering::Relaxed);
28}
29
30pub fn record_frozen_count(count: u64) {
31    FROZEN_MSG_SUM.fetch_add(count, Ordering::Relaxed);
32    FROZEN_MSG_COUNT.fetch_add(1, Ordering::Relaxed);
33}
34
35pub fn record_delta(original: u64, compressed: u64) {
36    DELTA_BYTES_ORIGINAL.fetch_add(original, Ordering::Relaxed);
37    DELTA_BYTES_COMPRESSED.fetch_add(compressed, Ordering::Relaxed);
38}
39
40pub fn record_replay_hit() {
41    REPLAY_HITS.fetch_add(1, Ordering::Relaxed);
42}
43
44pub fn record_replay_miss() {
45    REPLAY_MISSES.fetch_add(1, Ordering::Relaxed);
46}
47
48pub fn record_sticky_injection() {
49    STICKY_INJECTIONS.fetch_add(1, Ordering::Relaxed);
50}
51
52pub fn record_headroom_compat() {
53    HEADROOM_COMPAT_REQUESTS.fetch_add(1, Ordering::Relaxed);
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
57pub struct PrefixCacheStats {
58    pub hits: u64,
59    pub misses: u64,
60    pub hit_rate: f64,
61    pub alignment_score: u64,
62    pub frozen_message_count_avg: f64,
63    pub delta_compression_ratio: f64,
64    pub replay_hits: u64,
65    pub replay_misses: u64,
66    pub sticky_tool_injections: u64,
67    pub headroom_compat_requests: u64,
68}
69
70fn alignment_score(volatile_detected: u64, requests_scanned: u64) -> u64 {
71    if requests_scanned == 0 {
72        return 100;
73    }
74    let avg = volatile_detected as f64 / requests_scanned as f64;
75    100u64.saturating_sub((avg * 10.0).round() as u64)
76}
77
78#[must_use]
79pub fn snapshot() -> PrefixCacheStats {
80    let hits = PREFIX_HITS.load(Ordering::Relaxed);
81    let misses = PREFIX_MISSES.load(Ordering::Relaxed);
82    let total = hits + misses;
83    let hit_rate = if total > 0 {
84        hits as f64 / total as f64
85    } else {
86        1.0
87    };
88
89    let frozen_sum = FROZEN_MSG_SUM.load(Ordering::Relaxed);
90    let frozen_count = FROZEN_MSG_COUNT.load(Ordering::Relaxed);
91    let frozen_avg = if frozen_count > 0 {
92        frozen_sum as f64 / frozen_count as f64
93    } else {
94        0.0
95    };
96
97    let d_orig = DELTA_BYTES_ORIGINAL.load(Ordering::Relaxed);
98    let d_comp = DELTA_BYTES_COMPRESSED.load(Ordering::Relaxed);
99    let delta_ratio = if d_orig > 0 {
100        d_comp as f64 / d_orig as f64
101    } else {
102        0.0
103    };
104
105    let cs = super::cache_safety::snapshot();
106    let score = alignment_score(cs.volatile_fields_detected, cs.volatile_system_requests);
107
108    PrefixCacheStats {
109        hits,
110        misses,
111        hit_rate,
112        alignment_score: score,
113        frozen_message_count_avg: frozen_avg,
114        delta_compression_ratio: delta_ratio,
115        replay_hits: REPLAY_HITS.load(Ordering::Relaxed),
116        replay_misses: REPLAY_MISSES.load(Ordering::Relaxed),
117        sticky_tool_injections: STICKY_INJECTIONS.load(Ordering::Relaxed),
118        headroom_compat_requests: HEADROOM_COMPAT_REQUESTS.load(Ordering::Relaxed),
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn alignment_score_perfect_when_no_volatiles() {
128        assert_eq!(alignment_score(0, 100), 100);
129    }
130
131    #[test]
132    fn alignment_score_degrades_with_volatiles() {
133        assert_eq!(alignment_score(10, 10), 90);
134        assert_eq!(alignment_score(50, 10), 50);
135    }
136
137    #[test]
138    fn alignment_score_clamps_to_zero() {
139        assert_eq!(alignment_score(1000, 10), 0);
140    }
141
142    #[test]
143    fn alignment_score_is_100_when_no_requests() {
144        assert_eq!(alignment_score(0, 0), 100);
145    }
146}