1use std::sync::{Arc, LazyLock};
16
17use prometheus::{
18 IntCounterVec, Registry, exponential_buckets, histogram_opts,
19 register_int_counter_vec_with_registry,
20};
21use risingwave_common::metrics::{
22 LabelGuardedHistogramVec, LabelGuardedIntCounterVec, LabelGuardedIntGaugeVec,
23};
24use risingwave_common::monitor::GLOBAL_METRICS_REGISTRY;
25use risingwave_common::{
26 register_guarded_histogram_vec_with_registry, register_guarded_int_counter_vec_with_registry,
27 register_guarded_int_gauge_vec_with_registry,
28};
29
30use crate::source::kafka::stats::RdKafkaStats;
31
32#[derive(Debug, Clone, Copy)]
37pub enum ConnectorAckFailureType {
38 Error,
39 Timeout,
40 EmptyMessageId,
41 ChannelMissing,
42 ChannelSendError,
43 DecodeError,
44 BrokerError,
45}
46
47impl ConnectorAckFailureType {
48 pub fn as_str(self) -> &'static str {
49 match self {
50 Self::Error => "error",
51 Self::Timeout => "timeout",
52 Self::EmptyMessageId => "empty_message_id",
53 Self::ChannelMissing => "channel_missing",
54 Self::ChannelSendError => "channel_send_error",
55 Self::DecodeError => "decode_error",
56 Self::BrokerError => "broker_error",
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
62pub struct EnumeratorMetrics {
63 pub high_watermark: LabelGuardedIntGaugeVec,
64 pub kafka_consumer_group_delete_failure_count: IntCounterVec,
68 pub pg_cdc_confirmed_flush_lsn: LabelGuardedIntGaugeVec,
70 pub pg_cdc_upstream_max_lsn: LabelGuardedIntGaugeVec,
72 pub mysql_cdc_binlog_file_seq_min: LabelGuardedIntGaugeVec,
74 pub mysql_cdc_binlog_file_seq_max: LabelGuardedIntGaugeVec,
76 pub sqlserver_cdc_upstream_min_lsn: LabelGuardedIntGaugeVec,
78 pub sqlserver_cdc_upstream_max_lsn: LabelGuardedIntGaugeVec,
80}
81
82pub static GLOBAL_ENUMERATOR_METRICS: LazyLock<EnumeratorMetrics> =
83 LazyLock::new(|| EnumeratorMetrics::new(&GLOBAL_METRICS_REGISTRY));
84
85impl EnumeratorMetrics {
86 fn new(registry: &Registry) -> Self {
87 let high_watermark = register_guarded_int_gauge_vec_with_registry!(
88 "source_kafka_high_watermark",
89 "High watermark for a exec per partition",
90 &["source_id", "partition"],
91 registry,
92 )
93 .unwrap();
94
95 let kafka_consumer_group_delete_failure_count = register_int_counter_vec_with_registry!(
96 "source_kafka_consumer_group_delete_failure_count",
97 "Total number of Kafka consumer group delete attempts that failed during source enumerator cleanup",
98 &["source_id", "consumer_group"],
99 registry,
100 )
101 .unwrap();
102
103 let pg_cdc_confirmed_flush_lsn = register_guarded_int_gauge_vec_with_registry!(
104 "pg_cdc_confirmed_flush_lsn",
105 "PostgreSQL CDC confirmed flush LSN",
106 &["source_id", "slot_name"],
107 registry,
108 )
109 .unwrap();
110
111 let pg_cdc_upstream_max_lsn = register_guarded_int_gauge_vec_with_registry!(
112 "pg_cdc_upstream_max_lsn",
113 "PostgreSQL CDC upstream max LSN (pg_current_wal_lsn)",
114 &["source_id", "slot_name"],
115 registry,
116 )
117 .unwrap();
118
119 let mysql_cdc_binlog_file_seq_min = register_guarded_int_gauge_vec_with_registry!(
120 "mysql_cdc_binlog_file_seq_min",
121 "MySQL CDC upstream binlog file sequence number (minimum/oldest)",
122 &["hostname", "port"],
123 registry,
124 )
125 .unwrap();
126
127 let mysql_cdc_binlog_file_seq_max = register_guarded_int_gauge_vec_with_registry!(
128 "mysql_cdc_binlog_file_seq_max",
129 "MySQL CDC upstream binlog file sequence number (maximum/newest)",
130 &["hostname", "port"],
131 registry,
132 )
133 .unwrap();
134
135 let sqlserver_cdc_upstream_min_lsn = register_guarded_int_gauge_vec_with_registry!(
136 "sqlserver_cdc_upstream_min_lsn",
137 "SQL Server CDC upstream minimum LSN",
138 &["source_id"],
139 registry,
140 )
141 .unwrap();
142
143 let sqlserver_cdc_upstream_max_lsn = register_guarded_int_gauge_vec_with_registry!(
144 "sqlserver_cdc_upstream_max_lsn",
145 "SQL Server CDC upstream maximum LSN",
146 &["source_id"],
147 registry,
148 )
149 .unwrap();
150
151 EnumeratorMetrics {
152 high_watermark,
153 kafka_consumer_group_delete_failure_count,
154 pg_cdc_confirmed_flush_lsn,
155 pg_cdc_upstream_max_lsn,
156 mysql_cdc_binlog_file_seq_min,
157 mysql_cdc_binlog_file_seq_max,
158 sqlserver_cdc_upstream_min_lsn,
159 sqlserver_cdc_upstream_max_lsn,
160 }
161 }
162
163 pub fn unused() -> Self {
164 Default::default()
165 }
166}
167
168impl Default for EnumeratorMetrics {
169 fn default() -> Self {
170 GLOBAL_ENUMERATOR_METRICS.clone()
171 }
172}
173
174#[derive(Debug, Clone)]
175pub struct SourceMetrics {
176 pub partition_input_count: LabelGuardedIntCounterVec,
177
178 pub partition_input_bytes: LabelGuardedIntCounterVec,
181 pub latest_message_id: LabelGuardedIntGaugeVec,
183 pub partition_eof_count: LabelGuardedIntCounterVec,
184 pub partition_eof_offset: LabelGuardedIntGaugeVec,
185 pub rdkafka_native_metric: Arc<RdKafkaStats>,
186
187 pub direct_cdc_event_lag_latency: LabelGuardedHistogramVec,
188
189 pub parquet_source_skip_row_count: LabelGuardedIntCounterVec,
190 pub file_source_input_row_count: LabelGuardedIntCounterVec,
191 pub file_source_dirty_split_count: LabelGuardedIntGaugeVec,
192 pub file_source_failed_split_count: LabelGuardedIntCounterVec,
193
194 pub kinesis_throughput_exceeded_count: LabelGuardedIntCounterVec,
196 pub kinesis_timeout_count: LabelGuardedIntCounterVec,
197 pub kinesis_rebuild_shard_iter_count: LabelGuardedIntCounterVec,
198 pub kinesis_early_terminate_shard_count: LabelGuardedIntCounterVec,
199 pub kinesis_lag_latency_ms: LabelGuardedHistogramVec,
200
201 connector_ack_failure_count: IntCounterVec,
203 connector_ack_success_count: IntCounterVec,
205}
206
207pub static GLOBAL_SOURCE_METRICS: LazyLock<SourceMetrics> =
208 LazyLock::new(|| SourceMetrics::new(&GLOBAL_METRICS_REGISTRY));
209
210impl SourceMetrics {
211 pub fn inc_connector_ack_failure_count(
212 &self,
213 source_name: &str,
214 connector_type: &'static str,
215 failure_type: ConnectorAckFailureType,
216 ) {
217 self.connector_ack_failure_count
218 .with_label_values(&[source_name, connector_type, failure_type.as_str()])
219 .inc();
220 }
221
222 pub fn inc_connector_ack_success_count(&self, source_name: &str, connector_type: &'static str) {
223 self.connector_ack_success_count
224 .with_label_values(&[source_name, connector_type])
225 .inc();
226 }
227
228 fn new(registry: &Registry) -> Self {
229 let partition_input_count = register_guarded_int_counter_vec_with_registry!(
230 "source_partition_input_count",
231 "Total number of rows that have been input from specific partition",
232 &[
233 "actor_id",
234 "source_id",
235 "partition",
236 "source_name",
237 "fragment_id"
238 ],
239 registry
240 )
241 .unwrap();
242 let partition_input_bytes = register_guarded_int_counter_vec_with_registry!(
243 "source_partition_input_bytes",
244 "Total bytes that have been input from specific partition",
245 &[
246 "actor_id",
247 "source_id",
248 "partition",
249 "source_name",
250 "fragment_id"
251 ],
252 registry
253 )
254 .unwrap();
255 let latest_message_id = register_guarded_int_gauge_vec_with_registry!(
256 "source_latest_message_id",
257 "Latest message id for a exec per partition",
258 &["source_id", "actor_id", "partition"],
259 registry,
260 )
261 .unwrap();
262 let partition_eof_count = register_guarded_int_counter_vec_with_registry!(
263 "source_partition_eof_count",
264 "Total number of EOF events received from specific partition",
265 &["source_id", "partition", "source_name", "fragment_id"],
266 registry
267 )
268 .unwrap();
269 let partition_eof_offset = register_guarded_int_gauge_vec_with_registry!(
270 "source_partition_eof_offset",
271 "Latest resolved EOF offset for specific partition",
272 &["source_id", "partition", "source_name", "fragment_id"],
273 registry
274 )
275 .unwrap();
276
277 let opts = histogram_opts!(
278 "source_cdc_event_lag_duration_milliseconds",
279 "source_cdc_lag_latency",
280 exponential_buckets(1.0, 2.0, 21).unwrap(), );
282
283 let parquet_source_skip_row_count = register_guarded_int_counter_vec_with_registry!(
284 "parquet_source_skip_row_count",
285 "Total number of rows that have been set to null in parquet source",
286 &["actor_id", "source_id", "source_name", "fragment_id"],
287 registry
288 )
289 .unwrap();
290
291 let direct_cdc_event_lag_latency =
292 register_guarded_histogram_vec_with_registry!(opts, &["table_name"], registry).unwrap();
293
294 let rdkafka_native_metric = Arc::new(RdKafkaStats::new(registry.clone()));
295
296 let file_source_input_row_count = register_guarded_int_counter_vec_with_registry!(
297 "file_source_input_row_count",
298 "Total number of rows that have been read in file source",
299 &["source_id", "source_name", "actor_id", "fragment_id"],
300 registry
301 )
302 .unwrap();
303 let file_source_dirty_split_count = register_guarded_int_gauge_vec_with_registry!(
304 "file_source_dirty_split_count",
305 "Current number of dirty file splits in file source",
306 &["source_id", "source_name", "actor_id", "fragment_id"],
307 registry
308 )
309 .unwrap();
310 let file_source_failed_split_count = register_guarded_int_counter_vec_with_registry!(
311 "file_source_failed_split_count",
312 "Total number of file splits marked dirty in file source",
313 &["source_id", "source_name", "actor_id", "fragment_id"],
314 registry
315 )
316 .unwrap();
317
318 let kinesis_throughput_exceeded_count = register_guarded_int_counter_vec_with_registry!(
319 "kinesis_throughput_exceeded_count",
320 "Total number of times throughput exceeded in kinesis source",
321 &["source_id", "source_name", "fragment_id", "shard_id"],
322 registry
323 )
324 .unwrap();
325
326 let kinesis_timeout_count = register_guarded_int_counter_vec_with_registry!(
327 "kinesis_timeout_count",
328 "Total number of times timeout in kinesis source",
329 &["source_id", "source_name", "fragment_id", "shard_id"],
330 registry
331 )
332 .unwrap();
333
334 let kinesis_rebuild_shard_iter_count = register_guarded_int_counter_vec_with_registry!(
335 "kinesis_rebuild_shard_iter_count",
336 "Total number of times rebuild shard iter in kinesis source",
337 &["source_id", "source_name", "fragment_id", "shard_id"],
338 registry
339 )
340 .unwrap();
341
342 let kinesis_early_terminate_shard_count = register_guarded_int_counter_vec_with_registry!(
343 "kinesis_early_terminate_shard_count",
344 "Total number of times early terminate shard in kinesis source",
345 &["source_id", "source_name", "fragment_id", "shard_id"],
346 registry
347 )
348 .unwrap();
349
350 let kinesis_lag_latency_ms = register_guarded_histogram_vec_with_registry!(
351 "kinesis_lag_latency_ms",
352 "Lag latency in kinesis source",
353 &["source_id", "source_name", "fragment_id", "shard_id"],
354 registry
355 )
356 .unwrap();
357
358 let connector_ack_failure_count = register_int_counter_vec_with_registry!(
359 "source_connector_ack_failure_count",
360 "Total number of connector ack failures after checkpoint commit by bounded failure category",
361 &["source_name", "connector_type", "error_type"],
362 registry
363 )
364 .unwrap();
365 let connector_ack_success_count = register_int_counter_vec_with_registry!(
366 "source_connector_ack_success_count",
367 "Total number of successful connector acks after checkpoint commit",
368 &["source_name", "connector_type"],
369 registry
370 )
371 .unwrap();
372
373 SourceMetrics {
374 partition_input_count,
375 partition_input_bytes,
376 latest_message_id,
377 partition_eof_count,
378 partition_eof_offset,
379 rdkafka_native_metric,
380 direct_cdc_event_lag_latency,
381 parquet_source_skip_row_count,
382 file_source_input_row_count,
383 file_source_dirty_split_count,
384 file_source_failed_split_count,
385
386 kinesis_throughput_exceeded_count,
387 kinesis_timeout_count,
388 kinesis_rebuild_shard_iter_count,
389 kinesis_early_terminate_shard_count,
390 kinesis_lag_latency_ms,
391
392 connector_ack_failure_count,
393 connector_ack_success_count,
394 }
395 }
396}
397
398impl Default for SourceMetrics {
399 fn default() -> Self {
400 GLOBAL_SOURCE_METRICS.clone()
401 }
402}