risingwave_connector/source/iceberg/
metrics.rs1use std::sync::{Arc, LazyLock};
16
17use prometheus::{Registry, exponential_buckets, histogram_opts};
18use risingwave_common::metrics::{
19 LabelGuardedHistogram, LabelGuardedHistogramVec, LabelGuardedIntCounter,
20 LabelGuardedIntCounterVec, LabelGuardedIntGaugeVec,
21};
22use risingwave_common::monitor::GLOBAL_METRICS_REGISTRY;
23use risingwave_common::{
24 register_guarded_histogram_vec_with_registry, register_guarded_int_counter_vec_with_registry,
25 register_guarded_int_gauge_vec_with_registry,
26};
27
28#[derive(Clone)]
29pub struct IcebergScanMetrics {
30 pub iceberg_read_bytes: LabelGuardedIntCounterVec,
32
33 pub iceberg_source_snapshot_lag_seconds: LabelGuardedIntGaugeVec,
37
38 pub iceberg_source_snapshots_discovered_total: LabelGuardedIntCounterVec,
40
41 pub iceberg_source_list_duration_seconds: LabelGuardedHistogramVec,
43
44 pub iceberg_source_files_discovered_total: LabelGuardedIntCounterVec,
46
47 pub iceberg_source_file_read_duration_seconds: LabelGuardedHistogramVec,
50
51 pub iceberg_source_rows_read_total: LabelGuardedIntCounterVec,
53
54 pub iceberg_source_files_read_total: LabelGuardedIntCounterVec,
56
57 pub iceberg_source_delete_rows_applied_total: LabelGuardedIntCounterVec,
60
61 pub iceberg_source_delete_files_per_data_file: LabelGuardedHistogramVec,
63
64 pub iceberg_source_inflight_file_count: LabelGuardedIntGaugeVec,
67
68 pub iceberg_source_scan_errors_total: LabelGuardedIntCounterVec,
70}
71
72#[derive(Clone)]
73pub struct IcebergFileScanMetrics {
74 read_bytes: LabelGuardedIntCounter,
75 file_read_duration_seconds: LabelGuardedHistogram,
76 rows_read_total: LabelGuardedIntCounter,
77 files_read_total: LabelGuardedIntCounter,
78 delete_rows_applied_total: LabelGuardedIntCounter,
79}
80
81impl IcebergFileScanMetrics {
82 pub fn new(metrics: &IcebergScanMetrics, table_name: &str) -> Self {
83 Self {
84 read_bytes: metrics
85 .iceberg_read_bytes
86 .with_guarded_label_values(&[table_name]),
87 file_read_duration_seconds: metrics
88 .iceberg_source_file_read_duration_seconds
89 .with_guarded_label_values(&[table_name]),
90 rows_read_total: metrics
91 .iceberg_source_rows_read_total
92 .with_guarded_label_values(&[table_name]),
93 files_read_total: metrics
94 .iceberg_source_files_read_total
95 .with_guarded_label_values(&[table_name, "data"]),
96 delete_rows_applied_total: metrics
97 .iceberg_source_delete_rows_applied_total
98 .with_guarded_label_values(&[table_name, "sdk_applied_approx"]),
99 }
100 }
101
102 pub fn record_read_bytes(&self, bytes: u64) {
103 self.read_bytes.inc_by(bytes);
104 }
105
106 pub fn record_file_read_duration(&self, seconds: f64) {
107 self.file_read_duration_seconds.observe(seconds);
108 }
109
110 pub fn record_rows_read(&self, rows: u64) {
111 self.rows_read_total.inc_by(rows);
112 }
113
114 pub fn record_file_read(&self) {
115 self.files_read_total.inc();
116 }
117
118 pub fn record_delete_rows_applied(&self, rows: u64) {
119 self.delete_rows_applied_total.inc_by(rows);
120 }
121}
122
123impl IcebergScanMetrics {
124 fn new(registry: &Registry) -> Self {
125 let iceberg_read_bytes = register_guarded_int_counter_vec_with_registry!(
126 "iceberg_read_bytes",
127 "Total size of iceberg read requests",
128 &["table_name"],
129 registry
130 )
131 .unwrap();
132
133 let iceberg_source_snapshot_lag_seconds = register_guarded_int_gauge_vec_with_registry!(
134 "iceberg_source_snapshot_lag_seconds",
135 "Lag between latest available snapshot and last ingested snapshot in seconds",
136 &["source_id", "source_name", "table_name"],
137 registry
138 )
139 .unwrap();
140
141 let iceberg_source_snapshots_discovered_total =
142 register_guarded_int_counter_vec_with_registry!(
143 "iceberg_source_snapshots_discovered_total",
144 "Total number of snapshots discovered via incremental scan",
145 &["source_id", "source_name", "table_name"],
146 registry
147 )
148 .unwrap();
149
150 let iceberg_source_list_duration_seconds = register_guarded_histogram_vec_with_registry!(
151 histogram_opts!(
152 "iceberg_source_list_duration_seconds",
153 "Time spent planning files from a snapshot",
154 exponential_buckets(0.01, 2.0, 15).unwrap() ),
156 &["source_id", "source_name", "table_name"],
157 registry
158 )
159 .unwrap();
160
161 let iceberg_source_files_discovered_total =
162 register_guarded_int_counter_vec_with_registry!(
163 "iceberg_source_files_discovered_total",
164 "Total number of files discovered per scan",
165 &["source_id", "source_name", "table_name", "file_type"],
166 registry
167 )
168 .unwrap();
169
170 let iceberg_source_file_read_duration_seconds =
173 register_guarded_histogram_vec_with_registry!(
174 histogram_opts!(
175 "iceberg_source_file_read_duration_seconds",
176 "Per-file read duration",
177 exponential_buckets(0.01, 2.0, 15).unwrap() ),
179 &["table_name"],
180 registry
181 )
182 .unwrap();
183
184 let iceberg_source_rows_read_total = register_guarded_int_counter_vec_with_registry!(
185 "iceberg_source_rows_read_total",
186 "Total rows read from Iceberg source",
187 &["table_name"],
188 registry
189 )
190 .unwrap();
191
192 let iceberg_source_files_read_total = register_guarded_int_counter_vec_with_registry!(
193 "iceberg_source_files_read_total",
194 "Total files read from Iceberg source",
195 &["table_name", "file_type"],
196 registry
197 )
198 .unwrap();
199
200 let iceberg_source_delete_rows_applied_total =
201 register_guarded_int_counter_vec_with_registry!(
202 "iceberg_source_delete_rows_applied_total",
203 "Total rows removed by delete processing",
204 &["table_name", "delete_type"],
205 registry
206 )
207 .unwrap();
208
209 let iceberg_source_delete_files_per_data_file =
210 register_guarded_histogram_vec_with_registry!(
211 histogram_opts!(
212 "iceberg_source_delete_files_per_data_file",
213 "Number of delete files attached per data file scan task",
214 exponential_buckets(1.0, 2.0, 8).unwrap()
216 ),
217 &["source_id", "source_name", "table_name"],
218 registry
219 )
220 .unwrap();
221
222 let iceberg_source_inflight_file_count = register_guarded_int_gauge_vec_with_registry!(
223 "iceberg_source_inflight_file_count",
224 "Number of files currently being fetched by the active reader",
225 &["source_id", "source_name", "table_name"],
226 registry
227 )
228 .unwrap();
229
230 let iceberg_source_scan_errors_total = register_guarded_int_counter_vec_with_registry!(
231 "iceberg_source_scan_errors_total",
232 "Total number of scan errors categorized by error type",
233 &["source_id", "source_name", "table_name", "error_type"],
234 registry
235 )
236 .unwrap();
237
238 Self {
239 iceberg_read_bytes,
240 iceberg_source_snapshot_lag_seconds,
241 iceberg_source_snapshots_discovered_total,
242 iceberg_source_list_duration_seconds,
243 iceberg_source_files_discovered_total,
244 iceberg_source_file_read_duration_seconds,
245 iceberg_source_rows_read_total,
246 iceberg_source_files_read_total,
247 iceberg_source_delete_rows_applied_total,
248 iceberg_source_delete_files_per_data_file,
249 iceberg_source_inflight_file_count,
250 iceberg_source_scan_errors_total,
251 }
252 }
253
254 pub fn for_test() -> Arc<Self> {
255 Arc::new(GLOBAL_ICEBERG_SCAN_METRICS.clone())
256 }
257}
258
259pub static GLOBAL_ICEBERG_SCAN_METRICS: LazyLock<IcebergScanMetrics> =
260 LazyLock::new(|| IcebergScanMetrics::new(&GLOBAL_METRICS_REGISTRY));