laminar_connectors/lakehouse/delta_metrics/
mod.rs1use prometheus::{Histogram, HistogramOpts, IntCounter, IntGauge, Registry};
4
5use super::metrics::LakehouseSinkMetrics;
6use crate::prom::reg_or_local;
7
8#[derive(Debug, Clone)]
10pub struct DeltaLakeSinkMetrics {
11 pub common: LakehouseSinkMetrics,
13
14 pub merge_operations: IntCounter,
16
17 pub last_delta_version: IntGauge,
19
20 pub flush_duration: Histogram,
23
24 pub collapse_rows_in: IntCounter,
26
27 pub collapse_upserts_out: IntCounter,
29
30 pub collapse_deletes_out: IntCounter,
32
33 pub collapse_duration: Histogram,
36}
37
38impl DeltaLakeSinkMetrics {
39 #[must_use]
41 #[allow(clippy::missing_panics_doc)]
42 pub fn new(registry: Option<&Registry>) -> Self {
43 let mut local = None;
44 let handle = reg_or_local(registry, &mut local);
45
46 let flush_duration = Histogram::with_opts(
47 HistogramOpts::new(
48 "delta_sink_flush_duration_seconds",
49 "End-to-end Delta Lake flush duration (pre-concat → write → checkpoint)",
50 )
51 .buckets(prometheus::exponential_buckets(0.005, 2.0, 16).unwrap()),
52 )
53 .unwrap();
54 if let Err(e) = handle.registry().register(Box::new(flush_duration.clone())) {
58 tracing::warn!(
59 metric = "delta_sink_flush_duration_seconds",
60 error = %e,
61 "failed to register delta lake flush_duration histogram"
62 );
63 }
64
65 let collapse_duration = Histogram::with_opts(
66 HistogramOpts::new(
67 "delta_sink_collapse_duration_seconds",
68 "Changelog collapse duration per upsert flush (Z-set/CDC dedup)",
69 )
70 .buckets(prometheus::exponential_buckets(0.0001, 2.0, 16).unwrap()),
71 )
72 .unwrap();
73 if let Err(e) = handle
74 .registry()
75 .register(Box::new(collapse_duration.clone()))
76 {
77 tracing::warn!(
78 metric = "delta_sink_collapse_duration_seconds",
79 error = %e,
80 "failed to register delta lake collapse_duration histogram"
81 );
82 }
83
84 Self {
85 common: LakehouseSinkMetrics::new(registry),
86 merge_operations: handle.counter(
87 "delta_sink_merge_operations_total",
88 "Total MERGE operations (upsert)",
89 ),
90 last_delta_version: handle.gauge(
91 "delta_sink_last_version",
92 "Last committed Delta table version",
93 ),
94 flush_duration,
95 collapse_rows_in: handle.counter(
96 "delta_sink_collapse_rows_in_total",
97 "Changelog rows entering collapse (pre-dedup)",
98 ),
99 collapse_upserts_out: handle.counter(
100 "delta_sink_collapse_upserts_out_total",
101 "Upsert rows emitted by collapse (_op = U)",
102 ),
103 collapse_deletes_out: handle.counter(
104 "delta_sink_collapse_deletes_out_total",
105 "Delete rows emitted by collapse (_op = D)",
106 ),
107 collapse_duration,
108 }
109 }
110
111 pub fn record_flush(&self, records: u64, bytes: u64) {
113 self.common.record_flush(records, bytes);
114 }
115
116 #[allow(clippy::cast_possible_wrap)]
118 pub fn record_commit(&self, delta_version: u64) {
119 self.common.record_commit();
120 self.last_delta_version.set(delta_version as i64);
121 }
122
123 pub fn record_error(&self) {
125 self.common.record_error();
126 }
127
128 pub fn record_rollback(&self) {
130 self.common.record_rollback();
131 }
132
133 pub fn record_merge(&self) {
135 self.merge_operations.inc();
136 }
137
138 pub fn record_deletes(&self, count: u64) {
140 self.common.record_deletes(count);
141 }
142
143 pub fn observe_flush_duration(&self, seconds: f64) {
145 self.flush_duration.observe(seconds);
146 }
147
148 pub fn observe_collapse(&self, rows_in: u64, upserts_out: u64, deletes_out: u64, seconds: f64) {
151 self.collapse_rows_in.inc_by(rows_in);
152 self.collapse_upserts_out.inc_by(upserts_out);
153 self.collapse_deletes_out.inc_by(deletes_out);
154 self.collapse_duration.observe(seconds);
155 }
156}
157
158impl Default for DeltaLakeSinkMetrics {
159 fn default() -> Self {
160 Self::new(None)
161 }
162}
163
164#[cfg(test)]
165mod tests;