1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Read-cache reliability telemetry.
//!
//! The subjective "re-reads feel unreliable" signal is turned into data here:
//! every event that wipes or invalidates a *fully-delivered* entry — and thus
//! forces the next read to re-send the whole file instead of the cheap
//! `[unchanged]` stub — increments a process-global counter grouped by cause.
//!
//! Counters are monotonic `AtomicU64`s. They are surfaced ONLY in the
//! `ctx_cache status` diagnostic, never inside a cacheable tool-output body, so
//! output determinism (#498) is preserved.
use std::sync::atomic::{AtomicU64, Ordering};
static COMPACTION: AtomicU64 = AtomicU64::new(0);
static IDLE: AtomicU64 = AtomicU64::new(0);
static EVICTION: AtomicU64 = AtomicU64::new(0);
static CONVERSATION: AtomicU64 = AtomicU64::new(0);
static RAW_CAP_COUNT: AtomicU64 = AtomicU64::new(0);
static RAW_CAP_PREVENTED: AtomicU64 = AtomicU64::new(0);
/// Fully-delivered entries whose delivery flag was reset by a host compaction.
pub(crate) fn record_compaction(n: u64) {
if n > 0 {
COMPACTION.fetch_add(n, Ordering::Relaxed);
}
}
/// Fully-delivered entries dropped by an idle-TTL cache clear.
pub(crate) fn record_idle(n: u64) {
if n > 0 {
IDLE.fetch_add(n, Ordering::Relaxed);
}
}
/// Fully-delivered entries evicted under RAM / token-budget pressure.
pub(crate) fn record_eviction(n: u64) {
if n > 0 {
EVICTION.fetch_add(n, Ordering::Relaxed);
}
}
/// A re-read that fell back to full content because the reading conversation
/// differed from the one the entry was delivered to (conversation scoping, #954).
pub(crate) fn record_conversation_mismatch() {
CONVERSATION.fetch_add(1, Ordering::Relaxed);
}
/// Framed output was larger than raw content — `cap_to_raw()` fell back to
/// verbatim to prevent negative savings. Tracked so the dashboard can
/// distinguish "no savings" from "savings prevented inflation".
pub(crate) fn record_raw_cap(prevented_inflation_tokens: u64) {
RAW_CAP_COUNT.fetch_add(1, Ordering::Relaxed);
RAW_CAP_PREVENTED.fetch_add(prevented_inflation_tokens, Ordering::Relaxed);
}
/// Immutable snapshot of the re-delivery counters.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) struct Snapshot {
pub compaction: u64,
pub idle: u64,
pub eviction: u64,
pub conversation: u64,
pub raw_cap_count: u64,
pub raw_cap_prevented: u64,
}
impl Snapshot {
/// Total forced re-deliveries across all causes.
pub(crate) fn total(&self) -> u64 {
self.compaction
.saturating_add(self.idle)
.saturating_add(self.eviction)
.saturating_add(self.conversation)
}
}
/// Reads the current counters into a consistent snapshot.
pub(crate) fn snapshot() -> Snapshot {
Snapshot {
compaction: COMPACTION.load(Ordering::Relaxed),
idle: IDLE.load(Ordering::Relaxed),
eviction: EVICTION.load(Ordering::Relaxed),
conversation: CONVERSATION.load(Ordering::Relaxed),
raw_cap_count: RAW_CAP_COUNT.load(Ordering::Relaxed),
raw_cap_prevented: RAW_CAP_PREVENTED.load(Ordering::Relaxed),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn total_sums_every_cause() {
let s = Snapshot {
compaction: 1,
idle: 2,
eviction: 4,
conversation: 8,
raw_cap_count: 0,
raw_cap_prevented: 0,
};
assert_eq!(s.total(), 15);
}
#[test]
fn zero_is_a_noop() {
// A zero-sized event must never move a counter (avoids logging "0 wiped").
let before = snapshot();
record_compaction(0);
record_idle(0);
record_eviction(0);
// Only `>=` is safe to assert on a process-global counter: other tests
// may increment concurrently. A 0-sized call adds nothing of its own.
let after = snapshot();
assert!(after.compaction >= before.compaction);
assert!(after.idle >= before.idle);
assert!(after.eviction >= before.eviction);
}
#[test]
fn each_cause_increments_monotonically() {
let before = snapshot();
record_compaction(2);
record_idle(3);
record_eviction(5);
record_conversation_mismatch();
let after = snapshot();
// Monotonic deltas (`>=`) tolerate concurrent increments from sibling
// tests while still proving each recorder targets the right counter.
assert!(after.compaction >= before.compaction + 2);
assert!(after.idle >= before.idle + 3);
assert!(after.eviction >= before.eviction + 5);
assert!(after.conversation > before.conversation);
assert!(after.total() >= before.total() + 11);
}
}