Skip to main content

reifydb_core/metrics/
scan.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::cell::Cell;
5
6thread_local! {
7	static FETCHED: Cell<u64> = const { Cell::new(0) };
8	static TOMBSTONES: Cell<u64> = const { Cell::new(0) };
9}
10
11pub fn record_page(fetched: u64, tombstones: u64) {
12	FETCHED.with(|c| c.set(c.get().wrapping_add(fetched)));
13	TOMBSTONES.with(|c| c.set(c.get().wrapping_add(tombstones)));
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct ScanCounters {
18	pub fetched: u64,
19	pub tombstones: u64,
20}
21
22impl ScanCounters {
23	pub fn sample() -> Self {
24		Self {
25			fetched: FETCHED.with(|c| c.get()),
26			tombstones: TOMBSTONES.with(|c| c.get()),
27		}
28	}
29
30	pub fn since(self) -> Self {
31		let now = Self::sample();
32		Self {
33			fetched: now.fetched.wrapping_sub(self.fetched),
34			tombstones: now.tombstones.wrapping_sub(self.tombstones),
35		}
36	}
37}
38
39#[cfg(test)]
40mod tests {
41	use super::*;
42
43	#[test]
44	fn since_reports_only_what_the_caller_bracketed() {
45		// The counter is process-lifetime and shared by every scan on this thread, so a call site
46		// can only attribute the delta across its own execution. Reading the absolute value would
47		// bill each site for every scan that ran before it.
48		record_page(100, 90);
49		let before = ScanCounters::sample();
50		record_page(7, 3);
51		let delta = before.since();
52
53		assert_eq!(delta.fetched, 7, "rows fetched before the bracket must not be attributed to it");
54		assert_eq!(delta.tombstones, 3);
55	}
56
57	#[test]
58	fn a_bracket_with_no_scan_reports_nothing() {
59		record_page(5, 5);
60		let before = ScanCounters::sample();
61
62		assert_eq!(
63			before.since(),
64			ScanCounters {
65				fetched: 0,
66				tombstones: 0
67			}
68		);
69	}
70}