cbtop/incremental_snapshot/
types.rs1use std::time::Duration;
4
5pub type SnapshotResult<T> = Result<T, SnapshotError>;
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum SnapshotError {
11 IoError { reason: String },
13 NotFound { index: usize },
15 Corrupt { reason: String },
17 ChecksumMismatch { expected: u32, actual: u32 },
19 IndexOutOfBounds { index: usize, max: usize },
21 MemoryExceeded { limit_bytes: usize },
23}
24
25impl std::fmt::Display for SnapshotError {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 Self::IoError { reason } => write!(f, "IO error: {}", reason),
29 Self::NotFound { index } => write!(f, "Snapshot {} not found", index),
30 Self::Corrupt { reason } => write!(f, "Corrupt snapshot: {}", reason),
31 Self::ChecksumMismatch { expected, actual } => {
32 write!(
33 f,
34 "Checksum mismatch: expected {}, got {}",
35 expected, actual
36 )
37 }
38 Self::IndexOutOfBounds { index, max } => {
39 write!(f, "Index {} out of bounds (max {})", index, max)
40 }
41 Self::MemoryExceeded { limit_bytes } => {
42 write!(f, "Memory limit {} exceeded", limit_bytes)
43 }
44 }
45 }
46}
47
48impl std::error::Error for SnapshotError {}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum RetentionTier {
53 Raw,
55 Compressed,
57 Archive,
59}
60
61impl RetentionTier {
62 pub fn max_age(&self) -> Duration {
64 match self {
65 Self::Raw => Duration::from_secs(24 * 60 * 60), Self::Compressed => Duration::from_secs(30 * 24 * 60 * 60), Self::Archive => Duration::from_secs(365 * 24 * 60 * 60), }
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct MetricData {
75 pub name: String,
77 pub values: Vec<f64>,
79 pub timestamps: Vec<u64>,
81}
82
83impl MetricData {
84 pub fn new(name: impl Into<String>) -> Self {
86 Self {
87 name: name.into(),
88 values: Vec::new(),
89 timestamps: Vec::new(),
90 }
91 }
92
93 pub fn add(&mut self, value: f64, timestamp: u64) {
95 self.values.push(value);
96 self.timestamps.push(timestamp);
97 }
98
99 pub fn size_bytes(&self) -> usize {
101 self.name.len() + self.values.len() * 8 + self.timestamps.len() * 8
102 }
103
104 pub fn delta_from(&self, other: &MetricData) -> DeltaMetric {
106 let mut changed_indices = Vec::new();
107 let mut changed_values = Vec::new();
108
109 let max_len = self.values.len().max(other.values.len());
111 for i in 0..max_len {
112 let self_val = self.values.get(i).copied().unwrap_or(0.0);
113 let other_val = other.values.get(i).copied().unwrap_or(0.0);
114
115 if (self_val - other_val).abs() > 1e-10 {
116 changed_indices.push(i);
117 changed_values.push(self_val);
118 }
119 }
120
121 DeltaMetric {
122 name: self.name.clone(),
123 base_len: other.values.len(),
124 new_len: self.values.len(),
125 changed_indices,
126 changed_values,
127 }
128 }
129
130 pub fn apply_delta(&self, delta: &DeltaMetric) -> MetricData {
132 let mut result = MetricData::new(&delta.name);
133
134 result.values = self.values.clone();
136 result.timestamps = self.timestamps.clone();
137
138 result.values.resize(delta.new_len, 0.0);
140 result.timestamps.resize(delta.new_len, 0);
141
142 for (i, &idx) in delta.changed_indices.iter().enumerate() {
144 if idx < result.values.len() {
145 result.values[idx] = delta.changed_values[i];
146 }
147 }
148
149 result
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct DeltaMetric {
156 pub name: String,
158 pub base_len: usize,
160 pub new_len: usize,
162 pub changed_indices: Vec<usize>,
164 pub changed_values: Vec<f64>,
166}
167
168impl DeltaMetric {
169 pub fn size_bytes(&self) -> usize {
171 self.name.len() + 16 + self.changed_indices.len() * 8 + self.changed_values.len() * 8
172 }
173
174 pub fn compression_ratio(&self, original_size: usize) -> f64 {
176 if original_size == 0 {
177 1.0
178 } else {
179 self.size_bytes() as f64 / original_size as f64
180 }
181 }
182}