fast_telemetry/metric/gauge.rs
1//! Atomic gauge for point-in-time values.
2
3use crossbeam_utils::CachePadded;
4use std::sync::atomic::{AtomicI64, Ordering};
5
6/// A cache-padded atomic gauge for point-in-time measurements.
7///
8/// Gauges represent a current value that is periodically sampled and set,
9/// such as memory usage, queue depth, or progress percentage.
10///
11/// # Why Only `set()` and `get()`?
12///
13/// This type intentionally omits `add()`/`inc()`/`dec()` methods. If you need
14/// to increment or decrement a value from multiple threads (e.g., tracking
15/// active connections), use [`Counter`] instead - it's thread-sharded for
16/// contention-free concurrent increments.
17///
18/// Providing increment methods here would create a footgun: the API would look
19/// convenient for counting, but the single-atomic implementation would cause
20/// cache-line contention under concurrent writes. By limiting the API to
21/// `set()`/`get()`, we make the intended usage pattern clear: periodic
22/// point-in-time snapshots from a single writer.
23///
24/// # Why Not Thread-Sharded?
25///
26/// Thread-sharding works for `Counter` because addition is commutative — you
27/// can sum the shards to get the total. This includes subtraction (adding
28/// negative values): the shard sums still produce the correct aggregate.
29/// `set()` is not commutative; there's no meaningful way to aggregate "last
30/// value written" across shards.
31///
32/// The cache padding prevents false sharing if this gauge is stored adjacent
33/// to frequently-accessed data.
34///
35/// [`Counter`]: crate::Counter
36pub struct Gauge {
37 value: CachePadded<AtomicI64>,
38}
39
40impl Gauge {
41 /// Create a new gauge initialized to zero.
42 pub fn new() -> Self {
43 Self {
44 value: CachePadded::new(AtomicI64::new(0)),
45 }
46 }
47
48 /// Create a new gauge with an initial value.
49 pub fn with_value(initial: i64) -> Self {
50 Self {
51 value: CachePadded::new(AtomicI64::new(initial)),
52 }
53 }
54
55 /// Set the gauge to a value.
56 #[inline]
57 pub fn set(&self, value: i64) {
58 self.value.store(value, Ordering::Relaxed);
59 }
60
61 /// Get the current value.
62 #[inline]
63 pub fn get(&self) -> i64 {
64 self.value.load(Ordering::Relaxed)
65 }
66}
67
68impl Default for Gauge {
69 fn default() -> Self {
70 Self::new()
71 }
72}