Skip to main content

risingwave_meta/rpc/
metrics.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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    // ********************************** Meta ************************************
76    /// The number of workers in the cluster.
77    pub worker_num: IntGaugeVec,
78    /// The roles of all meta nodes in the cluster.
79    pub meta_type: IntGaugeVec,
80
81    // ********************************** gRPC ************************************
82    /// gRPC latency of meta services
83    pub grpc_latency: HistogramVec,
84
85    // ********************************** Barrier ************************************
86    /// The duration from barrier injection to commit
87    /// It is the sum of inflight-latency, sync-latency and wait-commit-latency
88    pub barrier_latency: LabelGuardedHistogramVec,
89    /// The duration from barrier complete to commit
90    pub barrier_wait_commit_latency: Histogram,
91    /// The number of all barriers. It is the sum of barriers that are in-flight or completed but
92    /// waiting for other barriers
93    pub all_barrier_nums: LabelGuardedIntGaugeVec,
94    /// The number of in-flight barriers
95    pub in_flight_barrier_nums: LabelGuardedIntGaugeVec,
96    /// The timestamp (UNIX epoch seconds) of the last committed barrier's epoch time.
97    pub last_committed_barrier_time: LabelGuardedIntGaugeVec,
98    /// The barrier interval of each database
99    pub barrier_interval_by_database: GaugeVec,
100
101    // ********************************** Snapshot Backfill ***************************
102    /// The barrier latency in second of `table_id` and snapshto backfill `barrier_type`
103    pub snapshot_backfill_barrier_latency: LabelGuardedHistogramVec, // (table_id, barrier_type)
104    /// The lags between the upstream epoch and the downstream epoch.
105    pub snapshot_backfill_lag: LabelGuardedIntGaugeVec, // (table_id, )
106    /// The number of inflight barriers of `table_id`
107    pub snapshot_backfill_inflight_barrier_num: LabelGuardedIntGaugeVec, // (table_id, _)
108
109    // ********************************** Recovery ************************************
110    pub recovery_failure_cnt: IntCounterVec,
111    pub recovery_latency: HistogramVec,
112
113    // ********************************** Hummock ************************************
114    /// Max committed epoch
115    pub max_committed_epoch: IntGauge,
116    /// Min committed epoch
117    pub min_committed_epoch: IntGauge,
118    /// The number of SSTs in each level
119    pub level_sst_num: IntGaugeVec,
120    /// The number of SSTs to be merged to next level in each level
121    pub level_compact_cnt: IntGaugeVec,
122    /// The number of compact tasks
123    pub compact_frequency: IntCounterVec,
124    /// Size of each level
125    pub level_file_size: IntGaugeVec,
126    /// Hummock version size
127    pub version_size: IntGauge,
128    /// The version Id of current version.
129    pub current_version_id: IntGauge,
130    /// The version id of checkpoint version.
131    pub checkpoint_version_id: IntGauge,
132    /// The smallest version id that is being pinned by worker nodes.
133    pub min_pinned_version_id: IntGauge,
134    /// The smallest version id that is being guarded by meta node safe points.
135    pub min_safepoint_version_id: IntGauge,
136    /// Compaction groups that is in write stop state.
137    pub write_stop_compaction_groups: IntGaugeVec,
138    /// The number of attempts to trigger full GC.
139    pub full_gc_trigger_count: IntGauge,
140    /// The number of candidate object to delete after scanning object store.
141    pub full_gc_candidate_object_count: Histogram,
142    /// The number of object to delete after filtering by meta node.
143    pub full_gc_selected_object_count: Histogram,
144    /// Hummock version stats
145    pub version_stats: IntGaugeVec,
146    /// Hummock version stats
147    pub materialized_view_stats: IntGaugeVec,
148    /// Total number of objects that is no longer referenced by versions.
149    pub stale_object_count: IntGauge,
150    /// Total size of objects that is no longer referenced by versions.
151    pub stale_object_size: IntGauge,
152    /// Total number of objects that is still referenced by non-current versions.
153    pub old_version_object_count: IntGauge,
154    /// Total size of objects that is still referenced by non-current versions.
155    pub old_version_object_size: IntGauge,
156    /// Total number of objects that is referenced by time travel.
157    pub time_travel_object_count: IntGauge,
158    /// Total number of objects that is referenced by current version.
159    pub current_version_object_count: IntGauge,
160    /// Total size of objects that is referenced by current version.
161    pub current_version_object_size: IntGauge,
162    /// Total number of objects that includes dangling objects.
163    pub total_object_count: IntGauge,
164    /// Total size of objects that includes dangling objects.
165    pub total_object_size: IntGauge,
166    /// Number of objects per table change log.
167    pub table_change_log_object_count: IntGaugeVec,
168    /// Size of objects per table change log.
169    pub table_change_log_object_size: IntGaugeVec,
170    /// Min epoch currently retained in table change log.
171    pub table_change_log_min_epoch: IntGaugeVec,
172    /// Latency of serving table change log requests.
173    pub table_change_log_get_latency: Histogram,
174    /// Latency of truncating persisted table change logs.
175    pub table_change_log_truncate_latency: Histogram,
176    /// The number of hummock version delta log.
177    pub delta_log_count: IntGauge,
178    /// latency of version checkpoint
179    pub version_checkpoint_latency: Histogram,
180    /// Latency for hummock manager to acquire lock
181    pub hummock_manager_lock_time: HistogramVec,
182    /// Latency for hummock manager to really process a request after acquire the lock
183    pub hummock_manager_real_process_time: HistogramVec,
184    /// The number of compactions from one level to another level that have been skipped
185    pub compact_skip_frequency: IntCounterVec,
186    /// Bytes of lsm tree needed to reach balance
187    pub compact_pending_bytes: IntGaugeVec,
188    /// Per level compression ratio
189    pub compact_level_compression_ratio: GenericGaugeVec<AtomicF64>,
190    /// Per level number of running compaction task
191    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    // ********************************** Object Store ************************************
208    // Object store related metrics (for backup/restore and version checkpoint)
209    pub object_store_metric: Arc<ObjectStoreMetrics>,
210
211    // ********************************** Source ************************************
212    /// supervisor for which source is still up.
213    pub source_is_up: LabelGuardedIntGaugeVec,
214    /// Duration of a source worker `tick` execution (`list_splits` + `on_tick`), in seconds.
215    pub source_worker_tick_duration_seconds: LabelGuardedHistogramVec,
216    /// Number of source enumerator `on_tick` monitor round-trip failures.
217    pub source_enumerator_monitor_error_count: LabelGuardedIntCounterVec,
218    pub source_enumerator_metrics: Arc<SourceEnumeratorMetrics>,
219
220    // ********************************** Fragment ************************************
221    /// A dummy gauge metrics with its label to be the mapping from actor id to fragment id.
222    ///
223    /// This is the only metric allowlisted to retain `actor_id` below `Debug`, because dashboards
224    /// use it to map actors to fragments and compute nodes and to count actors on each node.
225    pub actor_info: IntGaugeVec,
226    /// A dummy gauge metrics with its label to be the mapping from table id to actor id
227    pub table_info: IntGaugeVec,
228    /// A dummy gauge metrics with its label to be the mapping from actor id to sink id
229    pub sink_info: RelabeledMetricVec<IntGaugeVec>,
230    /// A dummy gauge metrics with its label to be relation info
231    pub relation_info: IntGaugeVec,
232    /// A dummy gauge metrics with its label to be the mapping from database id to database name
233    pub database_info: IntGaugeVec,
234    /// Backfill progress per fragment
235    pub backfill_fragment_progress: IntGaugeVec,
236    /// Max subscription retention configured for the table's changelog.
237    pub streaming_table_change_log_retention_seconds: IntGaugeVec,
238
239    // ********************************** System Params ************************************
240    /// A dummy gauge metric with labels carrying system parameter info.
241    /// Labels: (name, value)
242    pub system_param_info: IntGaugeVec,
243
244    /// Write throughput of commit epoch for each stable
245    pub table_write_throughput: IntCounterVec,
246
247    /// The number of compaction groups that have been triggered to move
248    pub merge_compaction_group_count: IntCounterVec,
249
250    // ********************************** Auto Schema Change ************************************
251    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    // ********************************** Refresh Manager ************************************
263    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() // max 52s
301        );
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        // snapshot backfill metrics
353        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() // max 76s
630        );
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() // max 76s
639        );
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() // max 221s
716        );
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() // max 221s
740        );
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() // max ~221s
756        );
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        // System parameter info
857        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() // max 191s
925        );
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() // max 191s
933        );
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
1130/// Refresh `system_param_info` metrics by reading current system parameters.
1131pub 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                // Wait for interval
1159                _ = monitor_interval.tick() => {},
1160                // Shutdown monitor
1161                _ = &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            // Reset metrics to clean the stale labels e.g. invalid lease ids
1176            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    // Start fresh with a reset to clear all outdated labels. This is safe since we always
1264    // report full info on each interval.
1265    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        // Report a dummy gauge metrics with (fragment id, actor id, node
1272        // address) as its label
1273        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                // Wait for interval
1714                _ = monitor_interval.tick() => {},
1715                // Shutdown monitor
1716                _ = &mut shutdown_rx => {
1717                    tracing::info!("Meta info monitor is stopped");
1718                    return;
1719                }
1720            }
1721
1722            // Fragment and relation info
1723            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            // System parameter info
1752            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(&registry, 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(&registry, "actor_info"), "11");
1791            assert_eq!(actor_id(&registry, "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(&registry, MetricLevel::Debug);
1799
1800        metrics
1801            .sink_info
1802            .with_label_values(&["11", "33", "sink"])
1803            .set(1);
1804
1805        assert_eq!(actor_id(&registry, "sink_info"), "11");
1806    }
1807}