Skip to main content

lean_ctx_proxy/
prefix_cache_stats.rs

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