linkrs_metrics/manager/
checkpoint.rs1use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::time::Duration;
5
6use super::core::StatsManager;
7use super::metric_type::MetricType;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CheckpointSnapshot {
12 pub trigger_count: u64,
13 pub success_count: u64,
14 pub failure_count: u64,
15 pub avg_duration_us: f64,
16 pub data_flushed_bytes: u64,
17 pub wal_files_truncated: u64,
18 pub triggered_by_wal_size: u64,
19 pub triggered_by_interval: u64,
20 pub triggered_explicit: u64,
21 pub deduplicated: u64,
22 pub blocked: u64,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum CheckpointTriggerReason {
28 WalSizeExceeded,
29 TimeSinceLastCheckpoint,
30 Explicit,
31}
32
33impl StatsManager {
34 pub fn record_checkpoint_success(
36 &self,
37 duration: Duration,
38 bytes_flushed: u64,
39 wal_files_truncated: u64,
40 ) {
41 self.add_value(MetricType::CheckpointSuccessCount);
42 self.add_value_with_amount(
43 MetricType::CheckpointDurationUs,
44 duration.as_micros() as u64,
45 );
46 self.add_value_with_amount(MetricType::CheckpointDataFlushedBytes, bytes_flushed);
47 self.add_value_with_amount(MetricType::CheckpointWalFilesTruncated, wal_files_truncated);
48 }
49
50 pub fn record_checkpoint_failure(&self) {
52 self.add_value(MetricType::CheckpointFailureCount);
53 }
54
55 pub fn record_checkpoint_trigger(&self, reason: CheckpointTriggerReason, age: Duration) {
57 self.add_value(MetricType::CheckpointTriggerCount);
58 self.add_value_with_amount(MetricType::CheckpointTriggerAgeSecs, age.as_secs());
59 match reason {
60 CheckpointTriggerReason::WalSizeExceeded => {
61 self.add_value(MetricType::CheckpointTriggeredByWalSize);
62 }
63 CheckpointTriggerReason::TimeSinceLastCheckpoint => {
64 self.add_value(MetricType::CheckpointTriggeredByInterval);
65 }
66 CheckpointTriggerReason::Explicit => {
67 self.add_value(MetricType::CheckpointTriggeredExplicit);
68 }
69 }
70 }
71
72 pub fn record_checkpoint_deduplicated(&self) {
74 self.add_value(MetricType::CheckpointRequestsDeduplicated);
75 }
76
77 pub fn record_checkpoint_blocked(&self) {
79 self.add_value(MetricType::CheckpointRequestsBlocked);
80 }
81
82 pub fn checkpoint_snapshot(&self) -> CheckpointSnapshot {
83 let v = |m: MetricType| self.get_value(m).unwrap_or(0);
84 let success = v(MetricType::CheckpointSuccessCount);
85 CheckpointSnapshot {
86 trigger_count: v(MetricType::CheckpointTriggerCount),
87 success_count: success,
88 failure_count: v(MetricType::CheckpointFailureCount),
89 avg_duration_us: if success == 0 {
90 0.0
91 } else {
92 v(MetricType::CheckpointDurationUs) as f64 / success as f64
93 },
94 data_flushed_bytes: v(MetricType::CheckpointDataFlushedBytes),
95 wal_files_truncated: v(MetricType::CheckpointWalFilesTruncated),
96 triggered_by_wal_size: v(MetricType::CheckpointTriggeredByWalSize),
97 triggered_by_interval: v(MetricType::CheckpointTriggeredByInterval),
98 triggered_explicit: v(MetricType::CheckpointTriggeredExplicit),
99 deduplicated: v(MetricType::CheckpointRequestsDeduplicated),
100 blocked: v(MetricType::CheckpointRequestsBlocked),
101 }
102 }
103
104 pub fn get_checkpoint_metrics(&self) -> HashMap<MetricType, u64> {
106 let mut out = HashMap::new();
107 for mt in [
108 MetricType::CheckpointTriggerCount,
109 MetricType::CheckpointSuccessCount,
110 MetricType::CheckpointFailureCount,
111 MetricType::CheckpointDurationUs,
112 MetricType::CheckpointDataFlushedBytes,
113 MetricType::CheckpointWalFilesTruncated,
114 MetricType::CheckpointTriggerAgeSecs,
115 MetricType::CheckpointTriggeredByWalSize,
116 MetricType::CheckpointTriggeredByInterval,
117 MetricType::CheckpointTriggeredExplicit,
118 MetricType::CheckpointRequestsDeduplicated,
119 MetricType::CheckpointRequestsBlocked,
120 MetricType::CheckpointStrategyIncremental,
121 MetricType::CheckpointStrategyHybrid,
122 MetricType::CheckpointStrategyFull,
123 MetricType::CheckpointIncrementalDurationUs,
124 MetricType::CheckpointIncrementalBytesFlushed,
125 ] {
126 if let Some(v) = self.get_value(mt) {
127 out.insert(mt, v);
128 }
129 }
130 out
131 }
132
133 pub fn record_dirty_pages(&self, dirty: u64, total: u64) {
134 self.set_value(MetricType::DirtyPagesCount, dirty);
135 self.set_value(MetricType::DirtyPagesTotal, total);
136 let ratio_permille = if total == 0 {
137 0
138 } else {
139 (dirty as f64 / total as f64 * 1000.0) as u64
140 };
141 self.set_value(MetricType::DirtyPageRatioPermille, ratio_permille);
142 }
143
144 pub fn record_checkpoint_strategy_by_name(&self, strategy: &str) {
145 match strategy {
146 "incremental" => self.add_value(MetricType::CheckpointStrategyIncremental),
147 "hybrid" => self.add_value(MetricType::CheckpointStrategyHybrid),
148 "full" => self.add_value(MetricType::CheckpointStrategyFull),
149 _ => {}
150 }
151 }
152
153 pub fn record_incremental_checkpoint(&self, duration: Duration, bytes: u64) {
154 self.add_value_with_amount(
155 MetricType::CheckpointIncrementalDurationUs,
156 duration.as_micros() as u64,
157 );
158 self.add_value_with_amount(MetricType::CheckpointIncrementalBytesFlushed, bytes);
159 }
160
161 pub fn get_dirty_page_metrics(&self) -> HashMap<MetricType, u64> {
162 let mut out = HashMap::new();
163 for mt in [
164 MetricType::DirtyPagesTotal,
165 MetricType::DirtyPagesCount,
166 MetricType::DirtyPageRatioPermille,
167 ] {
168 if let Some(v) = self.get_value(mt) {
169 out.insert(mt, v);
170 }
171 }
172 out
173 }
174}