1use std::collections::BTreeMap;
13use std::sync::{Arc, Mutex, Weak};
14
15use mz_ore::metric;
16use mz_ore::metrics::{
17 DeleteOnDropHistogram, DeregisterOnDropCounter, DeregisterOnDropGauge, DeregisterOnDropVec,
18 HistogramVec, MetricsRegistry, raw,
19};
20use mz_ore::stats::histogram_seconds_buckets;
21use mz_repr::GlobalId;
22use mz_rocksdb::RocksDBInstanceMetrics;
23use mz_storage_operators::metrics::BackpressureOperatorMetrics;
24use prometheus::core::{AtomicF64, AtomicU64};
25
26#[derive(Clone, Debug)]
28pub(crate) struct UpsertMetricDefs {
29 pub(crate) rehydration_latency: DeregisterOnDropVec<raw::GaugeVec>,
30 pub(crate) rehydration_total: DeregisterOnDropVec<raw::UIntGaugeVec>,
31 pub(crate) rehydration_updates: DeregisterOnDropVec<raw::UIntGaugeVec>,
32
33 pub(crate) merge_snapshot_latency: HistogramVec,
35 pub(crate) merge_snapshot_updates: DeregisterOnDropVec<raw::IntCounterVec>,
36 pub(crate) merge_snapshot_inserts: DeregisterOnDropVec<raw::IntCounterVec>,
37 pub(crate) merge_snapshot_deletes: DeregisterOnDropVec<raw::IntCounterVec>,
38 pub(crate) upsert_inserts: DeregisterOnDropVec<raw::IntCounterVec>,
39 pub(crate) upsert_updates: DeregisterOnDropVec<raw::IntCounterVec>,
40 pub(crate) upsert_deletes: DeregisterOnDropVec<raw::IntCounterVec>,
41 pub(crate) multi_get_latency: HistogramVec,
42 pub(crate) multi_get_size: DeregisterOnDropVec<raw::IntCounterVec>,
43 pub(crate) multi_get_result_count: DeregisterOnDropVec<raw::IntCounterVec>,
44 pub(crate) multi_get_result_bytes: DeregisterOnDropVec<raw::IntCounterVec>,
45 pub(crate) multi_put_latency: HistogramVec,
46 pub(crate) multi_put_size: DeregisterOnDropVec<raw::IntCounterVec>,
47 pub(crate) restashed: DeregisterOnDropVec<raw::IntCounterVec>,
48
49 pub(crate) rocksdb_multi_get_latency: HistogramVec,
51 pub(crate) rocksdb_multi_get_size: DeregisterOnDropVec<raw::IntCounterVec>,
52 pub(crate) rocksdb_multi_get_result_count: DeregisterOnDropVec<raw::IntCounterVec>,
53 pub(crate) rocksdb_multi_get_result_bytes: DeregisterOnDropVec<raw::IntCounterVec>,
54 pub(crate) rocksdb_multi_get_count: DeregisterOnDropVec<raw::IntCounterVec>,
55 pub(crate) rocksdb_multi_put_count: DeregisterOnDropVec<raw::IntCounterVec>,
56 pub(crate) rocksdb_multi_put_latency: HistogramVec,
57 pub(crate) rocksdb_multi_put_size: DeregisterOnDropVec<raw::IntCounterVec>,
58 pub(crate) shared: Arc<Mutex<BTreeMap<GlobalId, Weak<UpsertSharedMetrics>>>>,
66 pub(crate) rocksdb_shared:
67 Arc<Mutex<BTreeMap<GlobalId, Weak<mz_rocksdb::RocksDBSharedMetrics>>>>,
68}
69
70impl UpsertMetricDefs {
71 pub(crate) fn register_with(registry: &MetricsRegistry) -> Self {
72 let shared = Arc::new(Mutex::new(BTreeMap::new()));
73 let rocksdb_shared = Arc::new(Mutex::new(BTreeMap::new()));
74 Self {
75 rehydration_latency: registry.register(metric!(
76 name: "mz_storage_upsert_state_rehydration_latency",
77 help: "The latency, per-worker, in fractional seconds, \
78 of rehydrating the upsert state for this source",
79 var_labels: ["source_id", "worker_id"],
80 )),
81 rehydration_total: registry.register(metric!(
82 name: "mz_storage_upsert_state_rehydration_total",
83 help: "The number of values \
84 per-worker, rehydrated into the upsert state for \
85 this source",
86 var_labels: ["source_id", "worker_id"],
87 )),
88 rehydration_updates: registry.register(metric!(
89 name: "mz_storage_upsert_state_rehydration_updates",
90 help: "The number of updates (both negative and positive), \
91 per-worker, rehydrated into the upsert state for \
92 this source",
93 var_labels: ["source_id", "worker_id"],
94 )),
95 merge_snapshot_latency: registry.register(metric!(
97 name: "mz_storage_upsert_merge_snapshot_latency",
98 help: "The latencies, in fractional seconds, \
99 of merging snapshot updates into upsert state for this source. \
100 Specific implementations of upsert state may have more detailed \
101 metrics about sub-batches.",
102 var_labels: ["source_id"],
103 buckets: histogram_seconds_buckets(0.000_500, 32.0),
104 )),
105 merge_snapshot_updates: registry.register(metric!(
106 name: "mz_storage_upsert_merge_snapshot_updates_total",
107 help: "The batch size, \
108 of merging snapshot updates into upsert state for this source. \
109 Specific implementations of upsert state may have more detailed \
110 metrics about sub-batches.",
111 var_labels: ["source_id", "worker_id"],
112 )),
113 merge_snapshot_inserts: registry.register(metric!(
114 name: "mz_storage_upsert_merge_snapshot_inserts_total",
115 help: "The number of inserts in a batch for merging snapshot updates \
116 for this source.",
117 var_labels: ["source_id", "worker_id"],
118 )),
119 merge_snapshot_deletes: registry.register(metric!(
120 name: "mz_storage_upsert_merge_snapshot_deletes_total",
121 help: "The number of deletes in a batch for merging snapshot updates \
122 for this source.",
123 var_labels: ["source_id", "worker_id"],
124 )),
125 upsert_inserts: registry.register(metric!(
126 name: "mz_storage_upsert_inserts_total",
127 help: "The number of inserts done by the upsert operator",
128 var_labels: ["source_id", "worker_id"],
129 )),
130 upsert_updates: registry.register(metric!(
131 name: "mz_storage_upsert_updates_total",
132 help: "The number of updates done by the upsert operator",
133 var_labels: ["source_id", "worker_id"],
134 )),
135 upsert_deletes: registry.register(metric!(
136 name: "mz_storage_upsert_deletes_total",
137 help: "The number of deletes done by the upsert operator.",
138 var_labels: ["source_id", "worker_id"],
139 )),
140 multi_get_latency: registry.register(metric!(
141 name: "mz_storage_upsert_multi_get_latency",
142 help: "The latencies, in fractional seconds, \
143 of getting values from the upsert state for this source. \
144 Specific implementations of upsert state may have more detailed \
145 metrics about sub-batches.",
146 var_labels: ["source_id"],
147 buckets: histogram_seconds_buckets(0.000_500, 32.0),
148 )),
149 multi_get_size: registry.register(metric!(
150 name: "mz_storage_upsert_multi_get_size_total",
151 help: "The batch size, \
152 of getting values from the upsert state for this source. \
153 Specific implementations of upsert state may have more detailed \
154 metrics about sub-batches.",
155 var_labels: ["source_id", "worker_id"],
156 )),
157 multi_get_result_count: registry.register(metric!(
158 name: "mz_storage_upsert_multi_get_result_count_total",
159 help: "The number of non-empty records returned in a multi_get batch. \
160 Specific implementations of upsert state may have more detailed \
161 metrics about sub-batches.",
162 var_labels: ["source_id", "worker_id"],
163 )),
164 multi_get_result_bytes: registry.register(metric!(
165 name: "mz_storage_upsert_multi_get_result_bytes_total",
166 help: "The total size of records returned in a multi_get batch. \
167 Specific implementations of upsert state may have more detailed \
168 metrics about sub-batches.",
169 var_labels: ["source_id", "worker_id"],
170 )),
171 multi_put_latency: registry.register(metric!(
172 name: "mz_storage_upsert_multi_put_latency",
173 help: "The latencies, in fractional seconds, \
174 of getting values into the upsert state for this source. \
175 Specific implementations of upsert state may have more detailed \
176 metrics about sub-batches.",
177 var_labels: ["source_id"],
178 buckets: histogram_seconds_buckets(0.000_500, 32.0),
179 )),
180 multi_put_size: registry.register(metric!(
181 name: "mz_storage_upsert_multi_put_size_total",
182 help: "The batch size, \
183 of getting values into the upsert state for this source. \
184 Specific implementations of upsert state may have more detailed \
185 metrics about sub-batches.",
186 var_labels: ["source_id", "worker_id"],
187 )),
188 restashed: registry.register(metric!(
189 name: "mz_storage_upsert_restashed_total",
190 help: "The number of updates the upsert v2 operator extracted from its \
191 stash before the persist feedback frontier reached their time, and \
192 pushed back.",
193 var_labels: ["source_id", "worker_id"],
194 )),
195 shared,
196 rocksdb_multi_get_latency: registry.register(metric!(
197 name: "mz_storage_rocksdb_multi_get_latency",
198 help: "The latencies, in fractional seconds, \
199 of getting batches of values from RocksDB for this source.",
200 var_labels: ["source_id"],
201 buckets: histogram_seconds_buckets(0.000_500, 32.0),
202 )),
203 rocksdb_multi_get_size: registry.register(metric!(
204 name: "mz_storage_rocksdb_multi_get_size_total",
205 help: "The batch size, \
206 of getting batches of values from RocksDB for this source.",
207 var_labels: ["source_id", "worker_id"],
208 )),
209 rocksdb_multi_get_result_count: registry.register(metric!(
210 name: "mz_storage_rocksdb_multi_get_result_count_total",
211 help: "The number of non-empty records returned, \
212 when getting batches of values from RocksDB for this source.",
213 var_labels: ["source_id", "worker_id"],
214 )),
215 rocksdb_multi_get_result_bytes: registry.register(metric!(
216 name: "mz_storage_rocksdb_multi_get_result_bytes_total",
217 help: "The total size of records returned, \
218 when getting batches of values from RocksDB for this source.",
219 var_labels: ["source_id", "worker_id"],
220 )),
221 rocksdb_multi_get_count: registry.register(metric!(
222 name: "mz_storage_rocksdb_multi_get_count_total",
223 help: "The number of calls to rocksdb multi_get.",
224 var_labels: ["source_id", "worker_id"],
225 )),
226 rocksdb_multi_put_count: registry.register(metric!(
227 name: "mz_storage_rocksdb_multi_put_count_total",
228 help: "The number of calls to rocksdb multi_put.",
229 var_labels: ["source_id", "worker_id"],
230 )),
231 rocksdb_multi_put_latency: registry.register(metric!(
232 name: "mz_storage_rocksdb_multi_put_latency",
233 help: "The latencies, in fractional seconds, \
234 of putting batches of values into RocksDB for this source.",
235 var_labels: ["source_id"],
236 buckets: histogram_seconds_buckets(0.000_500, 32.0),
237 )),
238 rocksdb_multi_put_size: registry.register(metric!(
239 name: "mz_storage_rocksdb_multi_put_size_total",
240 help: "The batch size, \
241 of putting batches of values into RocksDB for this source.",
242 var_labels: ["source_id", "worker_id"],
243 )),
244 rocksdb_shared,
245 }
246 }
247
248 pub(crate) fn shared(&self, source_id: &GlobalId) -> Arc<UpsertSharedMetrics> {
250 let mut shared = self.shared.lock().expect("mutex poisoned");
251 if let Some(shared_metrics) = shared.get(source_id) {
252 if let Some(shared_metrics) = shared_metrics.upgrade() {
253 return Arc::clone(&shared_metrics);
254 } else {
255 assert!(shared.remove(source_id).is_some());
256 }
257 }
258 let shared_metrics = Arc::new(UpsertSharedMetrics::new(self, *source_id));
259 assert!(
260 shared
261 .insert(source_id.clone(), Arc::downgrade(&shared_metrics))
262 .is_none()
263 );
264 shared_metrics
265 }
266
267 pub(crate) fn rocksdb_shared(
269 &self,
270 source_id: &GlobalId,
271 ) -> Arc<mz_rocksdb::RocksDBSharedMetrics> {
272 let mut rocksdb = self.rocksdb_shared.lock().expect("mutex poisoned");
273 if let Some(shared_metrics) = rocksdb.get(source_id) {
274 if let Some(shared_metrics) = shared_metrics.upgrade() {
275 return Arc::clone(&shared_metrics);
276 } else {
277 assert!(rocksdb.remove(source_id).is_some());
278 }
279 }
280
281 let rocksdb_metrics = {
282 let source_id = source_id.to_string();
283 mz_rocksdb::RocksDBSharedMetrics {
284 multi_get_latency: self
285 .rocksdb_multi_get_latency
286 .get_delete_on_drop_metric(vec![source_id.clone()]),
287 multi_put_latency: self
288 .rocksdb_multi_put_latency
289 .get_delete_on_drop_metric(vec![source_id.clone()]),
290 }
291 };
292
293 let rocksdb_metrics = Arc::new(rocksdb_metrics);
294 assert!(
295 rocksdb
296 .insert(source_id.clone(), Arc::downgrade(&rocksdb_metrics))
297 .is_none()
298 );
299 rocksdb_metrics
300 }
301}
302
303#[derive(Debug)]
305pub(crate) struct UpsertSharedMetrics {
306 pub(crate) merge_snapshot_latency: DeleteOnDropHistogram<Vec<String>>,
307 pub(crate) multi_get_latency: DeleteOnDropHistogram<Vec<String>>,
308 pub(crate) multi_put_latency: DeleteOnDropHistogram<Vec<String>>,
309}
310
311impl UpsertSharedMetrics {
312 fn new(metrics: &UpsertMetricDefs, source_id: GlobalId) -> Self {
314 let source_id = source_id.to_string();
315 UpsertSharedMetrics {
316 merge_snapshot_latency: metrics
317 .merge_snapshot_latency
318 .get_delete_on_drop_metric(vec![source_id.clone()]),
319 multi_get_latency: metrics
320 .multi_get_latency
321 .get_delete_on_drop_metric(vec![source_id.clone()]),
322 multi_put_latency: metrics
323 .multi_put_latency
324 .get_delete_on_drop_metric(vec![source_id.clone()]),
325 }
326 }
327}
328
329#[derive(Clone, Debug)]
331pub(crate) struct UpsertBackpressureMetricDefs {
332 pub(crate) emitted_bytes: DeregisterOnDropVec<raw::IntCounterVec>,
333 pub(crate) last_backpressured_bytes: DeregisterOnDropVec<raw::UIntGaugeVec>,
334 pub(crate) retired_bytes: DeregisterOnDropVec<raw::IntCounterVec>,
335}
336
337impl UpsertBackpressureMetricDefs {
338 pub(crate) fn register_with(registry: &MetricsRegistry) -> Self {
339 Self {
340 emitted_bytes: registry.register(metric!(
341 name: "mz_storage_upsert_backpressure_emitted_bytes",
342 help: "A counter with the number of emitted bytes.",
343 var_labels: ["source_id", "worker_id"],
344 )),
345 last_backpressured_bytes: registry.register(metric!(
346 name: "mz_storage_upsert_backpressure_last_backpressured_bytes",
347 help: "The last count of bytes we are waiting to be retired in \
348 the operator. This cannot be directly compared to \
349 `retired_bytes`, but CAN indicate that backpressure is happening.",
350 var_labels: ["source_id", "worker_id"],
351 )),
352 retired_bytes: registry.register(metric!(
353 name: "mz_storage_upsert_backpressure_retired_bytes",
354 help:"A counter with the number of bytes retired by downstream processing.",
355 var_labels: ["source_id", "worker_id"],
356 )),
357 }
358 }
359}
360
361pub struct UpsertMetrics {
363 pub(crate) rehydration_latency: DeregisterOnDropGauge<AtomicF64>,
364 pub(crate) rehydration_total: DeregisterOnDropGauge<AtomicU64>,
365 pub(crate) rehydration_updates: DeregisterOnDropGauge<AtomicU64>,
366
367 pub(crate) merge_snapshot_updates: DeregisterOnDropCounter<AtomicU64>,
368 pub(crate) merge_snapshot_inserts: DeregisterOnDropCounter<AtomicU64>,
369 pub(crate) merge_snapshot_deletes: DeregisterOnDropCounter<AtomicU64>,
370 pub(crate) upsert_inserts: DeregisterOnDropCounter<AtomicU64>,
371 pub(crate) upsert_updates: DeregisterOnDropCounter<AtomicU64>,
372 pub(crate) upsert_deletes: DeregisterOnDropCounter<AtomicU64>,
373 pub(crate) multi_get_size: DeregisterOnDropCounter<AtomicU64>,
374 pub(crate) multi_get_result_bytes: DeregisterOnDropCounter<AtomicU64>,
375 pub(crate) multi_get_result_count: DeregisterOnDropCounter<AtomicU64>,
376 pub(crate) multi_put_size: DeregisterOnDropCounter<AtomicU64>,
377 pub(crate) restashed: DeregisterOnDropCounter<AtomicU64>,
378
379 pub(crate) shared: Arc<UpsertSharedMetrics>,
380 pub(crate) rocksdb_shared: Arc<mz_rocksdb::RocksDBSharedMetrics>,
381 pub(crate) rocksdb_instance_metrics: Arc<mz_rocksdb::RocksDBInstanceMetrics>,
382 _backpressure_metrics: Option<UpsertBackpressureMetrics>,
386}
387
388#[derive(Debug)]
393pub(crate) struct UpsertBackpressureMetrics {
394 emitted_bytes: DeregisterOnDropCounter<AtomicU64>,
395 last_backpressured_bytes: DeregisterOnDropGauge<AtomicU64>,
396 retired_bytes: DeregisterOnDropCounter<AtomicU64>,
397}
398
399impl UpsertBackpressureMetrics {
400 pub(crate) fn new(defs: &UpsertBackpressureMetricDefs, id: GlobalId, worker_id: usize) -> Self {
401 let labels = vec![id.to_string(), worker_id.to_string()];
402 UpsertBackpressureMetrics {
403 emitted_bytes: defs
404 .emitted_bytes
405 .get_deregister_on_drop_metric(labels.clone()),
406 last_backpressured_bytes: defs
407 .last_backpressured_bytes
408 .get_deregister_on_drop_metric(labels.clone()),
409 retired_bytes: defs.retired_bytes.get_deregister_on_drop_metric(labels),
410 }
411 }
412
413 pub(crate) fn operator_metrics(&self) -> BackpressureOperatorMetrics {
414 BackpressureOperatorMetrics::new(
415 (*self.emitted_bytes).clone(),
416 (*self.last_backpressured_bytes).clone(),
417 (*self.retired_bytes).clone(),
418 )
419 }
420}
421
422impl UpsertMetrics {
423 pub(crate) fn new(
425 defs: &UpsertMetricDefs,
426 source_id: GlobalId,
427 worker_id: usize,
428 backpressure_metrics: Option<UpsertBackpressureMetrics>,
429 ) -> Self {
430 let source_id_s = source_id.to_string();
431 let worker_id = worker_id.to_string();
432 Self {
433 rehydration_latency: defs
434 .rehydration_latency
435 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
436 rehydration_total: defs
437 .rehydration_total
438 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
439 rehydration_updates: defs
440 .rehydration_updates
441 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
442 merge_snapshot_updates: defs
443 .merge_snapshot_updates
444 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
445 merge_snapshot_inserts: defs
446 .merge_snapshot_inserts
447 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
448 merge_snapshot_deletes: defs
449 .merge_snapshot_deletes
450 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
451 upsert_inserts: defs
452 .upsert_inserts
453 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
454 upsert_updates: defs
455 .upsert_updates
456 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
457 upsert_deletes: defs
458 .upsert_deletes
459 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
460 multi_get_size: defs
461 .multi_get_size
462 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
463 multi_get_result_count: defs
464 .multi_get_result_count
465 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
466 multi_get_result_bytes: defs
467 .multi_get_result_bytes
468 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
469 multi_put_size: defs
470 .multi_put_size
471 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
472 restashed: defs
473 .restashed
474 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
475
476 shared: defs.shared(&source_id),
477 rocksdb_shared: defs.rocksdb_shared(&source_id),
478 rocksdb_instance_metrics: Arc::new(RocksDBInstanceMetrics {
479 multi_get_size: defs
480 .rocksdb_multi_get_size
481 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
482 multi_get_result_count: defs
483 .rocksdb_multi_get_result_count
484 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
485 multi_get_result_bytes: defs
486 .rocksdb_multi_get_result_bytes
487 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
488 multi_get_count: defs
489 .rocksdb_multi_get_count
490 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
491 multi_put_count: defs
492 .rocksdb_multi_put_count
493 .get_deregister_on_drop_metric(vec![source_id_s.clone(), worker_id.clone()]),
494 multi_put_size: defs
495 .rocksdb_multi_put_size
496 .get_deregister_on_drop_metric(vec![source_id_s, worker_id]),
497 }),
498 _backpressure_metrics: backpressure_metrics,
499 }
500 }
501}