1use std::sync::atomic::{AtomicU64, Ordering};
2use std::time::Duration;
3
4#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
5#[non_exhaustive]
6pub struct ReaderStatistics {
11 cursors_created: u64,
12 segment_parses: u64,
13 segment_handle_opens: u64,
14 segment_handle_reopens: u64,
15 table_checksum_bytes: u64,
16 table_checksum_nanos: u64,
17 chunk_cache_hits: u64,
18 chunk_cache_misses: u64,
19 table_page_cache_hits: u64,
20 table_page_cache_misses: u64,
21 encoded_bytes_read: u64,
22 decoded_bytes: u64,
23 decompression_nanos: u64,
24}
25
26impl ReaderStatistics {
27 pub fn cursors_created(&self) -> u64 {
29 self.cursors_created
30 }
31
32 pub fn segment_parses(&self) -> u64 {
34 self.segment_parses
35 }
36
37 pub fn segment_handle_opens(&self) -> u64 {
39 self.segment_handle_opens
40 }
41
42 pub fn segment_handle_reopens(&self) -> u64 {
44 self.segment_handle_reopens
45 }
46
47 pub fn table_checksum_bytes(&self) -> u64 {
49 self.table_checksum_bytes
50 }
51
52 pub fn table_checksum_nanos(&self) -> u64 {
54 self.table_checksum_nanos
55 }
56
57 pub fn chunk_cache_hits(&self) -> u64 {
59 self.chunk_cache_hits
60 }
61
62 pub fn chunk_cache_misses(&self) -> u64 {
64 self.chunk_cache_misses
65 }
66
67 pub fn table_page_cache_hits(&self) -> u64 {
69 self.table_page_cache_hits
70 }
71
72 pub fn table_page_cache_misses(&self) -> u64 {
74 self.table_page_cache_misses
75 }
76
77 pub fn encoded_bytes_read(&self) -> u64 {
79 self.encoded_bytes_read
80 }
81
82 pub fn decoded_bytes(&self) -> u64 {
84 self.decoded_bytes
85 }
86
87 pub fn decompression_nanos(&self) -> u64 {
89 self.decompression_nanos
90 }
91
92 #[must_use]
94 pub fn saturating_delta(self, earlier: Self) -> Self {
95 Self {
96 cursors_created: self.cursors_created.saturating_sub(earlier.cursors_created),
97 segment_parses: self.segment_parses.saturating_sub(earlier.segment_parses),
98 segment_handle_opens: self
99 .segment_handle_opens
100 .saturating_sub(earlier.segment_handle_opens),
101 segment_handle_reopens: self
102 .segment_handle_reopens
103 .saturating_sub(earlier.segment_handle_reopens),
104 table_checksum_bytes: self
105 .table_checksum_bytes
106 .saturating_sub(earlier.table_checksum_bytes),
107 table_checksum_nanos: self
108 .table_checksum_nanos
109 .saturating_sub(earlier.table_checksum_nanos),
110 chunk_cache_hits: self
111 .chunk_cache_hits
112 .saturating_sub(earlier.chunk_cache_hits),
113 chunk_cache_misses: self
114 .chunk_cache_misses
115 .saturating_sub(earlier.chunk_cache_misses),
116 table_page_cache_hits: self
117 .table_page_cache_hits
118 .saturating_sub(earlier.table_page_cache_hits),
119 table_page_cache_misses: self
120 .table_page_cache_misses
121 .saturating_sub(earlier.table_page_cache_misses),
122 encoded_bytes_read: self
123 .encoded_bytes_read
124 .saturating_sub(earlier.encoded_bytes_read),
125 decoded_bytes: self.decoded_bytes.saturating_sub(earlier.decoded_bytes),
126 decompression_nanos: self
127 .decompression_nanos
128 .saturating_sub(earlier.decompression_nanos),
129 }
130 }
131}
132
133#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
134#[non_exhaustive]
135pub struct ReaderCacheInfo {
137 chunk_cache_capacity: u64,
138 table_entry_cache_capacity: u64,
139 table_entry_cache_current: u64,
140 table_entry_cache_peak: u64,
141}
142
143impl ReaderCacheInfo {
144 pub(crate) fn new(
145 chunk_cache_capacity_bytes: u64,
146 table_entry_cache_capacity_bytes: usize,
147 table_entry_cache_current_bytes: usize,
148 table_entry_cache_peak_bytes: usize,
149 ) -> Self {
150 Self {
151 chunk_cache_capacity: chunk_cache_capacity_bytes,
152 table_entry_cache_capacity: usize_to_u64(table_entry_cache_capacity_bytes),
153 table_entry_cache_current: usize_to_u64(table_entry_cache_current_bytes),
154 table_entry_cache_peak: usize_to_u64(table_entry_cache_peak_bytes),
155 }
156 }
157
158 pub fn chunk_cache_capacity_bytes(&self) -> u64 {
160 self.chunk_cache_capacity
161 }
162
163 pub fn table_entry_cache_capacity_bytes(&self) -> u64 {
165 self.table_entry_cache_capacity
166 }
167
168 pub fn table_entry_cache_current_bytes(&self) -> u64 {
170 self.table_entry_cache_current
171 }
172
173 pub fn table_entry_cache_peak_bytes(&self) -> u64 {
175 self.table_entry_cache_peak
176 }
177}
178
179#[derive(Debug, Default)]
180pub(crate) struct ReaderStatisticsCollector {
181 enabled: bool,
182 cursors_created: AtomicU64,
183 segment_parses: AtomicU64,
184 segment_handle_opens: AtomicU64,
185 segment_handle_reopens: AtomicU64,
186 table_checksum_bytes: AtomicU64,
187 table_checksum_nanos: AtomicU64,
188 chunk_cache_hits: AtomicU64,
189 chunk_cache_misses: AtomicU64,
190 table_page_cache_hits: AtomicU64,
191 table_page_cache_misses: AtomicU64,
192 encoded_bytes_read: AtomicU64,
193 decoded_bytes: AtomicU64,
194 decompression_nanos: AtomicU64,
195}
196
197impl ReaderStatisticsCollector {
198 pub(crate) fn new(enabled: bool) -> Self {
199 Self {
200 enabled,
201 ..Self::default()
202 }
203 }
204
205 pub(crate) fn enabled(&self) -> bool {
206 self.enabled
207 }
208
209 pub(crate) fn snapshot(&self) -> Option<ReaderStatistics> {
210 self.enabled.then(|| ReaderStatistics {
211 cursors_created: self.cursors_created.load(Ordering::Relaxed),
212 segment_parses: self.segment_parses.load(Ordering::Relaxed),
213 segment_handle_opens: self.segment_handle_opens.load(Ordering::Relaxed),
214 segment_handle_reopens: self.segment_handle_reopens.load(Ordering::Relaxed),
215 table_checksum_bytes: self.table_checksum_bytes.load(Ordering::Relaxed),
216 table_checksum_nanos: self.table_checksum_nanos.load(Ordering::Relaxed),
217 chunk_cache_hits: self.chunk_cache_hits.load(Ordering::Relaxed),
218 chunk_cache_misses: self.chunk_cache_misses.load(Ordering::Relaxed),
219 table_page_cache_hits: self.table_page_cache_hits.load(Ordering::Relaxed),
220 table_page_cache_misses: self.table_page_cache_misses.load(Ordering::Relaxed),
221 encoded_bytes_read: self.encoded_bytes_read.load(Ordering::Relaxed),
222 decoded_bytes: self.decoded_bytes.load(Ordering::Relaxed),
223 decompression_nanos: self.decompression_nanos.load(Ordering::Relaxed),
224 })
225 }
226
227 pub(crate) fn record_cursor_created(&self) {
228 self.add(&self.cursors_created, 1);
229 }
230
231 pub(crate) fn record_segment_parse(&self) {
232 self.add(&self.segment_parses, 1);
233 }
234
235 pub(crate) fn record_segment_handle_open(&self, count: usize) {
236 self.add(&self.segment_handle_opens, usize_to_u64(count));
237 }
238
239 pub(crate) fn record_segment_handle_reopen(&self) {
240 self.add(&self.segment_handle_reopens, 1);
241 }
242
243 pub(crate) fn record_table_checksum(&self, bytes: u64, elapsed: Duration) {
244 self.add(&self.table_checksum_bytes, bytes);
245 self.add(&self.table_checksum_nanos, duration_nanos(elapsed));
246 }
247
248 pub(crate) fn record_chunk_cache_access(&self, hit: bool) {
249 if hit {
250 self.add(&self.chunk_cache_hits, 1);
251 } else {
252 self.add(&self.chunk_cache_misses, 1);
253 }
254 }
255
256 pub(crate) fn record_table_page_cache_access(&self, hit: bool) {
257 if hit {
258 self.add(&self.table_page_cache_hits, 1);
259 } else {
260 self.add(&self.table_page_cache_misses, 1);
261 }
262 }
263
264 pub(crate) fn record_encoded_bytes_read(&self, bytes: u64) {
265 self.add(&self.encoded_bytes_read, bytes);
266 }
267
268 pub(crate) fn record_decoded_bytes(&self, bytes: usize) {
269 self.add(&self.decoded_bytes, usize_to_u64(bytes));
270 }
271
272 pub(crate) fn record_decompression(&self, elapsed: Duration) {
273 self.add(&self.decompression_nanos, duration_nanos(elapsed));
274 }
275
276 fn add(&self, counter: &AtomicU64, value: u64) {
277 if !self.enabled || value == 0 {
278 return;
279 }
280 let _ = counter.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
281 Some(current.saturating_add(value))
282 });
283 }
284}
285
286fn duration_nanos(duration: Duration) -> u64 {
287 u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX)
288}
289
290fn usize_to_u64(value: usize) -> u64 {
291 u64::try_from(value).unwrap_or(u64::MAX)
292}