nautilus_model/defi/
reporting.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Performance reporting and metrics tracking for blockchain operations.
17
18use std::{fmt::Display, time::Instant};
19
20use thousands::Separable;
21
22#[derive(Debug, Clone)]
23pub enum BlockchainSyncReportItems {
24    Blocks,
25    PoolCreatedEvents,
26    PoolEvents,
27    PoolProfiling,
28}
29
30impl Display for BlockchainSyncReportItems {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        write!(f, "{self:?}")
33    }
34}
35
36/// Tracks performance metrics during block synchronization
37#[derive(Debug, Clone)]
38pub struct BlockchainSyncReporter {
39    item: BlockchainSyncReportItems,
40    start_time: Instant,
41    last_progress_time: Instant,
42    from_block: u64,
43    blocks_processed: u64,
44    blocks_since_last_report: u64,
45    total_blocks: u64,
46    progress_update_interval: u64,
47    next_progress_threshold: u64,
48}
49
50impl BlockchainSyncReporter {
51    /// Creates a new metrics tracker for block synchronization
52    #[must_use]
53    pub fn new(
54        item: BlockchainSyncReportItems,
55        from_block: u64,
56        total_blocks: u64,
57        update_interval: u64,
58    ) -> Self {
59        let now = Instant::now();
60        Self {
61            item,
62            start_time: now,
63            last_progress_time: now,
64            from_block,
65            blocks_processed: 0,
66            blocks_since_last_report: 0,
67            total_blocks,
68            progress_update_interval: update_interval,
69            next_progress_threshold: from_block + update_interval,
70        }
71    }
72
73    /// Updates metrics after a database operation
74    pub fn update(&mut self, batch_size: usize) {
75        self.blocks_processed += batch_size as u64;
76        self.blocks_since_last_report += batch_size as u64;
77    }
78
79    /// Checks if progress should be logged based on the current block number
80    #[must_use]
81    pub fn should_log_progress(&self, block_number: u64, current_block: u64) -> bool {
82        let block_threshold_reached =
83            block_number >= self.next_progress_threshold || block_number >= current_block;
84        // Minimum 1 second between logs
85        let time_threshold_reached = self.last_progress_time.elapsed().as_secs_f64() >= 1.0;
86
87        block_threshold_reached && time_threshold_reached
88    }
89
90    /// Logs current progress with detailed metrics
91    pub fn log_progress(&mut self, block_number: u64) {
92        let elapsed = self.start_time.elapsed();
93        let interval_elapsed = self.last_progress_time.elapsed();
94
95        // Calculate rates - avoid division by zero
96        let avg_rate = if elapsed.as_secs_f64() > 0.0 {
97            self.blocks_processed as f64 / elapsed.as_secs_f64()
98        } else {
99            0.0
100        };
101
102        let current_rate = if interval_elapsed.as_secs_f64() > 0.001 {
103            // Minimum 1ms
104            self.blocks_since_last_report as f64 / interval_elapsed.as_secs_f64()
105        } else {
106            0.0
107        };
108
109        // Calculate progress based on actual block position relative to the sync range
110        let blocks_completed = block_number.saturating_sub(self.from_block);
111        let progress_pct = (blocks_completed as f64 / self.total_blocks as f64 * 100.0).min(100.0);
112
113        tracing::info!(
114            "Syncing {} progress: {:.1}% | Block: {} | Rate: {} blocks/s | Avg: {} blocks/s",
115            self.item,
116            progress_pct,
117            block_number.separate_with_commas(),
118            (current_rate.round() as u64).separate_with_commas(),
119            (avg_rate.round() as u64).separate_with_commas(),
120        );
121
122        self.next_progress_threshold = block_number + self.progress_update_interval;
123        self.last_progress_time = Instant::now();
124        self.blocks_since_last_report = 0;
125    }
126
127    /// Logs final statistics summary
128    pub fn log_final_stats(&self) {
129        let total_elapsed = self.start_time.elapsed();
130        let avg_rate = self.blocks_processed as f64 / total_elapsed.as_secs_f64();
131        tracing::info!(
132            "Finished syncing {} | Total: {} blocks in {:.1}s | Avg rate: {} blocks/s",
133            self.item,
134            self.blocks_processed.separate_with_commas(),
135            total_elapsed.as_secs_f64(),
136            (avg_rate.round() as u64).separate_with_commas(),
137        );
138    }
139}