Skip to main content

liquid_cache/cache/observer/
stats.rs

1use std::fmt;
2use std::sync::atomic::{AtomicU64, Ordering};
3
4/// Macro to define runtime statistics metrics.
5///
6/// Usage:
7/// ```ignore
8/// define_runtime_stats! {
9///     (field_name, "doc comment", method_name),
10///     ...
11/// }
12/// ```
13///
14/// This generates:
15/// - Fields in `RuntimeStats` struct
16/// - Fields in `RuntimeStatsSnapshot` struct
17/// - Increment methods (`incr_*`)
18/// - `consume_snapshot` implementation
19/// - `reset` implementation
20macro_rules! define_runtime_stats {
21    (
22        $(
23            ($field:ident, $doc:literal, $method:ident)
24        ),* $(,)?
25    ) => {
26        /// Atomic runtime counters for cache API calls.
27        #[derive(Debug, Default)]
28        pub struct RuntimeStats {
29            $(
30                #[doc = $doc]
31                pub(crate) $field: AtomicU64,
32            )*
33        }
34
35        /// Immutable snapshot of [`RuntimeStats`].
36        #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
37        pub struct RuntimeStatsSnapshot {
38            $(
39                #[doc = concat!("Total ", stringify!($field), ".")]
40                pub $field: u64,
41            )*
42        }
43
44        impl RuntimeStats {
45            /// Return an immutable snapshot of the current runtime counters and reset the stats to 0.
46            pub fn consume_snapshot(&self) -> RuntimeStatsSnapshot {
47                let v = RuntimeStatsSnapshot {
48                    $(
49                        $field: self.$field.load(Ordering::Relaxed),
50                    )*
51                };
52                self.reset();
53                v
54            }
55
56            $(
57                /// Increment counter.
58                #[inline]
59                pub fn $method(&self) {
60                    self.$field.fetch_add(1, Ordering::Relaxed);
61                }
62            )*
63
64            /// Reset the runtime stats to 0.
65            pub fn reset(&self) {
66                $(
67                    self.$field.store(0, Ordering::Relaxed);
68                )*
69            }
70        }
71
72        impl fmt::Display for RuntimeStats {
73            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74                writeln!(f, "RuntimeStats:")?;
75                $(
76                    writeln!(f, "  {}: {}", stringify!($field), self.$field.load(Ordering::Relaxed))?;
77                )*
78                Ok(())
79            }
80        }
81
82        impl fmt::Display for RuntimeStatsSnapshot {
83            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84                writeln!(f, "RuntimeStatsSnapshot:")?;
85                $(
86                    writeln!(f, "  {}: {}", stringify!($field), self.$field)?;
87                )*
88                Ok(())
89            }
90        }
91    };
92}
93
94// Define all runtime statistics metrics here.
95// To add a new metric, add a line: (field_name, "doc comment", method_name)
96define_runtime_stats! {
97    (get, "Number of `get` calls issued via `CachedData`.", incr_get),
98    (get_with_selection, "Number of `get_with_selection` calls issued via `CachedData`.", incr_get_with_selection),
99    (eval_predicate, "Number of `eval_predicate` calls issued via `CachedData`.", incr_eval_predicate),
100    (get_squeezed_success, "Number of Squeezed-Liquid full evaluations finished without IO.", incr_get_squeezed_success),
101    (get_squeezed_needs_io, "Number of Squeezed-Liquid full paths that required IO.", incr_get_squeezed_needs_io),
102    (try_read_liquid_calls, "Number of `try_read_liquid` calls issued via `CachedData`.", incr_try_read_liquid),
103    (hit_date32_expression_calls, "Number of `hit_date32_expression` calls.", incr_hit_date32_expression),
104    (read_io_count, "Number of read IO operations.", incr_read_io_count),
105    (write_io_count, "Number of write IO operations.", incr_write_io_count),
106    (disk_evictions, "Number of disk cache entries evicted.", incr_disk_evictions),
107    (disk_reservation_failures, "Number of failed disk budget reservations.", incr_disk_reservation_failures),
108    (eval_predicate_on_liquid_failed, "Number of `eval_predicate` calls that failed on Liquid array.", incr_eval_predicate_on_liquid_failed),
109    (squeezed_decompressed_count, "Number of decompressed Squeezed-Liquid entries.", __incr_squeezed_decompressed_count),
110    (squeezed_total_count, "Total number of Squeezed-Liquid entries.", __incr_squeezed_total_count),
111    (squeeze_io_saved, "Number of io saved by squeezing.", incr_squeeze_io_saved),
112}
113
114impl RuntimeStats {
115    /// Track the number of decompressed Squeezed-Liquid entries.
116    pub fn track_decompress_squeezed_count(&self, decompressed: usize, total: usize) {
117        self.squeezed_decompressed_count
118            .fetch_add(decompressed as u64, Ordering::Relaxed);
119        self.squeezed_total_count
120            .fetch_add(total as u64, Ordering::Relaxed);
121    }
122}
123
124/// Snapshot of cache statistics.
125#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
126pub struct CacheStats {
127    /// Total number of entries in the cache.
128    pub total_entries: usize,
129    /// Number of in-memory Arrow entries.
130    pub memory_arrow_entries: usize,
131    /// Number of in-memory Liquid entries.
132    pub memory_liquid_entries: usize,
133    /// Number of in-memory Squeezed-Liquid entries.
134    pub memory_squeezed_liquid_entries: usize,
135    /// Number of on-disk Liquid entries.
136    pub disk_liquid_entries: usize,
137    /// Number of on-disk Arrow entries.
138    pub disk_arrow_entries: usize,
139    /// Total size of in-memory Arrow entries in bytes.
140    pub memory_arrow_bytes: usize,
141    /// Total size of in-memory Liquid entries in bytes.
142    pub memory_liquid_bytes: usize,
143    /// Total size of in-memory Squeezed-Liquid entries in bytes.
144    pub memory_squeezed_liquid_bytes: usize,
145    /// Total memory usage of the cache.
146    pub memory_usage_bytes: usize,
147    /// Total disk usage of the cache.
148    pub disk_usage_bytes: usize,
149    /// Maximum memory size.
150    pub max_memory_bytes: usize,
151    /// Maximum disk size.
152    pub max_disk_bytes: usize,
153    /// Runtime counters snapshot.
154    pub runtime: RuntimeStatsSnapshot,
155}