Skip to main content

ant_node/payment/
metrics.rs

1//! Quoting metrics tracking for ant-node.
2//!
3//! Tracks the single piece of state that influences quote pricing today:
4//! the number of records currently stored in the node's close range.
5//! See `payment::pricing::calculate_price` for the formula.
6
7use std::sync::atomic::{AtomicUsize, Ordering};
8
9/// Tracker for quoting metrics.
10///
11/// Holds the `close_records_stored` counter consumed by
12/// [`calculate_price`](crate::payment::pricing::calculate_price).
13#[derive(Debug)]
14pub struct QuotingMetricsTracker {
15    close_records_stored: AtomicUsize,
16}
17
18impl QuotingMetricsTracker {
19    /// Create a new metrics tracker.
20    ///
21    /// # Arguments
22    ///
23    /// * `initial_records` - Initial number of records stored
24    #[must_use]
25    pub fn new(initial_records: usize) -> Self {
26        Self {
27            close_records_stored: AtomicUsize::new(initial_records),
28        }
29    }
30
31    /// Record that a record was stored.
32    pub fn record_store(&self) {
33        self.close_records_stored.fetch_add(1, Ordering::SeqCst);
34    }
35
36    /// Overwrite the counter with an authoritative count of held records.
37    ///
38    /// This is the deletion-aware path and the SINGLE source of truth for the
39    /// priced record count: the handler calls it at quote time with the live
40    /// LMDB entry count (`current_chunks()`), so any record removed from
41    /// storage — by delete, prune, or otherwise — is reflected on the next
42    /// quote with no per-delete bookkeeping to keep in sync. `record_store`
43    /// remains only an optimistic between-quote hint; the resync overwrites it.
44    pub fn set_records(&self, count: usize) {
45        self.close_records_stored.store(count, Ordering::SeqCst);
46    }
47
48    /// Get the number of records stored.
49    #[must_use]
50    pub fn records_stored(&self) -> usize {
51        self.close_records_stored.load(Ordering::SeqCst)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn test_new_tracker() {
61        let tracker = QuotingMetricsTracker::new(50);
62        assert_eq!(tracker.records_stored(), 50);
63    }
64
65    #[test]
66    fn test_record_store_increments_counter() {
67        let tracker = QuotingMetricsTracker::new(0);
68        assert_eq!(tracker.records_stored(), 0);
69
70        tracker.record_store();
71        assert_eq!(tracker.records_stored(), 1);
72
73        tracker.record_store();
74        tracker.record_store();
75        assert_eq!(tracker.records_stored(), 3);
76    }
77
78    #[test]
79    fn test_set_records_resyncs_to_authoritative_count() {
80        let tracker = QuotingMetricsTracker::new(100);
81        assert_eq!(tracker.records_stored(), 100);
82
83        // Resync down (e.g. after deletions/pruning the store now holds fewer).
84        tracker.set_records(42);
85        assert_eq!(tracker.records_stored(), 42);
86
87        // Resync up (e.g. after new stores).
88        tracker.set_records(57);
89        assert_eq!(tracker.records_stored(), 57);
90
91        // Resync to zero (empty store).
92        tracker.set_records(0);
93        assert_eq!(tracker.records_stored(), 0);
94    }
95}