1use std::collections::{HashMap, HashSet};
16use std::sync::atomic::AtomicU64;
17use std::sync::{Arc, LazyLock, OnceLock};
18use std::time::Duration;
19
20use prometheus::core::{AtomicF64, Collector, GenericGaugeVec, MetricVec, MetricVecBuilder};
21use prometheus::{
22 GaugeVec, Histogram, HistogramVec, IntCounterVec, IntGauge, IntGaugeVec, Registry,
23 exponential_buckets, histogram_opts, register_gauge_vec_with_registry,
24 register_histogram_vec_with_registry, register_histogram_with_registry,
25 register_int_counter_vec_with_registry, register_int_gauge_vec_with_registry,
26 register_int_gauge_with_registry,
27};
28use risingwave_common::catalog::{FragmentTypeFlag, TableId};
29use risingwave_common::config::MetricLevel;
30use risingwave_common::metrics::{
31 LabelGuardedHistogramVec, LabelGuardedIntCounterVec, LabelGuardedIntGaugeVec,
32 LabelGuardedUintGaugeVec, MetricVecRelabelExt, RelabeledMetricVec,
33};
34use risingwave_common::monitor::GLOBAL_METRICS_REGISTRY;
35use risingwave_common::system_param::reader::SystemParamsRead;
36use risingwave_common::util::stream_graph_visitor::{
37 visit_stream_node_source_backfill, visit_stream_node_stream_scan,
38};
39use risingwave_common::{
40 register_guarded_histogram_vec_with_registry, register_guarded_int_counter_vec_with_registry,
41 register_guarded_int_gauge_vec_with_registry, register_guarded_uint_gauge_vec_with_registry,
42};
43use risingwave_connector::source::monitor::EnumeratorMetrics as SourceEnumeratorMetrics;
44use risingwave_meta_model::table::TableType;
45use risingwave_meta_model::{ObjectId, WorkerId};
46use risingwave_object_store::object::object_metrics::{
47 GLOBAL_OBJECT_STORE_METRICS, ObjectStoreMetrics,
48};
49use risingwave_pb::common::WorkerType;
50use risingwave_pb::meta::FragmentDistribution;
51use thiserror_ext::AsReport;
52use tokio::sync::oneshot::Sender;
53use tokio::task::JoinHandle;
54
55use crate::barrier::BarrierManagerRef;
56use crate::controller::catalog::CatalogControllerRef;
57use crate::controller::cluster::ClusterControllerRef;
58use crate::controller::system_param::SystemParamsControllerRef;
59use crate::controller::utils::PartialFragmentStateTables;
60use crate::hummock::HummockManagerRef;
61use crate::manager::MetadataManager;
62use crate::rpc::ElectionClientRef;
63
64struct BackfillFragmentInfo {
65 job_id: u32,
66 fragment_id: u32,
67 backfill_state_table_id: u32,
68 backfill_target_relation_id: u32,
69 backfill_type: &'static str,
70 backfill_epoch: u64,
71}
72
73#[derive(Clone)]
74pub struct MetaMetrics {
75 pub worker_num: IntGaugeVec,
78 pub meta_type: IntGaugeVec,
80
81 pub grpc_latency: HistogramVec,
84
85 pub barrier_latency: LabelGuardedHistogramVec,
89 pub barrier_wait_commit_latency: Histogram,
91 pub all_barrier_nums: LabelGuardedIntGaugeVec,
94 pub in_flight_barrier_nums: LabelGuardedIntGaugeVec,
96 pub last_committed_barrier_time: LabelGuardedIntGaugeVec,
98 pub barrier_interval_by_database: GaugeVec,
100
101 pub snapshot_backfill_barrier_latency: LabelGuardedHistogramVec, pub snapshot_backfill_lag: LabelGuardedIntGaugeVec, pub snapshot_backfill_inflight_barrier_num: LabelGuardedIntGaugeVec, pub recovery_failure_cnt: IntCounterVec,
111 pub recovery_latency: HistogramVec,
112
113 pub max_committed_epoch: IntGauge,
116 pub min_committed_epoch: IntGauge,
118 pub level_sst_num: IntGaugeVec,
120 pub level_compact_cnt: IntGaugeVec,
122 pub compact_frequency: IntCounterVec,
124 pub level_file_size: IntGaugeVec,
126 pub version_size: IntGauge,
128 pub current_version_id: IntGauge,
130 pub checkpoint_version_id: IntGauge,
132 pub min_pinned_version_id: IntGauge,
134 pub min_safepoint_version_id: IntGauge,
136 pub write_stop_compaction_groups: IntGaugeVec,
138 pub full_gc_trigger_count: IntGauge,
140 pub full_gc_candidate_object_count: Histogram,
142 pub full_gc_selected_object_count: Histogram,
144 pub version_stats: IntGaugeVec,
146 pub materialized_view_stats: IntGaugeVec,
148 pub stale_object_count: IntGauge,
150 pub stale_object_size: IntGauge,
152 pub old_version_object_count: IntGauge,
154 pub old_version_object_size: IntGauge,
156 pub time_travel_object_count: IntGauge,
158 pub current_version_object_count: IntGauge,
160 pub current_version_object_size: IntGauge,
162 pub total_object_count: IntGauge,
164 pub total_object_size: IntGauge,
166 pub table_change_log_object_count: IntGaugeVec,
168 pub table_change_log_object_size: IntGaugeVec,
170 pub table_change_log_min_epoch: IntGaugeVec,
172 pub table_change_log_get_latency: Histogram,
174 pub table_change_log_truncate_latency: Histogram,
176 pub delta_log_count: IntGauge,
178 pub version_checkpoint_latency: Histogram,
180 pub hummock_manager_lock_time: HistogramVec,
182 pub hummock_manager_real_process_time: HistogramVec,
184 pub compact_skip_frequency: IntCounterVec,
186 pub compact_pending_bytes: IntGaugeVec,
188 pub compact_level_compression_ratio: GenericGaugeVec<AtomicF64>,
190 pub level_compact_task_cnt: IntGaugeVec,
192 pub time_after_last_observation: Arc<AtomicU64>,
193 pub l0_compact_level_count: HistogramVec,
194 pub compact_task_size: HistogramVec,
195 pub compact_task_file_count: HistogramVec,
196 pub compact_task_batch_count: HistogramVec,
197 pub split_compaction_group_count: IntCounterVec,
198 pub state_table_count: IntGaugeVec,
199 pub branched_sst_count: IntGaugeVec,
200 pub compact_task_trivial_move_sst_count: HistogramVec,
201
202 pub compaction_event_consumed_latency: Histogram,
203 pub compaction_event_loop_iteration_latency: Histogram,
204 pub time_travel_vacuum_metadata_latency: Histogram,
205 pub time_travel_write_metadata_latency: Histogram,
206
207 pub object_store_metric: Arc<ObjectStoreMetrics>,
210
211 pub source_is_up: LabelGuardedIntGaugeVec,
214 pub source_worker_tick_duration_seconds: LabelGuardedHistogramVec,
216 pub source_enumerator_monitor_error_count: LabelGuardedIntCounterVec,
218 pub source_enumerator_metrics: Arc<SourceEnumeratorMetrics>,
219
220 pub actor_info: IntGaugeVec,
226 pub table_info: IntGaugeVec,
228 pub sink_info: RelabeledMetricVec<IntGaugeVec>,
230 pub relation_info: IntGaugeVec,
232 pub database_info: IntGaugeVec,
234 pub backfill_fragment_progress: IntGaugeVec,
236 pub streaming_table_change_log_retention_seconds: IntGaugeVec,
238
239 pub system_param_info: IntGaugeVec,
243
244 pub table_write_throughput: IntCounterVec,
246
247 pub merge_compaction_group_count: IntCounterVec,
249
250 pub auto_schema_change_failure_cnt: IntCounterVec,
252 pub auto_schema_change_success_cnt: IntCounterVec,
253 pub auto_schema_change_latency: HistogramVec,
254
255 pub time_travel_version_replay_latency: Histogram,
256
257 pub compaction_group_count: IntGauge,
258 pub compaction_group_size: IntGaugeVec,
259 pub compaction_group_file_count: IntGaugeVec,
260 pub compaction_group_throughput: IntGaugeVec,
261
262 pub refresh_job_duration: LabelGuardedUintGaugeVec,
264 pub refresh_job_finish_cnt: LabelGuardedIntCounterVec,
265 pub refresh_cron_job_trigger_cnt: LabelGuardedIntCounterVec,
266 pub refresh_cron_job_miss_cnt: LabelGuardedIntCounterVec,
267}
268
269static META_METRICS_LEVEL: OnceLock<MetricLevel> = OnceLock::new();
270
271pub fn init_meta_metrics(metric_level: MetricLevel) {
272 META_METRICS_LEVEL.get_or_init(|| metric_level);
273}
274
275fn meta_metrics_level() -> MetricLevel {
276 META_METRICS_LEVEL
277 .get()
278 .copied()
279 .unwrap_or(MetricLevel::Debug)
280}
281
282pub static GLOBAL_META_METRICS: LazyLock<MetaMetrics> =
283 LazyLock::new(|| MetaMetrics::new(&GLOBAL_METRICS_REGISTRY, meta_metrics_level()));
284
285fn latency_buckets(max: f64, count: usize) -> Vec<f64> {
286 const MIN: f64 = 0.1;
287
288 assert!(count > 1);
289 let factor = (max / MIN).powf(1.0 / (count - 1) as f64);
290 let mut buckets = exponential_buckets(MIN, factor, count).unwrap();
291 *buckets.last_mut().unwrap() = max;
292 buckets
293}
294
295impl MetaMetrics {
296 fn new(registry: &Registry, metric_level: MetricLevel) -> Self {
297 let opts = histogram_opts!(
298 "meta_grpc_duration_seconds",
299 "gRPC latency of meta services",
300 exponential_buckets(0.0001, 2.0, 20).unwrap() );
302 let grpc_latency =
303 register_histogram_vec_with_registry!(opts, &["path"], registry).unwrap();
304
305 let opts = histogram_opts!(
306 "meta_barrier_duration_seconds",
307 "barrier latency",
308 latency_buckets(600.0, 20)
309 );
310 let barrier_latency =
311 register_guarded_histogram_vec_with_registry!(opts, &["database_id"], registry)
312 .unwrap();
313
314 let opts = histogram_opts!(
315 "meta_barrier_wait_commit_duration_seconds",
316 "barrier_wait_commit_latency",
317 latency_buckets(10.0, 10)
318 );
319 let barrier_wait_commit_latency =
320 register_histogram_with_registry!(opts, registry).unwrap();
321
322 let barrier_interval_by_database = register_gauge_vec_with_registry!(
323 "meta_barrier_interval_by_database",
324 "barrier interval of each database",
325 &["database_id"],
326 registry
327 )
328 .unwrap();
329
330 let all_barrier_nums = register_guarded_int_gauge_vec_with_registry!(
331 "all_barrier_nums",
332 "num of of all_barrier",
333 &["database_id"],
334 registry
335 )
336 .unwrap();
337 let in_flight_barrier_nums = register_guarded_int_gauge_vec_with_registry!(
338 "in_flight_barrier_nums",
339 "num of of in_flight_barrier",
340 &["database_id"],
341 registry
342 )
343 .unwrap();
344 let last_committed_barrier_time = register_guarded_int_gauge_vec_with_registry!(
345 "last_committed_barrier_time",
346 "The timestamp (UNIX epoch seconds) of the last committed barrier's epoch time.",
347 &["database_id"],
348 registry
349 )
350 .unwrap();
351
352 let opts = histogram_opts!(
354 "meta_snapshot_backfill_barrier_duration_seconds",
355 "snapshot backfill barrier latency",
356 latency_buckets(600.0, 20)
357 );
358 let snapshot_backfill_barrier_latency = register_guarded_histogram_vec_with_registry!(
359 opts,
360 &["table_id", "barrier_type"],
361 registry
362 )
363 .unwrap();
364
365 let snapshot_backfill_lag = register_guarded_int_gauge_vec_with_registry!(
366 "meta_snapshot_backfill_upstream_lag",
367 "snapshot backfill upstream_lag",
368 &["table_id"],
369 registry
370 )
371 .unwrap();
372 let snapshot_backfill_inflight_barrier_num = register_guarded_int_gauge_vec_with_registry!(
373 "meta_snapshot_backfill_inflight_barrier_num",
374 "snapshot backfill inflight_barrier_num",
375 &["table_id"],
376 registry
377 )
378 .unwrap();
379
380 let max_committed_epoch = register_int_gauge_with_registry!(
381 "storage_max_committed_epoch",
382 "max committed epoch",
383 registry
384 )
385 .unwrap();
386
387 let min_committed_epoch = register_int_gauge_with_registry!(
388 "storage_min_committed_epoch",
389 "min committed epoch",
390 registry
391 )
392 .unwrap();
393
394 let level_sst_num = register_int_gauge_vec_with_registry!(
395 "storage_level_sst_num",
396 "num of SSTs in each level",
397 &["level_index"],
398 registry
399 )
400 .unwrap();
401
402 let level_compact_cnt = register_int_gauge_vec_with_registry!(
403 "storage_level_compact_cnt",
404 "num of SSTs to be merged to next level in each level",
405 &["level_index"],
406 registry
407 )
408 .unwrap();
409
410 let compact_frequency = register_int_counter_vec_with_registry!(
411 "storage_level_compact_frequency",
412 "The number of compactions from one level to another level that have completed or failed.",
413 &["compactor", "group", "task_type", "result"],
414 registry
415 )
416 .unwrap();
417 let compact_skip_frequency = register_int_counter_vec_with_registry!(
418 "storage_skip_compact_frequency",
419 "The number of compactions from one level to another level that have been skipped.",
420 &["level", "type"],
421 registry
422 )
423 .unwrap();
424
425 let version_size =
426 register_int_gauge_with_registry!("storage_version_size", "version size", registry)
427 .unwrap();
428
429 let current_version_id = register_int_gauge_with_registry!(
430 "storage_current_version_id",
431 "current version id",
432 registry
433 )
434 .unwrap();
435
436 let checkpoint_version_id = register_int_gauge_with_registry!(
437 "storage_checkpoint_version_id",
438 "checkpoint version id",
439 registry
440 )
441 .unwrap();
442
443 let min_pinned_version_id = register_int_gauge_with_registry!(
444 "storage_min_pinned_version_id",
445 "min pinned version id",
446 registry
447 )
448 .unwrap();
449
450 let write_stop_compaction_groups = register_int_gauge_vec_with_registry!(
451 "storage_write_stop_compaction_groups",
452 "compaction groups of write stop state",
453 &["compaction_group_id"],
454 registry
455 )
456 .unwrap();
457
458 let full_gc_trigger_count = register_int_gauge_with_registry!(
459 "storage_full_gc_trigger_count",
460 "the number of attempts to trigger full GC",
461 registry
462 )
463 .unwrap();
464
465 let opts = histogram_opts!(
466 "storage_full_gc_candidate_object_count",
467 "the number of candidate object to delete after scanning object store",
468 exponential_buckets(1.0, 10.0, 6).unwrap()
469 );
470 let full_gc_candidate_object_count =
471 register_histogram_with_registry!(opts, registry).unwrap();
472
473 let opts = histogram_opts!(
474 "storage_full_gc_selected_object_count",
475 "the number of object to delete after filtering by meta node",
476 exponential_buckets(1.0, 10.0, 6).unwrap()
477 );
478 let full_gc_selected_object_count =
479 register_histogram_with_registry!(opts, registry).unwrap();
480
481 let min_safepoint_version_id = register_int_gauge_with_registry!(
482 "storage_min_safepoint_version_id",
483 "min safepoint version id",
484 registry
485 )
486 .unwrap();
487
488 let level_file_size = register_int_gauge_vec_with_registry!(
489 "storage_level_total_file_size",
490 "KBs total file bytes in each level",
491 &["level_index"],
492 registry
493 )
494 .unwrap();
495
496 let version_stats = register_int_gauge_vec_with_registry!(
497 "storage_version_stats",
498 "per table stats in current hummock version",
499 &["table_id", "metric"],
500 registry
501 )
502 .unwrap();
503
504 let materialized_view_stats = register_int_gauge_vec_with_registry!(
505 "storage_materialized_view_stats",
506 "per materialized view stats in current hummock version",
507 &["table_id", "metric"],
508 registry
509 )
510 .unwrap();
511
512 let stale_object_count = register_int_gauge_with_registry!(
513 "storage_stale_object_count",
514 "total number of objects that is no longer referenced by versions.",
515 registry
516 )
517 .unwrap();
518
519 let stale_object_size = register_int_gauge_with_registry!(
520 "storage_stale_object_size",
521 "total size of objects that is no longer referenced by versions.",
522 registry
523 )
524 .unwrap();
525
526 let old_version_object_count = register_int_gauge_with_registry!(
527 "storage_old_version_object_count",
528 "total number of objects that is still referenced by non-current versions",
529 registry
530 )
531 .unwrap();
532
533 let old_version_object_size = register_int_gauge_with_registry!(
534 "storage_old_version_object_size",
535 "total size of objects that is still referenced by non-current versions",
536 registry
537 )
538 .unwrap();
539
540 let current_version_object_count = register_int_gauge_with_registry!(
541 "storage_current_version_object_count",
542 "total number of objects that is referenced by current version",
543 registry
544 )
545 .unwrap();
546
547 let current_version_object_size = register_int_gauge_with_registry!(
548 "storage_current_version_object_size",
549 "total size of objects that is referenced by current version",
550 registry
551 )
552 .unwrap();
553
554 let total_object_count = register_int_gauge_with_registry!(
555 "storage_total_object_count",
556 "Total number of objects that includes dangling objects. Note that the metric is updated right before full GC. So subsequent full GC may reduce the actual value significantly, without updating the metric.",
557 registry
558 ).unwrap();
559
560 let total_object_size = register_int_gauge_with_registry!(
561 "storage_total_object_size",
562 "Total size of objects that includes dangling objects. Note that the metric is updated right before full GC. So subsequent full GC may reduce the actual value significantly, without updating the metric.",
563 registry
564 ).unwrap();
565
566 let table_change_log_object_count = register_int_gauge_vec_with_registry!(
567 "storage_table_change_log_object_count",
568 "per table change log object count",
569 &["table_id"],
570 registry
571 )
572 .unwrap();
573
574 let table_change_log_object_size = register_int_gauge_vec_with_registry!(
575 "storage_table_change_log_object_size",
576 "per table change log object size",
577 &["table_id"],
578 registry
579 )
580 .unwrap();
581
582 let table_change_log_min_epoch = register_int_gauge_vec_with_registry!(
583 "storage_table_change_log_min_epoch",
584 "min epoch currently retained in table change log",
585 &["table_id"],
586 registry
587 )
588 .unwrap();
589
590 let opts = histogram_opts!(
591 "storage_table_change_log_get_latency",
592 "latency of serving table change log requests",
593 exponential_buckets(0.001, 5.0, 7).unwrap()
594 );
595 let table_change_log_get_latency =
596 register_histogram_with_registry!(opts, registry).unwrap();
597
598 let table_change_log_truncate_latency = register_histogram_with_registry!(
599 "storage_table_change_log_truncate_latency",
600 "latency of truncating persisted table change logs",
601 registry
602 )
603 .unwrap();
604
605 let time_travel_object_count = register_int_gauge_with_registry!(
606 "storage_time_travel_object_count",
607 "total number of objects that is referenced by time travel.",
608 registry
609 )
610 .unwrap();
611
612 let delta_log_count = register_int_gauge_with_registry!(
613 "storage_delta_log_count",
614 "total number of hummock version delta log",
615 registry
616 )
617 .unwrap();
618
619 let opts = histogram_opts!(
620 "storage_version_checkpoint_latency",
621 "hummock version checkpoint latency",
622 exponential_buckets(0.1, 1.5, 20).unwrap()
623 );
624 let version_checkpoint_latency = register_histogram_with_registry!(opts, registry).unwrap();
625
626 let opts = histogram_opts!(
627 "hummock_manager_lock_time",
628 "latency for hummock manager to acquire the rwlock",
629 exponential_buckets(0.02, 2.5, 10).unwrap() );
631 let hummock_manager_lock_time =
632 register_histogram_vec_with_registry!(opts, &["lock_name", "lock_type"], registry)
633 .unwrap();
634
635 let opts = histogram_opts!(
636 "meta_hummock_manager_real_process_time",
637 "latency for hummock manager to really process the request",
638 exponential_buckets(0.02, 2.5, 10).unwrap() );
640 let hummock_manager_real_process_time =
641 register_histogram_vec_with_registry!(opts, &["method", "lock_name"], registry)
642 .unwrap();
643
644 let worker_num = register_int_gauge_vec_with_registry!(
645 "worker_num",
646 "number of nodes in the cluster",
647 &["worker_type"],
648 registry,
649 )
650 .unwrap();
651
652 let meta_type = register_int_gauge_vec_with_registry!(
653 "meta_num",
654 "role of meta nodes in the cluster",
655 &["worker_addr", "role"],
656 registry,
657 )
658 .unwrap();
659
660 let compact_pending_bytes = register_int_gauge_vec_with_registry!(
661 "storage_compact_pending_bytes",
662 "bytes of lsm tree needed to reach balance",
663 &["group"],
664 registry
665 )
666 .unwrap();
667
668 let compact_level_compression_ratio = register_gauge_vec_with_registry!(
669 "storage_compact_level_compression_ratio",
670 "compression ratio of each level of the lsm tree",
671 &["group", "level", "algorithm"],
672 registry
673 )
674 .unwrap();
675
676 let level_compact_task_cnt = register_int_gauge_vec_with_registry!(
677 "storage_level_compact_task_cnt",
678 "num of compact_task organized by group and level",
679 &["task"],
680 registry
681 )
682 .unwrap();
683
684 let time_travel_vacuum_metadata_latency = register_histogram_with_registry!(
685 histogram_opts!(
686 "storage_time_travel_vacuum_metadata_latency",
687 "Latency of vacuuming metadata for time travel",
688 exponential_buckets(0.1, 1.5, 20).unwrap()
689 ),
690 registry
691 )
692 .unwrap();
693 let time_travel_write_metadata_latency = register_histogram_with_registry!(
694 histogram_opts!(
695 "storage_time_travel_write_metadata_latency",
696 "Latency of writing metadata for time travel",
697 exponential_buckets(0.1, 1.5, 20).unwrap()
698 ),
699 registry
700 )
701 .unwrap();
702
703 let object_store_metric = Arc::new(GLOBAL_OBJECT_STORE_METRICS.clone());
704
705 let recovery_failure_cnt = register_int_counter_vec_with_registry!(
706 "recovery_failure_cnt",
707 "Number of failed recovery attempts",
708 &["recovery_type"],
709 registry
710 )
711 .unwrap();
712 let opts = histogram_opts!(
713 "recovery_latency",
714 "Latency of the recovery process",
715 exponential_buckets(0.1, 1.5, 20).unwrap() );
717 let recovery_latency =
718 register_histogram_vec_with_registry!(opts, &["recovery_type"], registry).unwrap();
719
720 let auto_schema_change_failure_cnt = register_int_counter_vec_with_registry!(
721 "auto_schema_change_failure_cnt",
722 "Number of failed auto schema change",
723 &["table_id", "table_name"],
724 registry
725 )
726 .unwrap();
727
728 let auto_schema_change_success_cnt = register_int_counter_vec_with_registry!(
729 "auto_schema_change_success_cnt",
730 "Number of success auto schema change",
731 &["table_id", "table_name"],
732 registry
733 )
734 .unwrap();
735
736 let opts = histogram_opts!(
737 "auto_schema_change_latency",
738 "Latency of the auto schema change process",
739 exponential_buckets(0.1, 1.5, 20).unwrap() );
741 let auto_schema_change_latency =
742 register_histogram_vec_with_registry!(opts, &["table_id", "table_name"], registry)
743 .unwrap();
744
745 let source_is_up = register_guarded_int_gauge_vec_with_registry!(
746 "source_status_is_up",
747 "source is up or not",
748 &["source_id", "source_name"],
749 registry
750 )
751 .unwrap();
752 let opts = histogram_opts!(
753 "source_worker_tick_duration_seconds",
754 "Duration of a source worker tick (list_splits + on_tick) in seconds",
755 exponential_buckets(0.1, 1.5, 20).unwrap() );
757 let source_worker_tick_duration_seconds = register_guarded_histogram_vec_with_registry!(
758 opts,
759 &["source_id", "source_name"],
760 registry
761 )
762 .unwrap();
763 let source_enumerator_monitor_error_count =
764 register_guarded_int_counter_vec_with_registry!(
765 "source_enumerator_monitor_error_count",
766 "Number of source enumerator on_tick monitor round-trip failures",
767 &["source_id", "source_name"],
768 registry
769 )
770 .unwrap();
771 let source_enumerator_metrics = Arc::new(SourceEnumeratorMetrics::default());
772
773 let actor_info = register_int_gauge_vec_with_registry!(
774 "actor_info",
775 "Mapping from actor id to (fragment id, compute node)",
776 &["actor_id", "fragment_id", "compute_node"],
777 registry
778 )
779 .unwrap();
780
781 let table_info = register_int_gauge_vec_with_registry!(
782 "table_info",
783 "Mapping from table id to (actor id, table name)",
784 &[
785 "materialized_view_id",
786 "table_id",
787 "fragment_id",
788 "table_name",
789 "table_type",
790 "compaction_group_id"
791 ],
792 registry
793 )
794 .unwrap();
795
796 let sink_info = register_int_gauge_vec_with_registry!(
797 "sink_info",
798 "Mapping from actor id to (actor id, sink name)",
799 &["actor_id", "sink_id", "sink_name",],
800 registry
801 )
802 .unwrap()
803 .relabel_debug_1(metric_level);
804
805 let relation_info = register_int_gauge_vec_with_registry!(
806 "relation_info",
807 "Information of the database relation (table/source/sink/materialized view/index/internal)",
808 &["id", "database", "schema", "name", "resource_group", "type"],
809 registry
810 )
811 .unwrap();
812
813 let streaming_table_change_log_retention_seconds = register_int_gauge_vec_with_registry!(
814 "streaming_table_change_log_retention_seconds",
815 "Max subscription retention configured for the table change log in seconds",
816 &["table_id"],
817 registry
818 )
819 .unwrap();
820
821 let database_info = register_int_gauge_vec_with_registry!(
822 "database_info",
823 "Mapping from database id to database name",
824 &["database_id", "database_name"],
825 registry
826 )
827 .unwrap();
828
829 let backfill_fragment_progress = register_int_gauge_vec_with_registry!(
830 "backfill_fragment_progress",
831 "Backfill progress per fragment",
832 &[
833 "job_id",
834 "fragment_id",
835 "backfill_state_table_id",
836 "backfill_target_relation_id",
837 "backfill_target_relation_name",
838 "backfill_target_relation_type",
839 "backfill_type",
840 "backfill_epoch",
841 "upstream_type",
842 "backfill_progress",
843 ],
844 registry
845 )
846 .unwrap();
847
848 let l0_compact_level_count = register_histogram_vec_with_registry!(
849 "storage_l0_compact_level_count",
850 "level_count of l0 compact task",
851 &["group", "type"],
852 registry
853 )
854 .unwrap();
855
856 let system_param_info = register_int_gauge_vec_with_registry!(
858 "system_param_info",
859 "Information of system parameters",
860 &["name", "value"],
861 registry
862 )
863 .unwrap();
864
865 let opts = histogram_opts!(
866 "storage_compact_task_size",
867 "Total size of compact that have been issued to state store",
868 exponential_buckets(1048576.0, 2.0, 16).unwrap()
869 );
870
871 let compact_task_size =
872 register_histogram_vec_with_registry!(opts, &["group", "type"], registry).unwrap();
873
874 let compact_task_file_count = register_histogram_vec_with_registry!(
875 "storage_compact_task_file_count",
876 "file count of compact task",
877 &["group", "type"],
878 registry
879 )
880 .unwrap();
881 let opts = histogram_opts!(
882 "storage_compact_task_batch_count",
883 "count of compact task batch",
884 exponential_buckets(1.0, 2.0, 8).unwrap()
885 );
886 let compact_task_batch_count =
887 register_histogram_vec_with_registry!(opts, &["type"], registry).unwrap();
888
889 let table_write_throughput = register_int_counter_vec_with_registry!(
890 "storage_commit_write_throughput",
891 "The number of compactions from one level to another level that have been skipped.",
892 &["table_id"],
893 registry
894 )
895 .unwrap();
896
897 let split_compaction_group_count = register_int_counter_vec_with_registry!(
898 "storage_split_compaction_group_count",
899 "Count of trigger split compaction group",
900 &["group"],
901 registry
902 )
903 .unwrap();
904
905 let state_table_count = register_int_gauge_vec_with_registry!(
906 "storage_state_table_count",
907 "Count of stable table per compaction group",
908 &["group"],
909 registry
910 )
911 .unwrap();
912
913 let branched_sst_count = register_int_gauge_vec_with_registry!(
914 "storage_branched_sst_count",
915 "Count of branched sst per compaction group",
916 &["group"],
917 registry
918 )
919 .unwrap();
920
921 let opts = histogram_opts!(
922 "storage_compaction_event_consumed_latency",
923 "The latency(ms) of each event being consumed",
924 exponential_buckets(1.0, 1.5, 30).unwrap() );
926 let compaction_event_consumed_latency =
927 register_histogram_with_registry!(opts, registry).unwrap();
928
929 let opts = histogram_opts!(
930 "storage_compaction_event_loop_iteration_latency",
931 "The latency(ms) of each iteration of the compaction event loop",
932 exponential_buckets(1.0, 1.5, 30).unwrap() );
934 let compaction_event_loop_iteration_latency =
935 register_histogram_with_registry!(opts, registry).unwrap();
936
937 let merge_compaction_group_count = register_int_counter_vec_with_registry!(
938 "storage_merge_compaction_group_count",
939 "Count of trigger merge compaction group",
940 &["group"],
941 registry
942 )
943 .unwrap();
944
945 let opts = histogram_opts!(
946 "storage_time_travel_version_replay_latency",
947 "The latency(ms) of replaying a hummock version for time travel",
948 exponential_buckets(0.01, 10.0, 6).unwrap()
949 );
950 let time_travel_version_replay_latency =
951 register_histogram_with_registry!(opts, registry).unwrap();
952
953 let compaction_group_count = register_int_gauge_with_registry!(
954 "storage_compaction_group_count",
955 "The number of compaction groups",
956 registry,
957 )
958 .unwrap();
959
960 let compaction_group_size = register_int_gauge_vec_with_registry!(
961 "storage_compaction_group_size",
962 "The size of compaction group",
963 &["group"],
964 registry
965 )
966 .unwrap();
967
968 let compaction_group_file_count = register_int_gauge_vec_with_registry!(
969 "storage_compaction_group_file_count",
970 "The file count of compaction group",
971 &["group"],
972 registry
973 )
974 .unwrap();
975
976 let compaction_group_throughput = register_int_gauge_vec_with_registry!(
977 "storage_compaction_group_throughput",
978 "The throughput of compaction group",
979 &["group"],
980 registry
981 )
982 .unwrap();
983
984 let opts = histogram_opts!(
985 "storage_compact_task_trivial_move_sst_count",
986 "sst count of compact trivial-move task",
987 exponential_buckets(1.0, 2.0, 8).unwrap()
988 );
989 let compact_task_trivial_move_sst_count =
990 register_histogram_vec_with_registry!(opts, &["group"], registry).unwrap();
991
992 let refresh_job_duration = register_guarded_uint_gauge_vec_with_registry!(
993 "meta_refresh_job_duration",
994 "The duration of refresh job",
995 &["table_id", "status"],
996 registry
997 )
998 .unwrap();
999 let refresh_job_finish_cnt = register_guarded_int_counter_vec_with_registry!(
1000 "meta_refresh_job_finish_cnt",
1001 "The number of finished refresh jobs",
1002 &["table_id", "status"],
1003 registry
1004 )
1005 .unwrap();
1006 let refresh_cron_job_trigger_cnt = register_guarded_int_counter_vec_with_registry!(
1007 "meta_refresh_cron_job_trigger_cnt",
1008 "The number of cron refresh jobs triggered",
1009 &["table_id"],
1010 registry
1011 )
1012 .unwrap();
1013 let refresh_cron_job_miss_cnt = register_guarded_int_counter_vec_with_registry!(
1014 "meta_refresh_cron_job_miss_cnt",
1015 "The number of cron refresh jobs missed",
1016 &["table_id"],
1017 registry
1018 )
1019 .unwrap();
1020
1021 Self {
1022 grpc_latency,
1023 barrier_latency,
1024 barrier_wait_commit_latency,
1025 all_barrier_nums,
1026 in_flight_barrier_nums,
1027 last_committed_barrier_time,
1028 barrier_interval_by_database,
1029 snapshot_backfill_barrier_latency,
1030 snapshot_backfill_lag,
1031 snapshot_backfill_inflight_barrier_num,
1032 recovery_failure_cnt,
1033 recovery_latency,
1034
1035 max_committed_epoch,
1036 min_committed_epoch,
1037 level_sst_num,
1038 level_compact_cnt,
1039 compact_frequency,
1040 compact_skip_frequency,
1041 level_file_size,
1042 version_size,
1043 version_stats,
1044 materialized_view_stats,
1045 stale_object_count,
1046 stale_object_size,
1047 old_version_object_count,
1048 old_version_object_size,
1049 time_travel_object_count,
1050 current_version_object_count,
1051 current_version_object_size,
1052 total_object_count,
1053 total_object_size,
1054 table_change_log_object_count,
1055 table_change_log_object_size,
1056 table_change_log_min_epoch,
1057 table_change_log_get_latency,
1058 table_change_log_truncate_latency,
1059 delta_log_count,
1060 version_checkpoint_latency,
1061 current_version_id,
1062 checkpoint_version_id,
1063 min_pinned_version_id,
1064 min_safepoint_version_id,
1065 write_stop_compaction_groups,
1066 full_gc_trigger_count,
1067 full_gc_candidate_object_count,
1068 full_gc_selected_object_count,
1069 hummock_manager_lock_time,
1070 hummock_manager_real_process_time,
1071 time_after_last_observation: Arc::new(AtomicU64::new(0)),
1072 worker_num,
1073 meta_type,
1074 compact_pending_bytes,
1075 compact_level_compression_ratio,
1076 level_compact_task_cnt,
1077 object_store_metric,
1078 source_is_up,
1079 source_worker_tick_duration_seconds,
1080 source_enumerator_monitor_error_count,
1081 source_enumerator_metrics,
1082 actor_info,
1083 table_info,
1084 sink_info,
1085 relation_info,
1086 database_info,
1087 backfill_fragment_progress,
1088 streaming_table_change_log_retention_seconds,
1089 system_param_info,
1090 l0_compact_level_count,
1091 compact_task_size,
1092 compact_task_file_count,
1093 compact_task_batch_count,
1094 compact_task_trivial_move_sst_count,
1095 table_write_throughput,
1096 split_compaction_group_count,
1097 state_table_count,
1098 branched_sst_count,
1099 compaction_event_consumed_latency,
1100 compaction_event_loop_iteration_latency,
1101 auto_schema_change_failure_cnt,
1102 auto_schema_change_success_cnt,
1103 auto_schema_change_latency,
1104 merge_compaction_group_count,
1105 time_travel_version_replay_latency,
1106 compaction_group_count,
1107 compaction_group_size,
1108 compaction_group_file_count,
1109 compaction_group_throughput,
1110 refresh_job_duration,
1111 refresh_job_finish_cnt,
1112 refresh_cron_job_trigger_cnt,
1113 refresh_cron_job_miss_cnt,
1114 time_travel_vacuum_metadata_latency,
1115 time_travel_write_metadata_latency,
1116 }
1117 }
1118
1119 #[cfg(test)]
1120 pub fn for_test(registry: &Registry) -> Self {
1121 Self::new(registry, MetricLevel::Debug)
1122 }
1123}
1124impl Default for MetaMetrics {
1125 fn default() -> Self {
1126 GLOBAL_META_METRICS.clone()
1127 }
1128}
1129
1130pub async fn refresh_system_param_info_metrics(
1132 system_params_controller: &SystemParamsControllerRef,
1133 meta_metrics: Arc<MetaMetrics>,
1134) {
1135 let params_info = system_params_controller.get_params().await.get_all();
1136
1137 meta_metrics.system_param_info.reset();
1138 for info in params_info {
1139 meta_metrics
1140 .system_param_info
1141 .with_label_values(&[info.name, &info.value])
1142 .set(1);
1143 }
1144}
1145
1146pub fn start_worker_info_monitor(
1147 metadata_manager: MetadataManager,
1148 election_client: ElectionClientRef,
1149 interval: Duration,
1150 meta_metrics: Arc<MetaMetrics>,
1151) -> (JoinHandle<()>, Sender<()>) {
1152 let (shutdown_tx, mut shutdown_rx) = tokio::sync::oneshot::channel();
1153 let join_handle = tokio::spawn(async move {
1154 let mut monitor_interval = tokio::time::interval(interval);
1155 monitor_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
1156 loop {
1157 tokio::select! {
1158 _ = monitor_interval.tick() => {},
1160 _ = &mut shutdown_rx => {
1162 tracing::info!("Worker number monitor is stopped");
1163 return;
1164 }
1165 }
1166
1167 let node_map = match metadata_manager.count_worker_node().await {
1168 Ok(node_map) => node_map,
1169 Err(err) => {
1170 tracing::warn!(error = %err.as_report(), "failed to count worker nodes");
1171 continue;
1172 }
1173 };
1174
1175 meta_metrics.worker_num.reset();
1177 meta_metrics.meta_type.reset();
1178
1179 for (worker_type, worker_num) in node_map {
1180 meta_metrics
1181 .worker_num
1182 .with_label_values(&[(worker_type.as_str_name())])
1183 .set(worker_num as i64);
1184 }
1185 if let Ok(meta_members) = election_client.get_members().await {
1186 meta_metrics
1187 .worker_num
1188 .with_label_values(&[WorkerType::Meta.as_str_name()])
1189 .set(meta_members.len() as i64);
1190 meta_members.into_iter().for_each(|m| {
1191 let role = if m.is_leader { "leader" } else { "follower" };
1192 meta_metrics
1193 .meta_type
1194 .with_label_values(&[m.id.as_str(), role])
1195 .set(1);
1196 });
1197 }
1198 }
1199 });
1200
1201 (join_handle, shutdown_tx)
1202}
1203
1204pub async fn refresh_fragment_info_metrics(
1205 catalog_controller: &CatalogControllerRef,
1206 cluster_controller: &ClusterControllerRef,
1207 hummock_manager: &HummockManagerRef,
1208 meta_metrics: Arc<MetaMetrics>,
1209) {
1210 let worker_nodes = match cluster_controller
1211 .list_workers(Some(WorkerType::ComputeNode.into()), None)
1212 .await
1213 {
1214 Ok(worker_nodes) => worker_nodes,
1215 Err(err) => {
1216 tracing::warn!(error=%err.as_report(), "failed to list worker nodes");
1217 return;
1218 }
1219 };
1220 let actor_locations = match catalog_controller.list_actor_locations() {
1221 Ok(actor_locations) => actor_locations,
1222 Err(err) => {
1223 tracing::warn!(error=%err.as_report(), "failed to get actor locations");
1224 return;
1225 }
1226 };
1227 let sink_actor_mapping = match catalog_controller.list_sink_actor_mapping().await {
1228 Ok(sink_actor_mapping) => sink_actor_mapping,
1229 Err(err) => {
1230 tracing::warn!(error=%err.as_report(), "failed to get sink actor mappings");
1231 return;
1232 }
1233 };
1234 let fragment_state_tables = match catalog_controller.list_fragment_state_tables().await {
1235 Ok(fragment_state_tables) => fragment_state_tables,
1236 Err(err) => {
1237 tracing::warn!(error=%err.as_report(), "failed to get fragment state tables");
1238 return;
1239 }
1240 };
1241 let table_name_and_type_mapping = match catalog_controller.get_table_name_type_mapping().await {
1242 Ok(mapping) => mapping,
1243 Err(err) => {
1244 tracing::warn!(error=%err.as_report(), "failed to get the table name mapping");
1245 return;
1246 }
1247 };
1248
1249 let worker_addr_mapping: HashMap<WorkerId, String> = worker_nodes
1250 .into_iter()
1251 .map(|worker_node| {
1252 let addr = match worker_node.host {
1253 Some(host) => format!("{}:{}", host.host, host.port),
1254 None => "".to_owned(),
1255 };
1256 (worker_node.id, addr)
1257 })
1258 .collect();
1259 let table_compaction_group_id_mapping = hummock_manager
1260 .get_table_compaction_group_id_mapping()
1261 .await;
1262
1263 meta_metrics.actor_info.reset();
1266 meta_metrics.table_info.reset();
1267 meta_metrics.sink_info.reset();
1268 for actor_location in actor_locations {
1269 let actor_id_str = actor_location.actor_id.to_string();
1270 let fragment_id_str = actor_location.fragment_id.to_string();
1271 if let Some(address) = worker_addr_mapping.get(&actor_location.worker_id) {
1274 meta_metrics
1275 .actor_info
1276 .with_label_values(&[&actor_id_str, &fragment_id_str, address])
1277 .set(1);
1278 }
1279 }
1280 for (sink_id, (sink_name, actor_ids)) in sink_actor_mapping {
1281 let sink_id_str = sink_id.to_string();
1282 for actor_id in actor_ids {
1283 let actor_id_str = actor_id.to_string();
1284 meta_metrics
1285 .sink_info
1286 .with_label_values(&[&actor_id_str, &sink_id_str, &sink_name])
1287 .set(1);
1288 }
1289 }
1290 for PartialFragmentStateTables {
1291 fragment_id,
1292 job_id,
1293 state_table_ids,
1294 } in fragment_state_tables
1295 {
1296 let fragment_id_str = fragment_id.to_string();
1297 let job_id_str = job_id.to_string();
1298 for table_id in state_table_ids.into_inner() {
1299 let table_id_str = table_id.to_string();
1300 let (table_name, table_type) = table_name_and_type_mapping
1301 .get(&table_id)
1302 .cloned()
1303 .unwrap_or_else(|| ("unknown".to_owned(), "unknown".to_owned()));
1304 let compaction_group_id = table_compaction_group_id_mapping
1305 .get(&table_id)
1306 .map(|cg_id| cg_id.to_string())
1307 .unwrap_or_else(|| "unknown".to_owned());
1308 meta_metrics
1309 .table_info
1310 .with_label_values(&[
1311 &job_id_str,
1312 &table_id_str,
1313 &fragment_id_str,
1314 &table_name,
1315 &table_type,
1316 &compaction_group_id,
1317 ])
1318 .set(1);
1319 }
1320 }
1321}
1322
1323pub async fn refresh_relation_info_metrics(
1324 catalog_controller: &CatalogControllerRef,
1325 meta_metrics: Arc<MetaMetrics>,
1326) {
1327 let table_objects = match catalog_controller.list_table_objects().await {
1328 Ok(table_objects) => table_objects,
1329 Err(err) => {
1330 tracing::warn!(error=%err.as_report(), "failed to get table objects");
1331 return;
1332 }
1333 };
1334
1335 let source_objects = match catalog_controller.list_source_objects().await {
1336 Ok(source_objects) => source_objects,
1337 Err(err) => {
1338 tracing::warn!(error=%err.as_report(), "failed to get source objects");
1339 return;
1340 }
1341 };
1342
1343 let sink_objects = match catalog_controller.list_sink_objects().await {
1344 Ok(sink_objects) => sink_objects,
1345 Err(err) => {
1346 tracing::warn!(error=%err.as_report(), "failed to get sink objects");
1347 return;
1348 }
1349 };
1350 let subscriptions = match catalog_controller.list_subscriptions().await {
1351 Ok(subscriptions) => subscriptions,
1352 Err(err) => {
1353 tracing::warn!(error=%err.as_report(), "fail to get subscription objects");
1354 return;
1355 }
1356 };
1357
1358 meta_metrics.relation_info.reset();
1359 meta_metrics
1360 .streaming_table_change_log_retention_seconds
1361 .reset();
1362
1363 let mut active_table_labels = HashSet::with_capacity(table_objects.len());
1364 for (id, db, schema, name, resource_group, table_type) in table_objects {
1365 let table_id = id.to_string();
1366 active_table_labels.insert((table_id.clone(), name.clone()));
1367 let relation_type = match table_type {
1368 TableType::Table => "table",
1369 TableType::MaterializedView => "materialized_view",
1370 TableType::Index | TableType::VectorIndex => "index",
1371 TableType::Internal => "internal",
1372 };
1373 meta_metrics
1374 .relation_info
1375 .with_label_values(&[
1376 &table_id,
1377 &db,
1378 &schema,
1379 &name,
1380 &resource_group,
1381 &relation_type.to_owned(),
1382 ])
1383 .set(1);
1384 }
1385
1386 retain_table_metric_series(
1387 &meta_metrics.auto_schema_change_failure_cnt,
1388 &active_table_labels,
1389 );
1390 retain_table_metric_series(
1391 &meta_metrics.auto_schema_change_success_cnt,
1392 &active_table_labels,
1393 );
1394 retain_table_metric_series(
1395 &meta_metrics.auto_schema_change_latency,
1396 &active_table_labels,
1397 );
1398
1399 for (id, db, schema, name, resource_group) in source_objects {
1400 meta_metrics
1401 .relation_info
1402 .with_label_values(&[
1403 &id.to_string(),
1404 &db,
1405 &schema,
1406 &name,
1407 &resource_group,
1408 &"source".to_owned(),
1409 ])
1410 .set(1);
1411 }
1412
1413 for (id, db, schema, name, resource_group) in sink_objects {
1414 meta_metrics
1415 .relation_info
1416 .with_label_values(&[
1417 &id.to_string(),
1418 &db,
1419 &schema,
1420 &name,
1421 &resource_group,
1422 &"sink".to_owned(),
1423 ])
1424 .set(1);
1425 }
1426
1427 let mut max_retention_by_table = HashMap::new();
1428 for subscription in subscriptions {
1429 max_retention_by_table
1430 .entry(subscription.dependent_table_id)
1431 .and_modify(|retention: &mut u64| {
1432 *retention = (*retention).max(subscription.retention_seconds);
1433 })
1434 .or_insert(subscription.retention_seconds);
1435 }
1436 for (table_id, retention_seconds) in max_retention_by_table {
1437 meta_metrics
1438 .streaming_table_change_log_retention_seconds
1439 .with_label_values(&[&table_id.to_string()])
1440 .set(retention_seconds as _);
1441 }
1442}
1443
1444fn retain_table_metric_series<T>(
1445 metric_vec: &MetricVec<T>,
1446 active_table_labels: &HashSet<(String, String)>,
1447) where
1448 T: MetricVecBuilder,
1449{
1450 for (table_id, table_name) in collect_table_labels(metric_vec).difference(active_table_labels) {
1451 let labels = [table_id.as_str(), table_name.as_str()];
1452 metric_vec.remove_label_values(&labels).ok();
1453 }
1454}
1455
1456fn collect_table_labels(collector: &impl Collector) -> HashSet<(String, String)> {
1457 collector
1458 .collect()
1459 .into_iter()
1460 .flat_map(|mut family| family.take_metric())
1461 .filter_map(|metric| {
1462 let table_id = metric
1463 .get_label()
1464 .iter()
1465 .find(|label| label.name() == "table_id")?
1466 .value()
1467 .to_owned();
1468 let table_name = metric
1469 .get_label()
1470 .iter()
1471 .find(|label| label.name() == "table_name")?
1472 .value()
1473 .to_owned();
1474 Some((table_id, table_name))
1475 })
1476 .collect()
1477}
1478
1479pub async fn refresh_database_info_metrics(
1480 catalog_controller: &CatalogControllerRef,
1481 meta_metrics: Arc<MetaMetrics>,
1482) {
1483 let databases = match catalog_controller.list_databases().await {
1484 Ok(databases) => databases,
1485 Err(err) => {
1486 tracing::warn!(error=%err.as_report(), "failed to get databases");
1487 return;
1488 }
1489 };
1490
1491 meta_metrics.database_info.reset();
1492
1493 for db in databases {
1494 meta_metrics
1495 .database_info
1496 .with_label_values(&[&db.id.to_string(), &db.name])
1497 .set(1);
1498 }
1499}
1500
1501fn extract_backfill_fragment_info(
1502 distribution: &FragmentDistribution,
1503) -> Option<BackfillFragmentInfo> {
1504 let backfill_type =
1505 if distribution.fragment_type_mask & FragmentTypeFlag::SourceScan as u32 != 0 {
1506 "SOURCE"
1507 } else if distribution.fragment_type_mask
1508 & (FragmentTypeFlag::SnapshotBackfillStreamScan as u32
1509 | FragmentTypeFlag::CrossDbSnapshotBackfillStreamScan as u32)
1510 != 0
1511 {
1512 "SNAPSHOT_BACKFILL"
1513 } else if distribution.fragment_type_mask & FragmentTypeFlag::StreamScan as u32 != 0 {
1514 "ARRANGEMENT_OR_NO_SHUFFLE"
1515 } else {
1516 return None;
1517 };
1518
1519 let stream_node = distribution.node.as_ref()?;
1520 let mut info = None;
1521 match backfill_type {
1522 "SOURCE" => {
1523 visit_stream_node_source_backfill(stream_node, |node| {
1524 info = Some(BackfillFragmentInfo {
1525 job_id: distribution.table_id.as_raw_id(),
1526 fragment_id: distribution.fragment_id.as_raw_id(),
1527 backfill_state_table_id: node
1528 .state_table
1529 .as_ref()
1530 .map(|table| table.id.as_raw_id())
1531 .unwrap_or_default(),
1532 backfill_target_relation_id: node.upstream_source_id.as_raw_id(),
1533 backfill_type,
1534 backfill_epoch: 0,
1535 });
1536 });
1537 }
1538 "SNAPSHOT_BACKFILL" | "ARRANGEMENT_OR_NO_SHUFFLE" => {
1539 visit_stream_node_stream_scan(stream_node, |node| {
1540 info = Some(BackfillFragmentInfo {
1541 job_id: distribution.table_id.as_raw_id(),
1542 fragment_id: distribution.fragment_id.as_raw_id(),
1543 backfill_state_table_id: node
1544 .state_table
1545 .as_ref()
1546 .map(|table| table.id.as_raw_id())
1547 .unwrap_or_default(),
1548 backfill_target_relation_id: node.table_id.as_raw_id(),
1549 backfill_type,
1550 backfill_epoch: node.snapshot_backfill_epoch.unwrap_or_default(),
1551 });
1552 });
1553 }
1554 _ => {}
1555 }
1556
1557 info
1558}
1559
1560pub async fn refresh_backfill_progress_metrics(
1561 catalog_controller: &CatalogControllerRef,
1562 hummock_manager: &HummockManagerRef,
1563 barrier_manager: &BarrierManagerRef,
1564 meta_metrics: Arc<MetaMetrics>,
1565) {
1566 let fragment_descs = match catalog_controller.list_fragment_descs_with_node(true).await {
1567 Ok(fragment_descs) => fragment_descs,
1568 Err(err) => {
1569 tracing::warn!(error=%err.as_report(), "failed to list fragment descriptions for creating jobs");
1570 return;
1571 }
1572 };
1573
1574 let backfill_infos: HashMap<(u32, u32), BackfillFragmentInfo> = fragment_descs
1575 .iter()
1576 .filter_map(|(distribution, _)| extract_backfill_fragment_info(distribution))
1577 .map(|info| ((info.job_id, info.fragment_id), info))
1578 .collect();
1579
1580 let fragment_progresses = match barrier_manager.get_fragment_backfill_progress().await {
1581 Ok(progress) => progress,
1582 Err(err) => {
1583 tracing::warn!(error=%err.as_report(), "failed to get fragment backfill progress");
1584 return;
1585 }
1586 };
1587
1588 let progress_by_fragment: HashMap<(u32, u32), _> = fragment_progresses
1589 .into_iter()
1590 .map(|progress| {
1591 (
1592 (
1593 progress.job_id.as_raw_id(),
1594 progress.fragment_id.as_raw_id(),
1595 ),
1596 progress,
1597 )
1598 })
1599 .collect();
1600
1601 let version_stats = hummock_manager.get_version_stats().await;
1602
1603 let relation_ids: HashSet<_> = backfill_infos
1604 .values()
1605 .map(|info| ObjectId::new(info.backfill_target_relation_id))
1606 .collect();
1607 let relation_objects = match catalog_controller
1608 .list_relation_objects_by_ids(&relation_ids)
1609 .await
1610 {
1611 Ok(relation_objects) => relation_objects,
1612 Err(err) => {
1613 tracing::warn!(error=%err.as_report(), "failed to get relation objects");
1614 return;
1615 }
1616 };
1617
1618 let mut relation_info = HashMap::new();
1619 for (id, db, schema, name, rel_type) in relation_objects {
1620 relation_info.insert(id.as_raw_id(), (db, schema, name, rel_type));
1621 }
1622
1623 meta_metrics.backfill_fragment_progress.reset();
1624
1625 for info in backfill_infos.values() {
1626 let progress = progress_by_fragment.get(&(info.job_id, info.fragment_id));
1627 let (db, schema, name, rel_type) = relation_info
1628 .get(&info.backfill_target_relation_id)
1629 .cloned()
1630 .unwrap_or_else(|| {
1631 (
1632 "unknown".to_owned(),
1633 "unknown".to_owned(),
1634 "unknown".to_owned(),
1635 "unknown".to_owned(),
1636 )
1637 });
1638
1639 let job_id_str = info.job_id.to_string();
1640 let fragment_id_str = info.fragment_id.to_string();
1641 let backfill_state_table_id_str = info.backfill_state_table_id.to_string();
1642 let backfill_target_relation_id_str = info.backfill_target_relation_id.to_string();
1643 let backfill_target_relation_name_str = format!("{db}.{schema}.{name}");
1644 let backfill_target_relation_type_str = rel_type;
1645 let backfill_type_str = info.backfill_type.to_owned();
1646 let backfill_epoch_str = info.backfill_epoch.to_string();
1647 let total_key_count = version_stats
1648 .table_stats
1649 .get(&TableId::new(info.backfill_target_relation_id))
1650 .map(|stats| stats.total_key_count as u64);
1651
1652 let progress_label = match (info.backfill_type, progress) {
1653 ("SOURCE", Some(progress)) => format!("{} consumed rows", progress.consumed_rows),
1654 ("SOURCE", None) => "0 consumed rows".to_owned(),
1655 (_, Some(progress)) if progress.done => {
1656 let total = total_key_count.unwrap_or(0);
1657 format!("100.0000% ({}/{})", total, total)
1658 }
1659 (_, Some(progress)) => {
1660 let total = total_key_count.unwrap_or(0);
1661 if total == 0 {
1662 "100.0000% (0/0)".to_owned()
1663 } else {
1664 let raw = (progress.consumed_rows as f64) / (total as f64) * 100.0;
1665 format!(
1666 "{:.4}% ({}/{})",
1667 raw.min(100.0),
1668 progress.consumed_rows,
1669 total
1670 )
1671 }
1672 }
1673 (_, None) => "0.0000% (0/0)".to_owned(),
1674 };
1675 let upstream_type_str = progress
1676 .map(|progress| progress.upstream_type.to_string())
1677 .unwrap_or_else(|| "Unknown".to_owned());
1678
1679 meta_metrics
1680 .backfill_fragment_progress
1681 .with_label_values(&[
1682 &job_id_str,
1683 &fragment_id_str,
1684 &backfill_state_table_id_str,
1685 &backfill_target_relation_id_str,
1686 &backfill_target_relation_name_str,
1687 &backfill_target_relation_type_str,
1688 &backfill_type_str,
1689 &backfill_epoch_str,
1690 &upstream_type_str,
1691 &progress_label,
1692 ])
1693 .set(1);
1694 }
1695}
1696
1697pub fn start_info_monitor(
1698 metadata_manager: MetadataManager,
1699 hummock_manager: HummockManagerRef,
1700 barrier_manager: BarrierManagerRef,
1701 system_params_controller: SystemParamsControllerRef,
1702 meta_metrics: Arc<MetaMetrics>,
1703) -> (JoinHandle<()>, Sender<()>) {
1704 const COLLECT_INTERVAL_SECONDS: u64 = 60;
1705
1706 let (shutdown_tx, mut shutdown_rx) = tokio::sync::oneshot::channel();
1707 let join_handle = tokio::spawn(async move {
1708 let mut monitor_interval =
1709 tokio::time::interval(Duration::from_secs(COLLECT_INTERVAL_SECONDS));
1710 monitor_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
1711 loop {
1712 tokio::select! {
1713 _ = monitor_interval.tick() => {},
1715 _ = &mut shutdown_rx => {
1717 tracing::info!("Meta info monitor is stopped");
1718 return;
1719 }
1720 }
1721
1722 refresh_fragment_info_metrics(
1724 &metadata_manager.catalog_controller,
1725 &metadata_manager.cluster_controller,
1726 &hummock_manager,
1727 meta_metrics.clone(),
1728 )
1729 .await;
1730
1731 refresh_relation_info_metrics(
1732 &metadata_manager.catalog_controller,
1733 meta_metrics.clone(),
1734 )
1735 .await;
1736
1737 refresh_database_info_metrics(
1738 &metadata_manager.catalog_controller,
1739 meta_metrics.clone(),
1740 )
1741 .await;
1742
1743 refresh_backfill_progress_metrics(
1744 &metadata_manager.catalog_controller,
1745 &hummock_manager,
1746 &barrier_manager,
1747 meta_metrics.clone(),
1748 )
1749 .await;
1750
1751 refresh_system_param_info_metrics(&system_params_controller, meta_metrics.clone())
1753 .await;
1754 }
1755 });
1756
1757 (join_handle, shutdown_tx)
1758}
1759
1760#[cfg(test)]
1761mod tests {
1762 use risingwave_common::metrics::get_label;
1763
1764 use super::*;
1765
1766 fn actor_id(registry: &Registry, metric_name: &str) -> String {
1767 let metric_family = registry
1768 .gather()
1769 .into_iter()
1770 .find(|family| family.name() == metric_name)
1771 .unwrap();
1772 get_label(metric_family.get_metric().first().unwrap(), "actor_id").unwrap()
1773 }
1774
1775 #[test]
1776 fn actor_info_is_the_only_meta_actor_id_allowlist() {
1777 for level in [MetricLevel::Critical, MetricLevel::Info] {
1778 let registry = Registry::new();
1779 let metrics = MetaMetrics::new(®istry, level);
1780
1781 metrics
1782 .actor_info
1783 .with_label_values(&["11", "22", "compute-0"])
1784 .set(1);
1785 metrics
1786 .sink_info
1787 .with_label_values(&["11", "33", "sink"])
1788 .set(1);
1789
1790 assert_eq!(actor_id(®istry, "actor_info"), "11");
1791 assert_eq!(actor_id(®istry, "sink_info"), "");
1792 }
1793 }
1794
1795 #[test]
1796 fn debug_meta_metrics_retain_actor_id() {
1797 let registry = Registry::new();
1798 let metrics = MetaMetrics::new(®istry, MetricLevel::Debug);
1799
1800 metrics
1801 .sink_info
1802 .with_label_values(&["11", "33", "sink"])
1803 .set(1);
1804
1805 assert_eq!(actor_id(®istry, "sink_info"), "11");
1806 }
1807}