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