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