Skip to content

Commit 6c0ebf5

Browse files
authored
Merge pull request MaterializeInc#30568 from teskje/storage-wallclock-metrics
controller: export wallclock lag metrics also for storage collections
2 parents 44b073b + 7220e11 commit 6c0ebf5

File tree

10 files changed

+270
-128
lines changed

10 files changed

+270
-128
lines changed

src/cluster-client/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use anyhow::bail;
2020
use serde::{Deserialize, Serialize};
2121

2222
pub mod client;
23+
pub mod metrics;
2324

2425
/// A function that computes the lag between the given time and wallclock time.
2526
pub type WallclockLagFn<T> = Arc<dyn Fn(&T) -> Duration + Send + Sync>;

src/cluster-client/src/metrics.rs

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright Materialize, Inc. and contributors. All rights reserved.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the LICENSE file.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0.
9+
10+
//! Metrics shared by both compute and storage.
11+
12+
use std::time::Duration;
13+
14+
use mz_ore::metric;
15+
use mz_ore::metrics::{
16+
CounterVec, DeleteOnDropCounter, DeleteOnDropGauge, GaugeVec, IntCounterVec, MetricsRegistry,
17+
};
18+
use mz_ore::stats::SlidingMinMax;
19+
use prometheus::core::{AtomicF64, AtomicU64};
20+
21+
/// Controller metrics.
22+
#[derive(Debug, Clone)]
23+
pub struct ControllerMetrics {
24+
dataflow_wallclock_lag_seconds: GaugeVec,
25+
dataflow_wallclock_lag_seconds_sum: CounterVec,
26+
dataflow_wallclock_lag_seconds_count: IntCounterVec,
27+
}
28+
29+
impl ControllerMetrics {
30+
/// Create a metrics instance registered into the given registry.
31+
pub fn new(metrics_registry: &MetricsRegistry) -> Self {
32+
Self {
33+
// The next three metrics immitate a summary metric type. The `prometheus` crate lacks
34+
// support for summaries, so we roll our own. Note that we also only expose the 0- and
35+
// the 1-quantile, i.e., minimum and maximum lag values.
36+
dataflow_wallclock_lag_seconds: metrics_registry.register(metric!(
37+
name: "mz_dataflow_wallclock_lag_seconds",
38+
help: "A summary of the second-by-second lag of the dataflow frontier relative \
39+
to wallclock time, aggregated over the last minute.",
40+
var_labels: ["instance_id", "replica_id", "collection_id", "quantile"],
41+
)),
42+
dataflow_wallclock_lag_seconds_sum: metrics_registry.register(metric!(
43+
name: "mz_dataflow_wallclock_lag_seconds_sum",
44+
help: "The total sum of dataflow wallclock lag measurements.",
45+
var_labels: ["instance_id", "replica_id", "collection_id"],
46+
)),
47+
dataflow_wallclock_lag_seconds_count: metrics_registry.register(metric!(
48+
name: "mz_dataflow_wallclock_lag_seconds_count",
49+
help: "The total count of dataflow wallclock lag measurements.",
50+
var_labels: ["instance_id", "replica_id", "collection_id"],
51+
)),
52+
}
53+
}
54+
55+
/// Return an object that tracks wallclock lag metrics for the given collection on the given
56+
/// cluster and replica.
57+
pub fn wallclock_lag_metrics(
58+
&self,
59+
collection_id: String,
60+
instance_id: Option<String>,
61+
replica_id: Option<String>,
62+
) -> WallclockLagMetrics {
63+
let labels = vec![
64+
instance_id.unwrap_or_default(),
65+
replica_id.unwrap_or_default(),
66+
collection_id,
67+
];
68+
69+
let labels_with_quantile = |quantile: &str| {
70+
labels
71+
.iter()
72+
.cloned()
73+
.chain([quantile.to_string()])
74+
.collect()
75+
};
76+
77+
let wallclock_lag_seconds_min = self
78+
.dataflow_wallclock_lag_seconds
79+
.get_delete_on_drop_metric(labels_with_quantile("0"));
80+
let wallclock_lag_seconds_max = self
81+
.dataflow_wallclock_lag_seconds
82+
.get_delete_on_drop_metric(labels_with_quantile("1"));
83+
let wallclock_lag_seconds_sum = self
84+
.dataflow_wallclock_lag_seconds_sum
85+
.get_delete_on_drop_metric(labels.clone());
86+
let wallclock_lag_seconds_count = self
87+
.dataflow_wallclock_lag_seconds_count
88+
.get_delete_on_drop_metric(labels);
89+
let wallclock_lag_minmax = SlidingMinMax::new(60);
90+
91+
WallclockLagMetrics {
92+
wallclock_lag_seconds_min,
93+
wallclock_lag_seconds_max,
94+
wallclock_lag_seconds_sum,
95+
wallclock_lag_seconds_count,
96+
wallclock_lag_minmax,
97+
}
98+
}
99+
}
100+
101+
/// Metrics tracking frontier wallclock lag for a collection.
102+
#[derive(Debug)]
103+
pub struct WallclockLagMetrics {
104+
/// Gauge tracking minimum dataflow wallclock lag.
105+
wallclock_lag_seconds_min: DeleteOnDropGauge<'static, AtomicF64, Vec<String>>,
106+
/// Gauge tracking maximum dataflow wallclock lag.
107+
wallclock_lag_seconds_max: DeleteOnDropGauge<'static, AtomicF64, Vec<String>>,
108+
/// Counter tracking the total sum of dataflow wallclock lag.
109+
wallclock_lag_seconds_sum: DeleteOnDropCounter<'static, AtomicF64, Vec<String>>,
110+
/// Counter tracking the total count of dataflow wallclock lag measurements.
111+
wallclock_lag_seconds_count: DeleteOnDropCounter<'static, AtomicU64, Vec<String>>,
112+
113+
/// State maintaining minimum and maximum wallclock lag.
114+
wallclock_lag_minmax: SlidingMinMax<f32>,
115+
}
116+
117+
impl WallclockLagMetrics {
118+
/// Observe a new wallclock lag measurement.
119+
pub fn observe(&mut self, lag: Duration) {
120+
let lag_secs = lag.as_secs_f32();
121+
122+
self.wallclock_lag_minmax.add_sample(lag_secs);
123+
124+
let (&min, &max) = self
125+
.wallclock_lag_minmax
126+
.get()
127+
.expect("just added a sample");
128+
129+
self.wallclock_lag_seconds_min.set(min.into());
130+
self.wallclock_lag_seconds_max.set(max.into());
131+
self.wallclock_lag_seconds_sum.inc_by(lag_secs.into());
132+
self.wallclock_lag_seconds_count.inc();
133+
}
134+
}

src/compute-client/src/controller.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use futures::stream::FuturesUnordered;
3838
use futures::{FutureExt, StreamExt};
3939
use mz_build_info::BuildInfo;
4040
use mz_cluster_client::client::ClusterReplicaLocation;
41+
use mz_cluster_client::metrics::ControllerMetrics;
4142
use mz_cluster_client::{ReplicaId, WallclockLagFn};
4243
use mz_compute_types::dataflows::DataflowDescription;
4344
use mz_compute_types::dyncfgs::COMPUTE_REPLICA_EXPIRATION_OFFSET;
@@ -239,7 +240,8 @@ impl<T: ComputeControllerTimestamp> ComputeController<T> {
239240
storage_collections: StorageCollections<T>,
240241
envd_epoch: NonZeroI64,
241242
read_only: bool,
242-
metrics_registry: MetricsRegistry,
243+
metrics_registry: &MetricsRegistry,
244+
controller_metrics: ControllerMetrics,
243245
now: NowFn,
244246
wallclock_lag: WallclockLagFn<T>,
245247
) -> Self {
@@ -291,6 +293,8 @@ impl<T: ComputeControllerTimestamp> ComputeController<T> {
291293
}
292294
});
293295

296+
let metrics = ComputeControllerMetrics::new(metrics_registry, controller_metrics);
297+
294298
Self {
295299
instances: BTreeMap::new(),
296300
instance_workload_classes,
@@ -302,7 +306,7 @@ impl<T: ComputeControllerTimestamp> ComputeController<T> {
302306
arrangement_exert_proportionality: 16,
303307
stashed_response: None,
304308
envd_epoch,
305-
metrics: ComputeControllerMetrics::new(metrics_registry),
309+
metrics,
306310
now,
307311
wallclock_lag,
308312
dyncfg: Arc::new(mz_dyncfgs::all_dyncfgs()),

src/compute-client/src/controller/instance.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ impl<T: ComputeControllerTimestamp> Instance<T> {
564564
}
565565

566566
if let Some(metrics) = &mut collection.metrics {
567-
metrics.observe_wallclock_lag(lag);
567+
metrics.wallclock_lag.observe(lag);
568568
};
569569
}
570570
}

src/compute-client/src/metrics.rs

+17-86
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,24 @@ use std::borrow::Borrow;
1313
use std::sync::Arc;
1414
use std::time::Duration;
1515

16+
use mz_cluster_client::metrics::{ControllerMetrics, WallclockLagMetrics};
1617
use mz_cluster_client::ReplicaId;
1718
use mz_compute_types::ComputeInstanceId;
1819
use mz_ore::cast::CastFrom;
1920
use mz_ore::metric;
2021
use mz_ore::metrics::raw::UIntGaugeVec;
2122
use mz_ore::metrics::{
22-
CounterVec, DeleteOnDropCounter, DeleteOnDropGauge, DeleteOnDropHistogram, GaugeVec,
23-
HistogramVec, IntCounterVec, MetricVecExt, MetricsRegistry,
23+
DeleteOnDropCounter, DeleteOnDropGauge, DeleteOnDropHistogram, GaugeVec, HistogramVec,
24+
IntCounterVec, MetricVecExt, MetricsRegistry,
2425
};
25-
use mz_ore::stats::{histogram_seconds_buckets, SlidingMinMax};
26+
use mz_ore::stats::histogram_seconds_buckets;
2627
use mz_repr::GlobalId;
2728
use mz_service::codec::StatsCollector;
2829
use prometheus::core::{AtomicF64, AtomicU64};
2930

3031
use crate::protocol::command::{ComputeCommand, ProtoComputeCommand};
3132
use crate::protocol::response::{PeekResponse, ProtoComputeResponse};
3233

33-
type Counter = DeleteOnDropCounter<'static, AtomicF64, Vec<String>>;
3434
pub(crate) type IntCounter = DeleteOnDropCounter<'static, AtomicU64, Vec<String>>;
3535
type Gauge = DeleteOnDropGauge<'static, AtomicF64, Vec<String>>;
3636
/// TODO(database-issues#7533): Add documentation.
@@ -68,14 +68,14 @@ pub struct ComputeControllerMetrics {
6868

6969
// dataflows
7070
dataflow_initial_output_duration_seconds: GaugeVec,
71-
dataflow_wallclock_lag_seconds: GaugeVec,
72-
dataflow_wallclock_lag_seconds_sum: CounterVec,
73-
dataflow_wallclock_lag_seconds_count: IntCounterVec,
71+
72+
/// Metrics shared with the storage controller.
73+
shared: ControllerMetrics,
7474
}
7575

7676
impl ComputeControllerMetrics {
7777
/// Create a metrics instance registered into the given registry.
78-
pub fn new(metrics_registry: MetricsRegistry) -> Self {
78+
pub fn new(metrics_registry: &MetricsRegistry, shared: ControllerMetrics) -> Self {
7979
ComputeControllerMetrics {
8080
commands_total: metrics_registry.register(metric!(
8181
name: "mz_compute_commands_total",
@@ -174,25 +174,7 @@ impl ComputeControllerMetrics {
174174
var_labels: ["instance_id", "replica_id", "collection_id"],
175175
)),
176176

177-
// The next three metrics immitate a summary metric type. The `prometheus` crate lacks
178-
// support for summaries, so we roll our own. Note that we also only expose the 0- and
179-
// the 1-quantile, i.e., minimum and maximum lag values.
180-
dataflow_wallclock_lag_seconds: metrics_registry.register(metric!(
181-
name: "mz_dataflow_wallclock_lag_seconds",
182-
help: "A summary of the second-by-second lag of the dataflow frontier relative \
183-
to wallclock time, aggregated over the last minute.",
184-
var_labels: ["instance_id", "replica_id", "collection_id", "quantile"],
185-
)),
186-
dataflow_wallclock_lag_seconds_sum: metrics_registry.register(metric!(
187-
name: "mz_dataflow_wallclock_lag_seconds_sum",
188-
help: "The total sum of dataflow wallclock lag measurements.",
189-
var_labels: ["instance_id", "replica_id", "collection_id"],
190-
)),
191-
dataflow_wallclock_lag_seconds_count: metrics_registry.register(metric!(
192-
name: "mz_dataflow_wallclock_lag_seconds_count",
193-
help: "The total count of dataflow wallclock lag measurements.",
194-
var_labels: ["instance_id", "replica_id", "collection_id"],
195-
)),
177+
shared,
196178
}
197179
}
198180

@@ -418,44 +400,20 @@ impl ReplicaMetrics {
418400
collection_id.to_string(),
419401
];
420402

421-
let labels_with_quantile = |quantile: &str| {
422-
labels
423-
.iter()
424-
.cloned()
425-
.chain([quantile.to_string()])
426-
.collect()
427-
};
428-
429403
let initial_output_duration_seconds = self
430404
.metrics
431405
.dataflow_initial_output_duration_seconds
432406
.get_delete_on_drop_metric(labels.clone());
433407

434-
let wallclock_lag_seconds_min = self
435-
.metrics
436-
.dataflow_wallclock_lag_seconds
437-
.get_delete_on_drop_metric(labels_with_quantile("0"));
438-
let wallclock_lag_seconds_max = self
439-
.metrics
440-
.dataflow_wallclock_lag_seconds
441-
.get_delete_on_drop_metric(labels_with_quantile("1"));
442-
let wallclock_lag_seconds_sum = self
443-
.metrics
444-
.dataflow_wallclock_lag_seconds_sum
445-
.get_delete_on_drop_metric(labels.clone());
446-
let wallclock_lag_seconds_count = self
447-
.metrics
448-
.dataflow_wallclock_lag_seconds_count
449-
.get_delete_on_drop_metric(labels);
450-
let wallclock_lag_minmax = SlidingMinMax::new(60);
408+
let wallclock_lag = self.metrics.shared.wallclock_lag_metrics(
409+
collection_id.to_string(),
410+
Some(self.instance_id.to_string()),
411+
Some(self.replica_id.to_string()),
412+
);
451413

452414
Some(ReplicaCollectionMetrics {
453415
initial_output_duration_seconds,
454-
wallclock_lag_seconds_min,
455-
wallclock_lag_seconds_max,
456-
wallclock_lag_seconds_sum,
457-
wallclock_lag_seconds_count,
458-
wallclock_lag_minmax,
416+
wallclock_lag,
459417
})
460418
}
461419
}
@@ -484,35 +442,8 @@ impl StatsCollector<ProtoComputeCommand, ProtoComputeResponse> for ReplicaMetric
484442
pub(crate) struct ReplicaCollectionMetrics {
485443
/// Gauge tracking dataflow hydration time.
486444
pub initial_output_duration_seconds: Gauge,
487-
/// Gauge tracking minimum dataflow wallclock lag.
488-
wallclock_lag_seconds_min: Gauge,
489-
/// Gauge tracking maximum dataflow wallclock lag.
490-
wallclock_lag_seconds_max: Gauge,
491-
/// Counter tracking the total sum of dataflow wallclock lag.
492-
wallclock_lag_seconds_sum: Counter,
493-
/// Counter tracking the total count of dataflow wallclock lag measurements.
494-
wallclock_lag_seconds_count: IntCounter,
495-
496-
/// State maintaining minimum and maximum wallclock lag.
497-
wallclock_lag_minmax: SlidingMinMax<f32>,
498-
}
499-
500-
impl ReplicaCollectionMetrics {
501-
pub fn observe_wallclock_lag(&mut self, lag: Duration) {
502-
let lag_secs = lag.as_secs_f32();
503-
504-
self.wallclock_lag_minmax.add_sample(lag_secs);
505-
506-
let (&min, &max) = self
507-
.wallclock_lag_minmax
508-
.get()
509-
.expect("just added a sample");
510-
511-
self.wallclock_lag_seconds_min.set(min.into());
512-
self.wallclock_lag_seconds_max.set(max.into());
513-
self.wallclock_lag_seconds_sum.inc_by(lag_secs.into());
514-
self.wallclock_lag_seconds_count.inc();
515-
}
445+
/// Metrics tracking dataflow wallclock lag.
446+
pub wallclock_lag: WallclockLagMetrics,
516447
}
517448

518449
/// Metrics keyed by `ComputeCommand` type.

src/controller/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use std::time::Duration;
3030

3131
use futures::future::BoxFuture;
3232
use mz_build_info::BuildInfo;
33+
use mz_cluster_client::metrics::ControllerMetrics;
3334
use mz_cluster_client::{ReplicaId, WallclockLagFn};
3435
use mz_compute_client::controller::{
3536
ComputeController, ComputeControllerResponse, ComputeControllerTimestamp, PeekNotification,
@@ -649,6 +650,8 @@ where
649650
Duration::from(lag_ts)
650651
});
651652

653+
let controller_metrics = ControllerMetrics::new(&config.metrics_registry);
654+
652655
let txns_metrics = Arc::new(TxnMetrics::new(&config.metrics_registry));
653656
let collections_ctl = storage_collections::StorageCollectionsImpl::new(
654657
config.persist_location.clone(),
@@ -675,7 +678,8 @@ where
675678
Arc::clone(&txns_metrics),
676679
envd_epoch,
677680
read_only,
678-
config.metrics_registry.clone(),
681+
&config.metrics_registry,
682+
controller_metrics.clone(),
679683
config.connection_context,
680684
storage_txn,
681685
Arc::clone(&collections_ctl),
@@ -688,7 +692,8 @@ where
688692
storage_collections,
689693
envd_epoch,
690694
read_only,
691-
config.metrics_registry.clone(),
695+
&config.metrics_registry,
696+
controller_metrics,
692697
config.now.clone(),
693698
wallclock_lag,
694699
);

0 commit comments

Comments
 (0)