1use std::sync::Arc;
5use std::sync::atomic::AtomicU64;
6use std::time::Duration;
7
8use prometheus::Histogram;
9use prometheus::HistogramVec;
10use prometheus::IntCounter;
11use prometheus::IntCounterVec;
12use prometheus::IntGauge;
13use prometheus::IntGaugeVec;
14use prometheus::Registry;
15use prometheus::register_histogram_vec_with_registry;
16use prometheus::register_histogram_with_registry;
17use prometheus::register_int_counter_vec_with_registry;
18use prometheus::register_int_counter_with_registry;
19use prometheus::register_int_gauge_vec_with_registry;
20use tracing::warn;
21
22use crate::ingestion::error::Error;
23use crate::pipeline::Processor;
24
25pub(crate) const DEFAULT_METRICS_PREFIX: &str = "indexer";
27
28const INGESTION_LATENCY_SEC_BUCKETS: &[f64] = &[
30 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0,
31];
32
33const LAG_SEC_BUCKETS: &[f64] = &[
36 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9,
37 0.95, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 20.0, 50.0, 100.0, 1000.0,
38];
39
40const PROCESSING_LATENCY_SEC_BUCKETS: &[f64] = &[
43 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0,
44];
45
46const DB_UPDATE_LATENCY_SEC_BUCKETS: &[f64] = &[
48 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0,
49 2000.0, 5000.0, 10000.0,
50];
51
52const BATCH_SIZE_BUCKETS: &[f64] = &[
54 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0,
55];
56
57#[derive(Clone)]
65pub struct IngestionMetrics {
66 pub total_ingested_checkpoints: IntCounterVec,
68 pub total_ingested_transactions: IntCounterVec,
69 pub total_ingested_events: IntCounterVec,
70 pub total_ingested_objects: IntCounterVec,
71 pub total_ingested_bytes: IntCounter,
72 pub total_ingested_transient_retries: IntCounterVec,
73 pub total_ingested_not_found_retries: IntCounterVec,
74 pub total_streamed_checkpoints: IntCounterVec,
75 pub total_skipped_streamed_checkpoints: IntCounterVec,
76 pub total_out_of_order_streamed_checkpoints: IntCounterVec,
77 pub total_stream_disconnections: IntCounterVec,
78 pub total_streaming_connection_failures: IntCounterVec,
79
80 pub latest_ingested_checkpoint: IntGaugeVec,
82 pub latest_streamed_checkpoint: IntGaugeVec,
83 pub latest_skipped_streamed_checkpoint: IntGaugeVec,
84 pub latest_ingested_checkpoint_timestamp_lag_ms: IntGaugeVec,
85 pub ingested_checkpoint_timestamp_lag: HistogramVec,
86
87 pub ingested_checkpoint_latency: HistogramVec,
88 pub ingested_chain_id_latency: HistogramVec,
89 pub ingested_latest_checkpoint_latency: Histogram,
90
91 pub ingestion_concurrency_limit: IntGaugeVec,
92 pub ingestion_concurrency_inflight: IntGaugeVec,
93}
94
95pub(crate) struct CohortMetrics {
99 pub(crate) total_ingested_checkpoints: IntCounter,
100 pub(crate) total_ingested_transactions: IntCounter,
101 pub(crate) total_ingested_events: IntCounter,
102 pub(crate) total_ingested_objects: IntCounter,
103 pub(crate) total_ingested_not_found_retries: IntCounter,
104 pub(crate) total_streamed_checkpoints: IntCounter,
105 pub(crate) total_skipped_streamed_checkpoints: IntCounter,
106 pub(crate) total_out_of_order_streamed_checkpoints: IntCounter,
107 pub(crate) total_stream_disconnections: IntCounter,
108 pub(crate) total_streaming_connection_failures: IntCounter,
109
110 pub(crate) ingested_checkpoint_latency: Histogram,
111 pub(crate) ingested_chain_id_latency: Histogram,
112
113 pub(crate) latest_streamed_checkpoint: IntGauge,
114 pub(crate) latest_skipped_streamed_checkpoint: IntGauge,
115 pub(crate) ingestion_concurrency_limit: IntGauge,
116 pub(crate) ingestion_concurrency_inflight: IntGauge,
117
118 pub(crate) checkpoint_lag: Arc<CheckpointLagMetricReporter>,
120
121 total_ingested_transient_retries: IntCounterVec,
124 label: String,
126}
127
128#[derive(Clone)]
129pub struct IndexerMetrics {
130 pub total_handler_checkpoints_received: IntCounterVec,
132 pub total_handler_processor_retries: IntCounterVec,
133 pub total_handler_checkpoints_processed: IntCounterVec,
134 pub total_handler_rows_created: IntCounterVec,
135
136 pub latest_processed_checkpoint: IntGaugeVec,
137 pub latest_processed_checkpoint_timestamp_lag_ms: IntGaugeVec,
138 pub processed_checkpoint_timestamp_lag: HistogramVec,
139
140 pub handler_checkpoint_latency: HistogramVec,
141
142 pub processor_concurrency_limit: IntGaugeVec,
143 pub processor_concurrency_inflight: IntGaugeVec,
144
145 pub total_collector_checkpoints_received: IntCounterVec,
147 pub total_collector_rows_received: IntCounterVec,
148 pub total_collector_batches_created: IntCounterVec,
149 pub total_committer_batches_attempted: IntCounterVec,
150 pub total_committer_batches_succeeded: IntCounterVec,
151 pub total_committer_batches_failed: IntCounterVec,
152 pub total_committer_rows_committed: IntCounterVec,
153 pub total_committer_rows_affected: IntCounterVec,
154 pub total_watermarks_out_of_order: IntCounterVec,
155 pub total_pruner_chunks_attempted: IntCounterVec,
156 pub total_pruner_chunks_deleted: IntCounterVec,
157 pub total_pruner_rows_deleted: IntCounterVec,
158
159 pub latest_collected_checkpoint: IntGaugeVec,
161 pub latest_collected_checkpoint_timestamp_lag_ms: IntGaugeVec,
162 pub collected_checkpoint_timestamp_lag: HistogramVec,
163
164 pub latest_partially_committed_checkpoint: IntGaugeVec,
169 pub latest_partially_committed_checkpoint_timestamp_lag_ms: IntGaugeVec,
170 pub partially_committed_checkpoint_timestamp_lag: HistogramVec,
171
172 pub latest_watermarked_checkpoint_timestamp_lag_ms: IntGaugeVec,
177 pub watermarked_checkpoint_timestamp_lag: HistogramVec,
178
179 pub collector_gather_latency: HistogramVec,
180 pub collector_batch_size: HistogramVec,
181 pub total_collector_skipped_checkpoints: IntCounterVec,
182 pub collector_reader_lo: IntGaugeVec,
183 pub committer_commit_latency: HistogramVec,
184 pub committer_tx_rows: HistogramVec,
185 pub watermark_gather_latency: HistogramVec,
186 pub watermark_commit_latency: HistogramVec,
187 pub watermark_pruner_read_latency: HistogramVec,
188 pub watermark_pruner_write_latency: HistogramVec,
189 pub pruner_delete_latency: HistogramVec,
190
191 pub watermark_epoch: IntGaugeVec,
192 pub watermark_checkpoint: IntGaugeVec,
193 pub watermark_transaction: IntGaugeVec,
194 pub watermark_timestamp_ms: IntGaugeVec,
195 pub watermark_reader_lo: IntGaugeVec,
196 pub watermark_pruner_hi: IntGaugeVec,
197
198 pub watermark_epoch_in_db: IntGaugeVec,
199 pub watermark_checkpoint_in_db: IntGaugeVec,
200 pub watermark_transaction_in_db: IntGaugeVec,
201 pub watermark_timestamp_in_db_ms: IntGaugeVec,
202 pub watermark_reader_lo_in_db: IntGaugeVec,
203 pub watermark_pruner_hi_in_db: IntGaugeVec,
204}
205
206pub(crate) struct CheckpointLagMetricReporter {
208 checkpoint_time_lag_histogram: Histogram,
210 latest_checkpoint_time_lag_gauge: IntGauge,
213 latest_checkpoint_sequence_number_gauge: IntGauge,
215 latest_reported_checkpoint: AtomicU64,
217}
218
219impl IngestionMetrics {
220 pub fn new(prefix: Option<&str>, registry: &Registry) -> Arc<Self> {
221 let prefix = prefix.unwrap_or(DEFAULT_METRICS_PREFIX);
222 let name = |n| format!("{prefix}_{n}");
223 Arc::new(Self {
224 total_ingested_checkpoints: register_int_counter_vec_with_registry!(
225 name("total_ingested_checkpoints"),
226 "Total number of checkpoints fetched from the remote store",
227 &["cohort"],
228 registry,
229 )
230 .unwrap(),
231 total_ingested_transactions: register_int_counter_vec_with_registry!(
232 name("total_ingested_transactions"),
233 "Total number of transactions fetched from the remote store",
234 &["cohort"],
235 registry,
236 )
237 .unwrap(),
238 total_ingested_events: register_int_counter_vec_with_registry!(
239 name("total_ingested_events"),
240 "Total number of events fetched from the remote store",
241 &["cohort"],
242 registry,
243 )
244 .unwrap(),
245 total_ingested_objects: register_int_counter_vec_with_registry!(
246 name("total_ingested_objects"),
247 "Total number of objects in checkpoints fetched from the remote store",
248 &["cohort"],
249 registry,
250 )
251 .unwrap(),
252 total_ingested_bytes: register_int_counter_with_registry!(
253 name("total_ingested_bytes"),
254 "Total number of bytes fetched from the remote store",
255 registry,
256 )
257 .unwrap(),
258 total_ingested_transient_retries: register_int_counter_vec_with_registry!(
259 name("total_ingested_retries"),
260 "Total number of retries due to transient errors while fetching data from the \
261 remote store",
262 &["reason", "cohort"],
263 registry,
264 )
265 .unwrap(),
266 total_ingested_not_found_retries: register_int_counter_vec_with_registry!(
267 name("total_ingested_not_found_retries"),
268 "Total number of retries due to the not found errors while fetching data from the \
269 remote store",
270 &["cohort"],
271 registry,
272 )
273 .unwrap(),
274 total_streamed_checkpoints: register_int_counter_vec_with_registry!(
275 name("total_streamed_checkpoints"),
276 "Total number of checkpoints received from gRPC streaming",
277 &["cohort"],
278 registry,
279 )
280 .unwrap(),
281 total_skipped_streamed_checkpoints: register_int_counter_vec_with_registry!(
282 name("total_skipped_streamed_checkpoints"),
283 "Total number of streamed checkpoints skipped because they were already processed",
284 &["cohort"],
285 registry,
286 )
287 .unwrap(),
288 total_out_of_order_streamed_checkpoints: register_int_counter_vec_with_registry!(
289 name("total_out_of_order_streamed_checkpoints"),
290 "Total number of streamed checkpoints received out of order",
291 &["cohort"],
292 registry,
293 )
294 .unwrap(),
295 total_stream_disconnections: register_int_counter_vec_with_registry!(
296 name("total_stream_disconnections"),
297 "Total number of times the gRPC stream was disconnected",
298 &["cohort"],
299 registry,
300 )
301 .unwrap(),
302 total_streaming_connection_failures: register_int_counter_vec_with_registry!(
303 name("total_streaming_connection_failures"),
304 "Total number of failures due to streaming service connection or peek failures",
305 &["cohort"],
306 registry,
307 )
308 .unwrap(),
309 latest_ingested_checkpoint: register_int_gauge_vec_with_registry!(
310 name("latest_ingested_checkpoint"),
311 "Latest checkpoint sequence number fetched from the remote store",
312 &["cohort"],
313 registry,
314 )
315 .unwrap(),
316 latest_streamed_checkpoint: register_int_gauge_vec_with_registry!(
317 name("latest_streamed_checkpoint"),
318 "Latest checkpoint sequence number received from gRPC streaming",
319 &["cohort"],
320 registry,
321 )
322 .unwrap(),
323 latest_skipped_streamed_checkpoint: register_int_gauge_vec_with_registry!(
324 name("latest_skipped_streamed_checkpoint"),
325 "Latest streamed checkpoint sequence number skipped because it was already processed",
326 &["cohort"],
327 registry,
328 )
329 .unwrap(),
330 latest_ingested_checkpoint_timestamp_lag_ms: register_int_gauge_vec_with_registry!(
331 name("latest_ingested_checkpoint_timestamp_lag_ms"),
332 "Difference between the system timestamp when the latest checkpoint was fetched and the \
333 timestamp in the checkpoint, in milliseconds",
334 &["cohort"],
335 registry,
336 )
337 .unwrap(),
338 ingested_checkpoint_timestamp_lag: register_histogram_vec_with_registry!(
339 name("ingested_checkpoint_timestamp_lag"),
340 "Difference between the system timestamp when a checkpoint was fetched and the \
341 timestamp in each checkpoint, in seconds",
342 &["cohort"],
343 LAG_SEC_BUCKETS.to_vec(),
344 registry,
345 )
346 .unwrap(),
347 ingested_checkpoint_latency: register_histogram_vec_with_registry!(
348 name("ingested_checkpoint_latency"),
349 "Time taken to fetch a checkpoint from the remote store, including retries",
350 &["cohort"],
351 INGESTION_LATENCY_SEC_BUCKETS.to_vec(),
352 registry,
353 )
354 .unwrap(),
355 ingested_chain_id_latency: register_histogram_vec_with_registry!(
356 name("ingested_chain_id_latency"),
357 "Time taken to fetch the chain identifier, including retries",
358 &["cohort"],
359 INGESTION_LATENCY_SEC_BUCKETS.to_vec(),
360 registry,
361 )
362 .unwrap(),
363 ingested_latest_checkpoint_latency: register_histogram_with_registry!(
364 name("ingested_latest_checkpoint_latency"),
365 "Time taken to fetch the latest checkpoint number, including retries",
366 INGESTION_LATENCY_SEC_BUCKETS.to_vec(),
367 registry,
368 )
369 .unwrap(),
370 ingestion_concurrency_limit: register_int_gauge_vec_with_registry!(
371 name("ingestion_concurrency_limit"),
372 "Current adaptive concurrency limit for checkpoint ingestion",
373 &["cohort"],
374 registry,
375 )
376 .unwrap(),
377 ingestion_concurrency_inflight: register_int_gauge_vec_with_registry!(
378 name("ingestion_concurrency_inflight"),
379 "Current number of in-flight checkpoint ingestion tasks",
380 &["cohort"],
381 registry,
382 )
383 .unwrap(),
384 })
385 }
386}
387
388impl CohortMetrics {
389 pub(crate) fn new(metrics: &IngestionMetrics, label: &str) -> Arc<Self> {
392 let c = label;
393 let checkpoint_lag = CheckpointLagMetricReporter::with_label(
394 &metrics.ingested_checkpoint_timestamp_lag,
395 &metrics.latest_ingested_checkpoint_timestamp_lag_ms,
396 &metrics.latest_ingested_checkpoint,
397 c,
398 );
399 Arc::new(Self {
400 total_ingested_checkpoints: metrics.total_ingested_checkpoints.with_label_values(&[c]),
401 total_ingested_transactions: metrics
402 .total_ingested_transactions
403 .with_label_values(&[c]),
404 total_ingested_events: metrics.total_ingested_events.with_label_values(&[c]),
405 total_ingested_objects: metrics.total_ingested_objects.with_label_values(&[c]),
406 total_ingested_not_found_retries: metrics
407 .total_ingested_not_found_retries
408 .with_label_values(&[c]),
409 total_streamed_checkpoints: metrics.total_streamed_checkpoints.with_label_values(&[c]),
410 total_skipped_streamed_checkpoints: metrics
411 .total_skipped_streamed_checkpoints
412 .with_label_values(&[c]),
413 total_out_of_order_streamed_checkpoints: metrics
414 .total_out_of_order_streamed_checkpoints
415 .with_label_values(&[c]),
416 total_stream_disconnections: metrics
417 .total_stream_disconnections
418 .with_label_values(&[c]),
419 total_streaming_connection_failures: metrics
420 .total_streaming_connection_failures
421 .with_label_values(&[c]),
422 ingested_checkpoint_latency: metrics
423 .ingested_checkpoint_latency
424 .with_label_values(&[c]),
425 ingested_chain_id_latency: metrics.ingested_chain_id_latency.with_label_values(&[c]),
426 latest_streamed_checkpoint: metrics.latest_streamed_checkpoint.with_label_values(&[c]),
427 latest_skipped_streamed_checkpoint: metrics
428 .latest_skipped_streamed_checkpoint
429 .with_label_values(&[c]),
430 ingestion_concurrency_limit: metrics
431 .ingestion_concurrency_limit
432 .with_label_values(&[c]),
433 ingestion_concurrency_inflight: metrics
434 .ingestion_concurrency_inflight
435 .with_label_values(&[c]),
436 checkpoint_lag,
437 total_ingested_transient_retries: metrics.total_ingested_transient_retries.clone(),
438 label: label.to_string(),
439 })
440 }
441
442 pub(crate) fn inc_retry(
445 &self,
446 checkpoint: u64,
447 reason: &str,
448 error: Error,
449 ) -> backoff::Error<Error> {
450 warn!(
451 checkpoint,
452 reason,
453 "Retrying due to error: {}",
454 error_with_sources(&error)
455 );
456
457 self.total_ingested_transient_retries
458 .with_label_values(&[reason, self.label.as_str()])
459 .inc();
460
461 backoff::Error::transient(error)
462 }
463}
464
465impl IndexerMetrics {
466 pub fn new(prefix: Option<&str>, registry: &Registry) -> Arc<Self> {
467 let prefix = prefix.unwrap_or(DEFAULT_METRICS_PREFIX);
468 let name = |n| format!("{prefix}_{n}");
469 Arc::new(Self {
470 total_handler_checkpoints_received: register_int_counter_vec_with_registry!(
471 name("total_handler_checkpoints_received"),
472 "Total number of checkpoints received by this handler",
473 &["pipeline"],
474 registry,
475 )
476 .unwrap(),
477 total_handler_processor_retries: register_int_counter_vec_with_registry!(
478 name("total_handler_processor_retries"),
479 "Total number of handler retries after transient processing failures",
480 &["pipeline"],
481 registry,
482 )
483 .unwrap(),
484 total_handler_checkpoints_processed: register_int_counter_vec_with_registry!(
485 name("total_handler_checkpoints_processed"),
486 "Total number of checkpoints processed (converted into rows) by this handler",
487 &["pipeline"],
488 registry,
489 )
490 .unwrap(),
491 total_handler_rows_created: register_int_counter_vec_with_registry!(
492 name("total_handler_rows_created"),
493 "Total number of rows created by this handler",
494 &["pipeline"],
495 registry,
496 )
497 .unwrap(),
498 latest_processed_checkpoint: register_int_gauge_vec_with_registry!(
499 name("latest_processed_checkpoint"),
500 "Latest checkpoint sequence number processed by this handler",
501 &["pipeline"],
502 registry,
503 )
504 .unwrap(),
505 latest_processed_checkpoint_timestamp_lag_ms: register_int_gauge_vec_with_registry!(
506 name("latest_processed_checkpoint_timestamp_lag_ms"),
507 "Difference between the system timestamp when the latest checkpoint was processed and the \
508 timestamp in the checkpoint, in milliseconds",
509 &["pipeline"],
510 registry,
511 )
512 .unwrap(),
513 processed_checkpoint_timestamp_lag: register_histogram_vec_with_registry!(
514 name("processed_checkpoint_timestamp_lag"),
515 "Difference between the system timestamp when a checkpoint was processed and the \
516 timestamp in each checkpoint, in seconds",
517 &["pipeline"],
518 LAG_SEC_BUCKETS.to_vec(),
519 registry,
520 )
521 .unwrap(),
522 handler_checkpoint_latency: register_histogram_vec_with_registry!(
523 name("handler_checkpoint_latency"),
524 "Time taken to process a checkpoint by this handler",
525 &["pipeline"],
526 PROCESSING_LATENCY_SEC_BUCKETS.to_vec(),
527 registry,
528 )
529 .unwrap(),
530 processor_concurrency_limit: register_int_gauge_vec_with_registry!(
531 name("processor_concurrency_limit"),
532 "Current adaptive concurrency limit for this processor",
533 &["pipeline"],
534 registry,
535 )
536 .unwrap(),
537 processor_concurrency_inflight: register_int_gauge_vec_with_registry!(
538 name("processor_concurrency_inflight"),
539 "Current number of in-flight processor tasks for this pipeline",
540 &["pipeline"],
541 registry,
542 )
543 .unwrap(),
544 total_collector_checkpoints_received: register_int_counter_vec_with_registry!(
545 name("total_collector_checkpoints_received"),
546 "Total number of checkpoints received by this collector",
547 &["pipeline"],
548 registry,
549 )
550 .unwrap(),
551 total_collector_rows_received: register_int_counter_vec_with_registry!(
552 name("total_collector_rows_received"),
553 "Total number of rows received by this collector",
554 &["pipeline"],
555 registry,
556 )
557 .unwrap(),
558 total_collector_batches_created: register_int_counter_vec_with_registry!(
559 name("total_collector_batches_created"),
560 "Total number of batches created by this collector",
561 &["pipeline"],
562 registry,
563 )
564 .unwrap(),
565 total_committer_batches_attempted: register_int_counter_vec_with_registry!(
566 name("total_committer_batches_attempted"),
567 "Total number of batches writes attempted by this committer",
568 &["pipeline"],
569 registry,
570 )
571 .unwrap(),
572 total_committer_batches_succeeded: register_int_counter_vec_with_registry!(
573 name("total_committer_batches_succeeded"),
574 "Total number of successful batches writes by this committer",
575 &["pipeline"],
576 registry,
577 )
578 .unwrap(),
579 total_committer_batches_failed: register_int_counter_vec_with_registry!(
580 name("total_committer_batches_failed"),
581 "Total number of failed batches writes by this committer",
582 &["pipeline"],
583 registry,
584 )
585 .unwrap(),
586 total_committer_rows_committed: register_int_counter_vec_with_registry!(
587 name("total_committer_rows_committed"),
588 "Total number of rows sent to the database by this committer",
589 &["pipeline"],
590 registry,
591 )
592 .unwrap(),
593 total_committer_rows_affected: register_int_counter_vec_with_registry!(
594 name("total_committer_rows_affected"),
595 "Total number of rows actually written to the database by this committer",
596 &["pipeline"],
597 registry,
598 )
599 .unwrap(),
600 total_watermarks_out_of_order: register_int_counter_vec_with_registry!(
601 name("watermark_out_of_order"),
602 "Number of times this committer encountered a batch for a checkpoint before its watermark",
603 &["pipeline"],
604 registry,
605 )
606 .unwrap(),
607 total_pruner_chunks_attempted: register_int_counter_vec_with_registry!(
608 name("pruner_chunks_attempted"),
609 "Number of chunks this pruner attempted to delete",
610 &["pipeline"],
611 registry,
612 )
613 .unwrap(),
614 total_pruner_chunks_deleted: register_int_counter_vec_with_registry!(
615 name("pruner_chunks_deleted"),
616 "Number of chunks this pruner successfully deleted",
617 &["pipeline"],
618 registry,
619 )
620 .unwrap(),
621 total_pruner_rows_deleted: register_int_counter_vec_with_registry!(
622 name("pruner_rows_deleted"),
623 "Number of rows this pruner successfully deleted",
624 &["pipeline"],
625 registry,
626 )
627 .unwrap(),
628 latest_collected_checkpoint: register_int_gauge_vec_with_registry!(
629 name("latest_collected_checkpoint"),
630 "Latest checkpoint sequence number collected by this collector",
631 &["pipeline"],
632 registry,
633 )
634 .unwrap(),
635 latest_collected_checkpoint_timestamp_lag_ms: register_int_gauge_vec_with_registry!(
636 name("latest_collected_checkpoint_timestamp_lag_ms"),
637 "Difference between the system timestamp when the latest checkpoint was collected and the \
638 timestamp in the checkpoint, in milliseconds",
639 &["pipeline"],
640 registry,
641 )
642 .unwrap(),
643 collected_checkpoint_timestamp_lag: register_histogram_vec_with_registry!(
644 name("collected_checkpoint_timestamp_lag"),
645 "Difference between the system timestamp when a checkpoint was collected and the \
646 timestamp in each checkpoint, in seconds",
647 &["pipeline"],
648 LAG_SEC_BUCKETS.to_vec(),
649 registry,
650 )
651 .unwrap(),
652 latest_partially_committed_checkpoint: register_int_gauge_vec_with_registry!(
653 name("latest_partially_committed_checkpoint"),
654 "Latest checkpoint sequence number partially committed by this collector",
655 &["pipeline"],
656 registry,
657 )
658 .unwrap(),
659 latest_partially_committed_checkpoint_timestamp_lag_ms: register_int_gauge_vec_with_registry!(
660 name("latest_partially_committed_checkpoint_timestamp_lag_ms"),
661 "Difference between the system timestamp when the latest checkpoint was partially committed and the \
662 timestamp in the checkpoint, in milliseconds",
663 &["pipeline"],
664 registry,
665 )
666 .unwrap(),
667 partially_committed_checkpoint_timestamp_lag: register_histogram_vec_with_registry!(
668 name("partially_committed_checkpoint_timestamp_lag"),
669 "Difference between the system timestamp when a checkpoint was partially committed and the \
670 timestamp in each checkpoint, in seconds",
671 &["pipeline"],
672 LAG_SEC_BUCKETS.to_vec(),
673 registry,
674 )
675 .unwrap(),
676 latest_watermarked_checkpoint_timestamp_lag_ms: register_int_gauge_vec_with_registry!(
677 name("latest_watermarked_checkpoint_timestamp_lag_ms"),
678 "Difference between the system timestamp when the latest checkpoint was watermarked and the \
679 timestamp in the checkpoint, in milliseconds",
680 &["pipeline"],
681 registry,
682 )
683 .unwrap(),
684 watermarked_checkpoint_timestamp_lag: register_histogram_vec_with_registry!(
685 name("watermarked_checkpoint_timestamp_lag"),
686 "Difference between the system timestamp when a checkpoint was watermarked and the \
687 timestamp in each checkpoint, in seconds",
688 &["pipeline"],
689 LAG_SEC_BUCKETS.to_vec(),
690 registry,
691 )
692 .unwrap(),
693 collector_gather_latency: register_histogram_vec_with_registry!(
694 name("collector_gather_latency"),
695 "Time taken to gather rows into a batch by this collector",
696 &["pipeline"],
697 PROCESSING_LATENCY_SEC_BUCKETS.to_vec(),
698 registry,
699 )
700 .unwrap(),
701 collector_batch_size: register_histogram_vec_with_registry!(
702 name("collector_batch_size"),
703 "Number of rows in a batch written to the database by this collector",
704 &["pipeline"],
705 BATCH_SIZE_BUCKETS.to_vec(),
706 registry,
707 )
708 .unwrap(),
709 total_collector_skipped_checkpoints: register_int_counter_vec_with_registry!(
710 name("total_collector_skipped_checkpoints"),
711 "Number of checkpoints skipped by the tasked pipeline's collector due to being below the main reader lo watermark",
712 &["pipeline"],
713 registry,
714 ).unwrap(),
715 collector_reader_lo: register_int_gauge_vec_with_registry!(
716 name("collector_reader_lo"),
717 "Reader low watermark as observed by the collector",
718 &["pipeline"],
719 registry,
720 )
721 .unwrap(),
722 committer_commit_latency: register_histogram_vec_with_registry!(
723 name("committer_commit_latency"),
724 "Time taken to write a batch of rows to the database by this committer",
725 &["pipeline"],
726 DB_UPDATE_LATENCY_SEC_BUCKETS.to_vec(),
727 registry,
728 )
729 .unwrap(),
730 committer_tx_rows: register_histogram_vec_with_registry!(
731 name("committer_tx_rows"),
732 "Number of rows written to the database in a single database transaction by this committer",
733 &["pipeline"],
734 BATCH_SIZE_BUCKETS.to_vec(),
735 registry,
736 )
737 .unwrap(),
738 watermark_gather_latency: register_histogram_vec_with_registry!(
739 name("watermark_gather_latency"),
740 "Time taken to calculate the new high watermark after a write by this committer",
741 &["pipeline"],
742 PROCESSING_LATENCY_SEC_BUCKETS.to_vec(),
743 registry,
744 )
745 .unwrap(),
746 watermark_commit_latency: register_histogram_vec_with_registry!(
747 name("watermark_commit_latency"),
748 "Time taken to write the new high watermark to the database by this committer",
749 &["pipeline"],
750 DB_UPDATE_LATENCY_SEC_BUCKETS.to_vec(),
751 registry,
752 )
753 .unwrap(),
754 watermark_pruner_read_latency: register_histogram_vec_with_registry!(
755 name("watermark_pruner_read_latency"),
756 "Time taken to read pruner's next upper and lowerbounds from the database by this pruner",
757 &["pipeline"],
758 DB_UPDATE_LATENCY_SEC_BUCKETS.to_vec(),
759 registry,
760 )
761 .unwrap(),
762 watermark_pruner_write_latency: register_histogram_vec_with_registry!(
763 name("watermark_pruner_write_latency"),
764 "Time taken to write the pruner's new upperbound to the database by this pruner",
765 &["pipeline"],
766 DB_UPDATE_LATENCY_SEC_BUCKETS.to_vec(),
767 registry,
768 )
769 .unwrap(),
770 pruner_delete_latency: register_histogram_vec_with_registry!(
771 name("pruner_delete_latency"),
772 "Time taken to delete a chunk of data from the database by this pruner",
773 &["pipeline"],
774 DB_UPDATE_LATENCY_SEC_BUCKETS.to_vec(),
775 registry,
776 )
777 .unwrap(),
778 watermark_epoch: register_int_gauge_vec_with_registry!(
779 name("watermark_epoch"),
780 "Current epoch high watermark for this committer",
781 &["pipeline"],
782 registry,
783 )
784 .unwrap(),
785 watermark_checkpoint: register_int_gauge_vec_with_registry!(
786 name("watermark_checkpoint"),
787 "Current checkpoint high watermark for this committer",
788 &["pipeline"],
789 registry,
790 )
791 .unwrap(),
792 watermark_transaction: register_int_gauge_vec_with_registry!(
793 name("watermark_transaction"),
794 "Current transaction high watermark for this committer",
795 &["pipeline"],
796 registry,
797 )
798 .unwrap(),
799 watermark_timestamp_ms: register_int_gauge_vec_with_registry!(
800 name("watermark_timestamp_ms"),
801 "Current timestamp high watermark for this committer, in milliseconds",
802 &["pipeline"],
803 registry,
804 )
805 .unwrap(),
806 watermark_reader_lo: register_int_gauge_vec_with_registry!(
807 name("watermark_reader_lo"),
808 "Current reader low watermark for this pruner",
809 &["pipeline"],
810 registry,
811 )
812 .unwrap(),
813 watermark_pruner_hi: register_int_gauge_vec_with_registry!(
814 name("watermark_pruner_hi"),
815 "Current pruner high watermark for this pruner",
816 &["pipeline"],
817 registry,
818 )
819 .unwrap(),
820 watermark_epoch_in_db: register_int_gauge_vec_with_registry!(
821 name("watermark_epoch_in_db"),
822 "Last epoch high watermark this committer wrote to the DB",
823 &["pipeline"],
824 registry,
825 )
826 .unwrap(),
827 watermark_checkpoint_in_db: register_int_gauge_vec_with_registry!(
828 name("watermark_checkpoint_in_db"),
829 "Last checkpoint high watermark this committer wrote to the DB",
830 &["pipeline"],
831 registry,
832 )
833 .unwrap(),
834 watermark_transaction_in_db: register_int_gauge_vec_with_registry!(
835 name("watermark_transaction_in_db"),
836 "Last transaction high watermark this committer wrote to the DB",
837 &["pipeline"],
838 registry,
839 )
840 .unwrap(),
841 watermark_timestamp_in_db_ms: register_int_gauge_vec_with_registry!(
842 name("watermark_timestamp_ms_in_db"),
843 "Last timestamp high watermark this committer wrote to the DB, in milliseconds",
844 &["pipeline"],
845 registry,
846 )
847 .unwrap(),
848 watermark_reader_lo_in_db: register_int_gauge_vec_with_registry!(
849 name("watermark_reader_lo_in_db"),
850 "Last reader low watermark this pruner wrote to the DB",
851 &["pipeline"],
852 registry,
853 )
854 .unwrap(),
855 watermark_pruner_hi_in_db: register_int_gauge_vec_with_registry!(
856 name("watermark_pruner_hi_in_db"),
857 "Last pruner high watermark this pruner wrote to the DB",
858 &["pipeline"],
859 registry,
860 )
861 .unwrap(),
862 })
863 }
864
865 pub(crate) fn inc_processor_retry<P: Processor>(
866 &self,
867 checkpoint: u64,
868 error: &anyhow::Error,
869 delay: Duration,
870 ) {
871 warn!(
872 pipeline = P::NAME,
873 checkpoint,
874 retry_delay_ms = delay.as_millis(),
875 "Retrying processor after error: {error:?}",
876 );
877
878 self.total_handler_processor_retries
879 .with_label_values(&[P::NAME])
880 .inc();
881 }
882}
883
884impl CheckpointLagMetricReporter {
885 pub fn new(
886 checkpoint_time_lag_histogram: Histogram,
887 latest_checkpoint_time_lag_gauge: IntGauge,
888 latest_checkpoint_sequence_number_gauge: IntGauge,
889 ) -> Arc<Self> {
890 Arc::new(Self {
891 checkpoint_time_lag_histogram,
892 latest_checkpoint_time_lag_gauge,
893 latest_checkpoint_sequence_number_gauge,
894 latest_reported_checkpoint: AtomicU64::new(0),
895 })
896 }
897
898 pub fn new_for_pipeline<P: Processor>(
899 checkpoint_time_lag_histogram: &HistogramVec,
900 latest_checkpoint_time_lag_gauge: &IntGaugeVec,
901 latest_checkpoint_sequence_number_gauge: &IntGaugeVec,
902 ) -> Arc<Self> {
903 Self::with_label(
904 checkpoint_time_lag_histogram,
905 latest_checkpoint_time_lag_gauge,
906 latest_checkpoint_sequence_number_gauge,
907 P::NAME,
908 )
909 }
910
911 pub fn with_label(
914 checkpoint_time_lag_histogram: &HistogramVec,
915 latest_checkpoint_time_lag_gauge: &IntGaugeVec,
916 latest_checkpoint_sequence_number_gauge: &IntGaugeVec,
917 label: &str,
918 ) -> Arc<Self> {
919 Self::new(
920 checkpoint_time_lag_histogram.with_label_values(&[label]),
921 latest_checkpoint_time_lag_gauge.with_label_values(&[label]),
922 latest_checkpoint_sequence_number_gauge.with_label_values(&[label]),
923 )
924 }
925
926 pub fn report_lag(&self, cp_sequence_number: u64, checkpoint_timestamp_ms: u64) {
927 let lag = chrono::Utc::now().timestamp_millis() - checkpoint_timestamp_ms as i64;
928 self.checkpoint_time_lag_histogram
929 .observe((lag as f64) / 1000.0);
930
931 let prev = self
932 .latest_reported_checkpoint
933 .fetch_max(cp_sequence_number, std::sync::atomic::Ordering::Relaxed);
934 if cp_sequence_number > prev {
935 self.latest_checkpoint_sequence_number_gauge
936 .set(cp_sequence_number as i64);
937 self.latest_checkpoint_time_lag_gauge.set(lag);
938 }
939 }
940}
941
942fn error_with_sources(mut err: &dyn std::error::Error) -> String {
949 let mut out = String::new();
950 let mut next = err.to_string();
951
952 let mut prefix = "";
953 while let Some(src) = err.source() {
954 err = src;
955 let msg = err.to_string();
956 if !next.contains(&msg) {
957 out.push_str(prefix);
958 out.push_str(&next);
959 prefix = ": ";
960 next = msg;
961 }
962 }
963
964 out.push_str(prefix);
965 out.push_str(&next);
966 out
967}
968
969#[cfg(test)]
970pub(crate) mod tests {
971 use std::sync::Arc;
972
973 use prometheus::Registry;
974
975 use super::*;
976
977 pub fn test_metrics() -> Arc<IndexerMetrics> {
979 IndexerMetrics::new(None, &Registry::new())
980 }
981
982 pub fn test_ingestion_metrics() -> Arc<IngestionMetrics> {
984 IngestionMetrics::new(None, &Registry::new())
985 }
986
987 #[test]
988 fn test_error_with_sources_dedupes_inlined_and_appends_new() {
989 #[derive(thiserror::Error, Debug)]
991 #[error("operation timed out")]
992 struct Leaf;
993
994 #[derive(thiserror::Error, Debug)]
997 #[error("error sending request")]
998 struct Terse(#[source] Leaf);
999
1000 #[derive(thiserror::Error, Debug)]
1002 #[error("Error performing GET: {0}")]
1003 struct Inlining(#[source] Terse);
1004
1005 let err = Inlining(Terse(Leaf));
1008 assert_eq!(
1009 error_with_sources(&err),
1010 "Error performing GET: error sending request: operation timed out"
1011 );
1012 }
1013
1014 #[test]
1015 fn test_error_with_sources_dedupes_inlined_mid_message() {
1016 #[derive(thiserror::Error, Debug)]
1018 #[error("operation timed out")]
1019 struct Leaf;
1020
1021 #[derive(thiserror::Error, Debug)]
1023 #[error("failed [{0}] while fetching")]
1024 struct MidInlining(#[source] Leaf);
1025
1026 let err = MidInlining(Leaf);
1029 assert_eq!(
1030 error_with_sources(&err),
1031 "failed [operation timed out] while fetching"
1032 );
1033 }
1034}