pedant_graph/cache/stats.rs
1//! What one cache did, counted once per examined or dropped entry.
2//!
3//! Statistics describe cache work alone. Nothing here enters graph identity,
4//! record order, serialization, or a query answer: a counter is read by a host
5//! deciding whether its retention budget is worth the memory, and by the proofs
6//! that hold this crate to reusing only what it claims to reuse.
7//!
8//! The reading is a copied value. The live counts it is taken from belong to
9//! [`super::state`], so a host reads a number that cannot move under it.
10
11/// One category's three cumulative counts, as a copied reading.
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub(crate) struct CategoryCounts {
14 hits: u64,
15 misses: u64,
16 evictions: u64,
17}
18
19impl CategoryCounts {
20 /// One category's three counts, read together.
21 pub(crate) fn new(hits: u64, misses: u64, evictions: u64) -> Self {
22 Self {
23 hits,
24 misses,
25 evictions,
26 }
27 }
28}
29
30/// A copied reading of one cache's cumulative activity.
31///
32/// Counters saturate rather than wrap, so a long-lived host reads a ceiling
33/// instead of a count that silently restarted.
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub struct GraphCacheStats {
36 source_projections: CategoryCounts,
37 exact_graphs: CategoryCounts,
38 selected_indexes: CategoryCounts,
39 derived_products: CategoryCounts,
40}
41
42impl GraphCacheStats {
43 /// One reading, taken across all four categories at once.
44 pub(crate) fn new(
45 source_projections: CategoryCounts,
46 exact_graphs: CategoryCounts,
47 selected_indexes: CategoryCounts,
48 derived_products: CategoryCounts,
49 ) -> Self {
50 Self {
51 source_projections,
52 exact_graphs,
53 selected_indexes,
54 derived_products,
55 }
56 }
57
58 /// Source-unit projections reused from retained state.
59 pub fn source_projection_hits(self) -> u64 {
60 self.source_projections.hits
61 }
62
63 /// Source-unit projections that had to be rebuilt.
64 pub fn source_projection_misses(self) -> u64 {
65 self.source_projections.misses
66 }
67
68 /// Source-unit projections dropped to stay within their ceiling.
69 pub fn source_projection_evictions(self) -> u64 {
70 self.source_projections.evictions
71 }
72
73 /// Exact graphs answered from retained state.
74 pub fn exact_graph_hits(self) -> u64 {
75 self.exact_graphs.hits
76 }
77
78 /// Exact graphs that had to be built.
79 pub fn exact_graph_misses(self) -> u64 {
80 self.exact_graphs.misses
81 }
82
83 /// Exact graphs dropped to stay within their ceiling.
84 pub fn exact_graph_evictions(self) -> u64 {
85 self.exact_graphs.evictions
86 }
87
88 /// Edge-selection index sets answered from retained state.
89 pub fn selected_index_hits(self) -> u64 {
90 self.selected_indexes.hits
91 }
92
93 /// Edge-selection index sets that had to be built.
94 pub fn selected_index_misses(self) -> u64 {
95 self.selected_indexes.misses
96 }
97
98 /// Edge-selection index sets dropped to stay within their ceiling.
99 pub fn selected_index_evictions(self) -> u64 {
100 self.selected_indexes.evictions
101 }
102
103 /// Derived products answered from retained state.
104 pub fn derived_product_hits(self) -> u64 {
105 self.derived_products.hits
106 }
107
108 /// Derived products that had to be computed.
109 pub fn derived_product_misses(self) -> u64 {
110 self.derived_products.misses
111 }
112
113 /// Derived products dropped to stay within their ceiling.
114 pub fn derived_product_evictions(self) -> u64 {
115 self.derived_products.evictions
116 }
117}