Line data Source code
1 : use std::collections::HashMap;
2 : use std::num::NonZeroUsize;
3 : use std::pin::Pin;
4 : use std::sync::atomic::AtomicU64;
5 : use std::sync::{Arc, Mutex};
6 : use std::task::{Context, Poll};
7 : use std::time::{Duration, Instant};
8 :
9 : use enum_map::{Enum as _, EnumMap};
10 : use futures::Future;
11 : use metrics::{
12 : register_counter_vec, register_gauge_vec, register_histogram, register_histogram_vec,
13 : register_int_counter, register_int_counter_pair_vec, register_int_counter_vec,
14 : register_int_gauge, register_int_gauge_vec, register_uint_gauge, register_uint_gauge_vec,
15 : Counter, CounterVec, Gauge, GaugeVec, Histogram, HistogramVec, IntCounter, IntCounterPair,
16 : IntCounterPairVec, IntCounterVec, IntGauge, IntGaugeVec, UIntGauge, UIntGaugeVec,
17 : };
18 : use once_cell::sync::Lazy;
19 : use pageserver_api::config::{
20 : PageServicePipeliningConfig, PageServicePipeliningConfigPipelined,
21 : PageServiceProtocolPipelinedExecutionStrategy,
22 : };
23 : use pageserver_api::models::InMemoryLayerInfo;
24 : use pageserver_api::shard::TenantShardId;
25 : use pin_project_lite::pin_project;
26 : use postgres_backend::{is_expected_io_error, QueryError};
27 : use pq_proto::framed::ConnectionError;
28 :
29 : use strum::{EnumCount, IntoEnumIterator as _, VariantNames};
30 : use strum_macros::{IntoStaticStr, VariantNames};
31 : use utils::id::TimelineId;
32 :
33 : use crate::config::PageServerConf;
34 : use crate::context::{PageContentKind, RequestContext};
35 : use crate::pgdatadir_mapping::DatadirModificationStats;
36 : use crate::task_mgr::TaskKind;
37 : use crate::tenant::layer_map::LayerMap;
38 : use crate::tenant::mgr::TenantSlot;
39 : use crate::tenant::storage_layer::{InMemoryLayer, PersistentLayerDesc};
40 : use crate::tenant::tasks::BackgroundLoopKind;
41 : use crate::tenant::throttle::ThrottleResult;
42 : use crate::tenant::Timeline;
43 :
44 : /// Prometheus histogram buckets (in seconds) for operations in the critical
45 : /// path. In other words, operations that directly affect that latency of user
46 : /// queries.
47 : ///
48 : /// The buckets capture the majority of latencies in the microsecond and
49 : /// millisecond range but also extend far enough up to distinguish "bad" from
50 : /// "really bad".
51 : const CRITICAL_OP_BUCKETS: &[f64] = &[
52 : 0.000_001, 0.000_010, 0.000_100, // 1 us, 10 us, 100 us
53 : 0.001_000, 0.010_000, 0.100_000, // 1 ms, 10 ms, 100 ms
54 : 1.0, 10.0, 100.0, // 1 s, 10 s, 100 s
55 : ];
56 :
57 : // Metrics collected on operations on the storage repository.
58 : #[derive(Debug, VariantNames, IntoStaticStr)]
59 : #[strum(serialize_all = "kebab_case")]
60 : pub(crate) enum StorageTimeOperation {
61 : #[strum(serialize = "layer flush")]
62 : LayerFlush,
63 :
64 : #[strum(serialize = "layer flush delay")]
65 : LayerFlushDelay,
66 :
67 : #[strum(serialize = "compact")]
68 : Compact,
69 :
70 : #[strum(serialize = "create images")]
71 : CreateImages,
72 :
73 : #[strum(serialize = "logical size")]
74 : LogicalSize,
75 :
76 : #[strum(serialize = "imitate logical size")]
77 : ImitateLogicalSize,
78 :
79 : #[strum(serialize = "load layer map")]
80 : LoadLayerMap,
81 :
82 : #[strum(serialize = "gc")]
83 : Gc,
84 :
85 : #[strum(serialize = "find gc cutoffs")]
86 : FindGcCutoffs,
87 : }
88 :
89 404 : pub(crate) static STORAGE_TIME_SUM_PER_TIMELINE: Lazy<CounterVec> = Lazy::new(|| {
90 404 : register_counter_vec!(
91 404 : "pageserver_storage_operations_seconds_sum",
92 404 : "Total time spent on storage operations with operation, tenant and timeline dimensions",
93 404 : &["operation", "tenant_id", "shard_id", "timeline_id"],
94 404 : )
95 404 : .expect("failed to define a metric")
96 404 : });
97 :
98 404 : pub(crate) static STORAGE_TIME_COUNT_PER_TIMELINE: Lazy<IntCounterVec> = Lazy::new(|| {
99 404 : register_int_counter_vec!(
100 404 : "pageserver_storage_operations_seconds_count",
101 404 : "Count of storage operations with operation, tenant and timeline dimensions",
102 404 : &["operation", "tenant_id", "shard_id", "timeline_id"],
103 404 : )
104 404 : .expect("failed to define a metric")
105 404 : });
106 :
107 : // Buckets for background operation duration in seconds, like compaction, GC, size calculation.
108 : const STORAGE_OP_BUCKETS: &[f64] = &[0.010, 0.100, 1.0, 10.0, 100.0, 1000.0];
109 :
110 404 : pub(crate) static STORAGE_TIME_GLOBAL: Lazy<HistogramVec> = Lazy::new(|| {
111 404 : register_histogram_vec!(
112 404 : "pageserver_storage_operations_seconds_global",
113 404 : "Time spent on storage operations",
114 404 : &["operation"],
115 404 : STORAGE_OP_BUCKETS.into(),
116 404 : )
117 404 : .expect("failed to define a metric")
118 404 : });
119 :
120 : /// Measures layers visited per read (i.e. read amplification).
121 : ///
122 : /// NB: for a batch, we count all visited layers towards each read. While the cost of layer visits
123 : /// are amortized across the batch, and some layers may not intersect with a given key, each visited
124 : /// layer contributes directly to the observed latency for every read in the batch, which is what we
125 : /// care about.
126 404 : pub(crate) static LAYERS_PER_READ: Lazy<HistogramVec> = Lazy::new(|| {
127 404 : register_histogram_vec!(
128 404 : "pageserver_layers_per_read",
129 404 : "Layers visited to serve a single read (read amplification). In a batch, all visited layers count towards every read.",
130 404 : &["tenant_id", "shard_id", "timeline_id"],
131 404 : // Low resolution to reduce cardinality.
132 404 : vec![1.0, 5.0, 10.0, 25.0, 50.0, 100.0],
133 404 : )
134 404 : .expect("failed to define a metric")
135 404 : });
136 :
137 396 : pub(crate) static LAYERS_PER_READ_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
138 396 : register_histogram!(
139 396 : "pageserver_layers_per_read_global",
140 396 : "Layers visited to serve a single read (read amplification). In a batch, all visited layers count towards every read.",
141 396 : vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0],
142 396 : )
143 396 : .expect("failed to define a metric")
144 396 : });
145 :
146 396 : pub(crate) static DELTAS_PER_READ_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
147 396 : // We expect this to be low because of Postgres checkpoints. Let's see if that holds.
148 396 : register_histogram!(
149 396 : "pageserver_deltas_per_read_global",
150 396 : "Number of delta pages applied to image page per read",
151 396 : vec![0.0, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0],
152 396 : )
153 396 : .expect("failed to define a metric")
154 396 : });
155 :
156 0 : pub(crate) static CONCURRENT_INITDBS: Lazy<UIntGauge> = Lazy::new(|| {
157 0 : register_uint_gauge!(
158 0 : "pageserver_concurrent_initdb",
159 0 : "Number of initdb processes running"
160 0 : )
161 0 : .expect("failed to define a metric")
162 0 : });
163 :
164 0 : pub(crate) static INITDB_SEMAPHORE_ACQUISITION_TIME: Lazy<Histogram> = Lazy::new(|| {
165 0 : register_histogram!(
166 0 : "pageserver_initdb_semaphore_seconds_global",
167 0 : "Time spent getting a permit from the global initdb semaphore",
168 0 : STORAGE_OP_BUCKETS.into()
169 0 : )
170 0 : .expect("failed to define metric")
171 0 : });
172 :
173 0 : pub(crate) static INITDB_RUN_TIME: Lazy<Histogram> = Lazy::new(|| {
174 0 : register_histogram!(
175 0 : "pageserver_initdb_seconds_global",
176 0 : "Time spent performing initdb",
177 0 : STORAGE_OP_BUCKETS.into()
178 0 : )
179 0 : .expect("failed to define metric")
180 0 : });
181 :
182 : pub(crate) struct GetVectoredLatency {
183 : map: EnumMap<TaskKind, Option<Histogram>>,
184 : }
185 :
186 : #[allow(dead_code)]
187 : pub(crate) struct ScanLatency {
188 : map: EnumMap<TaskKind, Option<Histogram>>,
189 : }
190 :
191 : impl GetVectoredLatency {
192 : // Only these task types perform vectored gets. Filter all other tasks out to reduce total
193 : // cardinality of the metric.
194 : const TRACKED_TASK_KINDS: [TaskKind; 2] = [TaskKind::Compaction, TaskKind::PageRequestHandler];
195 :
196 39332 : pub(crate) fn for_task_kind(&self, task_kind: TaskKind) -> Option<&Histogram> {
197 39332 : self.map[task_kind].as_ref()
198 39332 : }
199 : }
200 :
201 : impl ScanLatency {
202 : // Only these task types perform vectored gets. Filter all other tasks out to reduce total
203 : // cardinality of the metric.
204 : const TRACKED_TASK_KINDS: [TaskKind; 1] = [TaskKind::PageRequestHandler];
205 :
206 24 : pub(crate) fn for_task_kind(&self, task_kind: TaskKind) -> Option<&Histogram> {
207 24 : self.map[task_kind].as_ref()
208 24 : }
209 : }
210 :
211 : pub(crate) struct ScanLatencyOngoingRecording<'a> {
212 : parent: &'a Histogram,
213 : start: std::time::Instant,
214 : }
215 :
216 : impl<'a> ScanLatencyOngoingRecording<'a> {
217 0 : pub(crate) fn start_recording(parent: &'a Histogram) -> ScanLatencyOngoingRecording<'a> {
218 0 : let start = Instant::now();
219 0 : ScanLatencyOngoingRecording { parent, start }
220 0 : }
221 :
222 0 : pub(crate) fn observe(self) {
223 0 : let elapsed = self.start.elapsed();
224 0 : self.parent.observe(elapsed.as_secs_f64());
225 0 : }
226 : }
227 :
228 388 : pub(crate) static GET_VECTORED_LATENCY: Lazy<GetVectoredLatency> = Lazy::new(|| {
229 388 : let inner = register_histogram_vec!(
230 388 : "pageserver_get_vectored_seconds",
231 388 : "Time spent in get_vectored.",
232 388 : &["task_kind"],
233 388 : CRITICAL_OP_BUCKETS.into(),
234 388 : )
235 388 : .expect("failed to define a metric");
236 388 :
237 388 : GetVectoredLatency {
238 12028 : map: EnumMap::from_array(std::array::from_fn(|task_kind_idx| {
239 12028 : let task_kind = TaskKind::from_usize(task_kind_idx);
240 12028 :
241 12028 : if GetVectoredLatency::TRACKED_TASK_KINDS.contains(&task_kind) {
242 776 : let task_kind = task_kind.into();
243 776 : Some(inner.with_label_values(&[task_kind]))
244 : } else {
245 11252 : None
246 : }
247 12028 : })),
248 388 : }
249 388 : });
250 :
251 8 : pub(crate) static SCAN_LATENCY: Lazy<ScanLatency> = Lazy::new(|| {
252 8 : let inner = register_histogram_vec!(
253 8 : "pageserver_scan_seconds",
254 8 : "Time spent in scan.",
255 8 : &["task_kind"],
256 8 : CRITICAL_OP_BUCKETS.into(),
257 8 : )
258 8 : .expect("failed to define a metric");
259 8 :
260 8 : ScanLatency {
261 248 : map: EnumMap::from_array(std::array::from_fn(|task_kind_idx| {
262 248 : let task_kind = TaskKind::from_usize(task_kind_idx);
263 248 :
264 248 : if ScanLatency::TRACKED_TASK_KINDS.contains(&task_kind) {
265 8 : let task_kind = task_kind.into();
266 8 : Some(inner.with_label_values(&[task_kind]))
267 : } else {
268 240 : None
269 : }
270 248 : })),
271 8 : }
272 8 : });
273 :
274 : pub(crate) struct PageCacheMetricsForTaskKind {
275 : pub read_accesses_immutable: IntCounter,
276 : pub read_hits_immutable: IntCounter,
277 : }
278 :
279 : pub(crate) struct PageCacheMetrics {
280 : map: EnumMap<TaskKind, EnumMap<PageContentKind, PageCacheMetricsForTaskKind>>,
281 : }
282 :
283 184 : static PAGE_CACHE_READ_HITS: Lazy<IntCounterVec> = Lazy::new(|| {
284 184 : register_int_counter_vec!(
285 184 : "pageserver_page_cache_read_hits_total",
286 184 : "Number of read accesses to the page cache that hit",
287 184 : &["task_kind", "key_kind", "content_kind", "hit_kind"]
288 184 : )
289 184 : .expect("failed to define a metric")
290 184 : });
291 :
292 184 : static PAGE_CACHE_READ_ACCESSES: Lazy<IntCounterVec> = Lazy::new(|| {
293 184 : register_int_counter_vec!(
294 184 : "pageserver_page_cache_read_accesses_total",
295 184 : "Number of read accesses to the page cache",
296 184 : &["task_kind", "key_kind", "content_kind"]
297 184 : )
298 184 : .expect("failed to define a metric")
299 184 : });
300 :
301 184 : pub(crate) static PAGE_CACHE: Lazy<PageCacheMetrics> = Lazy::new(|| PageCacheMetrics {
302 5704 : map: EnumMap::from_array(std::array::from_fn(|task_kind| {
303 5704 : let task_kind = TaskKind::from_usize(task_kind);
304 5704 : let task_kind: &'static str = task_kind.into();
305 45632 : EnumMap::from_array(std::array::from_fn(|content_kind| {
306 45632 : let content_kind = PageContentKind::from_usize(content_kind);
307 45632 : let content_kind: &'static str = content_kind.into();
308 45632 : PageCacheMetricsForTaskKind {
309 45632 : read_accesses_immutable: {
310 45632 : PAGE_CACHE_READ_ACCESSES
311 45632 : .get_metric_with_label_values(&[task_kind, "immutable", content_kind])
312 45632 : .unwrap()
313 45632 : },
314 45632 :
315 45632 : read_hits_immutable: {
316 45632 : PAGE_CACHE_READ_HITS
317 45632 : .get_metric_with_label_values(&[task_kind, "immutable", content_kind, "-"])
318 45632 : .unwrap()
319 45632 : },
320 45632 : }
321 45632 : }))
322 5704 : })),
323 184 : });
324 :
325 : impl PageCacheMetrics {
326 2331602 : pub(crate) fn for_ctx(&self, ctx: &RequestContext) -> &PageCacheMetricsForTaskKind {
327 2331602 : &self.map[ctx.task_kind()][ctx.page_content_kind()]
328 2331602 : }
329 : }
330 :
331 : pub(crate) struct PageCacheSizeMetrics {
332 : pub max_bytes: UIntGauge,
333 :
334 : pub current_bytes_immutable: UIntGauge,
335 : }
336 :
337 184 : static PAGE_CACHE_SIZE_CURRENT_BYTES: Lazy<UIntGaugeVec> = Lazy::new(|| {
338 184 : register_uint_gauge_vec!(
339 184 : "pageserver_page_cache_size_current_bytes",
340 184 : "Current size of the page cache in bytes, by key kind",
341 184 : &["key_kind"]
342 184 : )
343 184 : .expect("failed to define a metric")
344 184 : });
345 :
346 : pub(crate) static PAGE_CACHE_SIZE: Lazy<PageCacheSizeMetrics> =
347 184 : Lazy::new(|| PageCacheSizeMetrics {
348 184 : max_bytes: {
349 184 : register_uint_gauge!(
350 184 : "pageserver_page_cache_size_max_bytes",
351 184 : "Maximum size of the page cache in bytes"
352 184 : )
353 184 : .expect("failed to define a metric")
354 184 : },
355 184 : current_bytes_immutable: {
356 184 : PAGE_CACHE_SIZE_CURRENT_BYTES
357 184 : .get_metric_with_label_values(&["immutable"])
358 184 : .unwrap()
359 184 : },
360 184 : });
361 :
362 : pub(crate) mod page_cache_eviction_metrics {
363 : use std::num::NonZeroUsize;
364 :
365 : use metrics::{register_int_counter_vec, IntCounter, IntCounterVec};
366 : use once_cell::sync::Lazy;
367 :
368 : #[derive(Clone, Copy)]
369 : pub(crate) enum Outcome {
370 : FoundSlotUnused { iters: NonZeroUsize },
371 : FoundSlotEvicted { iters: NonZeroUsize },
372 : ItersExceeded { iters: NonZeroUsize },
373 : }
374 :
375 184 : static ITERS_TOTAL_VEC: Lazy<IntCounterVec> = Lazy::new(|| {
376 184 : register_int_counter_vec!(
377 184 : "pageserver_page_cache_find_victim_iters_total",
378 184 : "Counter for the number of iterations in the find_victim loop",
379 184 : &["outcome"],
380 184 : )
381 184 : .expect("failed to define a metric")
382 184 : });
383 :
384 184 : static CALLS_VEC: Lazy<IntCounterVec> = Lazy::new(|| {
385 184 : register_int_counter_vec!(
386 184 : "pageserver_page_cache_find_victim_calls",
387 184 : "Incremented at the end of each find_victim() call.\
388 184 : Filter by outcome to get e.g., eviction rate.",
389 184 : &["outcome"]
390 184 : )
391 184 : .unwrap()
392 184 : });
393 :
394 63485 : pub(crate) fn observe(outcome: Outcome) {
395 : macro_rules! dry {
396 : ($label:literal, $iters:expr) => {{
397 : static LABEL: &'static str = $label;
398 : static ITERS_TOTAL: Lazy<IntCounter> =
399 224 : Lazy::new(|| ITERS_TOTAL_VEC.with_label_values(&[LABEL]));
400 : static CALLS: Lazy<IntCounter> =
401 224 : Lazy::new(|| CALLS_VEC.with_label_values(&[LABEL]));
402 : ITERS_TOTAL.inc_by(($iters.get()) as u64);
403 : CALLS.inc();
404 : }};
405 : }
406 63485 : match outcome {
407 3248 : Outcome::FoundSlotUnused { iters } => dry!("found_empty", iters),
408 60237 : Outcome::FoundSlotEvicted { iters } => {
409 60237 : dry!("found_evicted", iters)
410 : }
411 0 : Outcome::ItersExceeded { iters } => {
412 0 : dry!("err_iters_exceeded", iters);
413 0 : super::page_cache_errors_inc(super::PageCacheErrorKind::EvictIterLimit);
414 0 : }
415 : }
416 63485 : }
417 : }
418 :
419 0 : static PAGE_CACHE_ERRORS: Lazy<IntCounterVec> = Lazy::new(|| {
420 0 : register_int_counter_vec!(
421 0 : "page_cache_errors_total",
422 0 : "Number of timeouts while acquiring a pinned slot in the page cache",
423 0 : &["error_kind"]
424 0 : )
425 0 : .expect("failed to define a metric")
426 0 : });
427 :
428 : #[derive(IntoStaticStr)]
429 : #[strum(serialize_all = "kebab_case")]
430 : pub(crate) enum PageCacheErrorKind {
431 : AcquirePinnedSlotTimeout,
432 : EvictIterLimit,
433 : }
434 :
435 0 : pub(crate) fn page_cache_errors_inc(error_kind: PageCacheErrorKind) {
436 0 : PAGE_CACHE_ERRORS
437 0 : .get_metric_with_label_values(&[error_kind.into()])
438 0 : .unwrap()
439 0 : .inc();
440 0 : }
441 :
442 40 : pub(crate) static WAIT_LSN_TIME: Lazy<Histogram> = Lazy::new(|| {
443 40 : register_histogram!(
444 40 : "pageserver_wait_lsn_seconds",
445 40 : "Time spent waiting for WAL to arrive",
446 40 : CRITICAL_OP_BUCKETS.into(),
447 40 : )
448 40 : .expect("failed to define a metric")
449 40 : });
450 :
451 404 : static FLUSH_WAIT_UPLOAD_TIME: Lazy<GaugeVec> = Lazy::new(|| {
452 404 : register_gauge_vec!(
453 404 : "pageserver_flush_wait_upload_seconds",
454 404 : "Time spent waiting for preceding uploads during layer flush",
455 404 : &["tenant_id", "shard_id", "timeline_id"]
456 404 : )
457 404 : .expect("failed to define a metric")
458 404 : });
459 :
460 404 : static LAST_RECORD_LSN: Lazy<IntGaugeVec> = Lazy::new(|| {
461 404 : register_int_gauge_vec!(
462 404 : "pageserver_last_record_lsn",
463 404 : "Last record LSN grouped by timeline",
464 404 : &["tenant_id", "shard_id", "timeline_id"]
465 404 : )
466 404 : .expect("failed to define a metric")
467 404 : });
468 :
469 404 : static DISK_CONSISTENT_LSN: Lazy<IntGaugeVec> = Lazy::new(|| {
470 404 : register_int_gauge_vec!(
471 404 : "pageserver_disk_consistent_lsn",
472 404 : "Disk consistent LSN grouped by timeline",
473 404 : &["tenant_id", "shard_id", "timeline_id"]
474 404 : )
475 404 : .expect("failed to define a metric")
476 404 : });
477 :
478 404 : pub(crate) static PROJECTED_REMOTE_CONSISTENT_LSN: Lazy<UIntGaugeVec> = Lazy::new(|| {
479 404 : register_uint_gauge_vec!(
480 404 : "pageserver_projected_remote_consistent_lsn",
481 404 : "Projected remote consistent LSN grouped by timeline",
482 404 : &["tenant_id", "shard_id", "timeline_id"]
483 404 : )
484 404 : .expect("failed to define a metric")
485 404 : });
486 :
487 404 : static PITR_HISTORY_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
488 404 : register_uint_gauge_vec!(
489 404 : "pageserver_pitr_history_size",
490 404 : "Data written since PITR cutoff on this timeline",
491 404 : &["tenant_id", "shard_id", "timeline_id"]
492 404 : )
493 404 : .expect("failed to define a metric")
494 404 : });
495 :
496 : #[derive(
497 240 : strum_macros::EnumIter,
498 0 : strum_macros::EnumString,
499 : strum_macros::Display,
500 : strum_macros::IntoStaticStr,
501 : )]
502 : #[strum(serialize_all = "kebab_case")]
503 : pub(crate) enum LayerKind {
504 : Delta,
505 : Image,
506 : }
507 :
508 : #[derive(
509 100 : strum_macros::EnumIter,
510 0 : strum_macros::EnumString,
511 : strum_macros::Display,
512 : strum_macros::IntoStaticStr,
513 : )]
514 : #[strum(serialize_all = "kebab_case")]
515 : pub(crate) enum LayerLevel {
516 : // We don't track the currently open ephemeral layer, since there's always exactly 1 and its
517 : // size changes. See `TIMELINE_EPHEMERAL_BYTES`.
518 : Frozen,
519 : L0,
520 : L1,
521 : }
522 :
523 396 : static TIMELINE_LAYER_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
524 396 : register_uint_gauge_vec!(
525 396 : "pageserver_layer_bytes",
526 396 : "Sum of frozen, L0, and L1 layer physical sizes in bytes (excluding the open ephemeral layer)",
527 396 : &["tenant_id", "shard_id", "timeline_id", "level", "kind"]
528 396 : )
529 396 : .expect("failed to define a metric")
530 396 : });
531 :
532 396 : static TIMELINE_LAYER_COUNT: Lazy<UIntGaugeVec> = Lazy::new(|| {
533 396 : register_uint_gauge_vec!(
534 396 : "pageserver_layer_count",
535 396 : "Number of frozen, L0, and L1 layers (excluding the open ephemeral layer)",
536 396 : &["tenant_id", "shard_id", "timeline_id", "level", "kind"]
537 396 : )
538 396 : .expect("failed to define a metric")
539 396 : });
540 :
541 404 : static TIMELINE_ARCHIVE_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
542 404 : register_uint_gauge_vec!(
543 404 : "pageserver_archive_size",
544 404 : "Timeline's logical size if it is considered eligible for archival (outside PITR window), else zero",
545 404 : &["tenant_id", "shard_id", "timeline_id"]
546 404 : )
547 404 : .expect("failed to define a metric")
548 404 : });
549 :
550 404 : static STANDBY_HORIZON: Lazy<IntGaugeVec> = Lazy::new(|| {
551 404 : register_int_gauge_vec!(
552 404 : "pageserver_standby_horizon",
553 404 : "Standby apply LSN for which GC is hold off, by timeline.",
554 404 : &["tenant_id", "shard_id", "timeline_id"]
555 404 : )
556 404 : .expect("failed to define a metric")
557 404 : });
558 :
559 404 : static RESIDENT_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
560 404 : register_uint_gauge_vec!(
561 404 : "pageserver_resident_physical_size",
562 404 : "The size of the layer files present in the pageserver's filesystem, for attached locations.",
563 404 : &["tenant_id", "shard_id", "timeline_id"]
564 404 : )
565 404 : .expect("failed to define a metric")
566 404 : });
567 :
568 404 : static VISIBLE_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
569 404 : register_uint_gauge_vec!(
570 404 : "pageserver_visible_physical_size",
571 404 : "The size of the layer files present in the pageserver's filesystem.",
572 404 : &["tenant_id", "shard_id", "timeline_id"]
573 404 : )
574 404 : .expect("failed to define a metric")
575 404 : });
576 :
577 396 : pub(crate) static RESIDENT_PHYSICAL_SIZE_GLOBAL: Lazy<UIntGauge> = Lazy::new(|| {
578 396 : register_uint_gauge!(
579 396 : "pageserver_resident_physical_size_global",
580 396 : "Like `pageserver_resident_physical_size`, but without tenant/timeline dimensions."
581 396 : )
582 396 : .expect("failed to define a metric")
583 396 : });
584 :
585 404 : static REMOTE_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
586 404 : register_uint_gauge_vec!(
587 404 : "pageserver_remote_physical_size",
588 404 : "The size of the layer files present in the remote storage that are listed in the remote index_part.json.",
589 404 : // Corollary: If any files are missing from the index part, they won't be included here.
590 404 : &["tenant_id", "shard_id", "timeline_id"]
591 404 : )
592 404 : .expect("failed to define a metric")
593 404 : });
594 :
595 404 : static REMOTE_PHYSICAL_SIZE_GLOBAL: Lazy<UIntGauge> = Lazy::new(|| {
596 404 : register_uint_gauge!(
597 404 : "pageserver_remote_physical_size_global",
598 404 : "Like `pageserver_remote_physical_size`, but without tenant/timeline dimensions."
599 404 : )
600 404 : .expect("failed to define a metric")
601 404 : });
602 :
603 12 : pub(crate) static REMOTE_ONDEMAND_DOWNLOADED_LAYERS: Lazy<IntCounter> = Lazy::new(|| {
604 12 : register_int_counter!(
605 12 : "pageserver_remote_ondemand_downloaded_layers_total",
606 12 : "Total on-demand downloaded layers"
607 12 : )
608 12 : .unwrap()
609 12 : });
610 :
611 12 : pub(crate) static REMOTE_ONDEMAND_DOWNLOADED_BYTES: Lazy<IntCounter> = Lazy::new(|| {
612 12 : register_int_counter!(
613 12 : "pageserver_remote_ondemand_downloaded_bytes_total",
614 12 : "Total bytes of layers on-demand downloaded",
615 12 : )
616 12 : .unwrap()
617 12 : });
618 :
619 404 : static CURRENT_LOGICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
620 404 : register_uint_gauge_vec!(
621 404 : "pageserver_current_logical_size",
622 404 : "Current logical size grouped by timeline",
623 404 : &["tenant_id", "shard_id", "timeline_id"]
624 404 : )
625 404 : .expect("failed to define current logical size metric")
626 404 : });
627 :
628 404 : static AUX_FILE_SIZE: Lazy<IntGaugeVec> = Lazy::new(|| {
629 404 : register_int_gauge_vec!(
630 404 : "pageserver_aux_file_estimated_size",
631 404 : "The size of all aux files for a timeline in aux file v2 store.",
632 404 : &["tenant_id", "shard_id", "timeline_id"]
633 404 : )
634 404 : .expect("failed to define a metric")
635 404 : });
636 :
637 404 : static VALID_LSN_LEASE_COUNT: Lazy<UIntGaugeVec> = Lazy::new(|| {
638 404 : register_uint_gauge_vec!(
639 404 : "pageserver_valid_lsn_lease_count",
640 404 : "The number of valid leases after refreshing gc info.",
641 404 : &["tenant_id", "shard_id", "timeline_id"],
642 404 : )
643 404 : .expect("failed to define a metric")
644 404 : });
645 :
646 0 : pub(crate) static CIRCUIT_BREAKERS_BROKEN: Lazy<IntCounter> = Lazy::new(|| {
647 0 : register_int_counter!(
648 0 : "pageserver_circuit_breaker_broken",
649 0 : "How many times a circuit breaker has broken"
650 0 : )
651 0 : .expect("failed to define a metric")
652 0 : });
653 :
654 0 : pub(crate) static CIRCUIT_BREAKERS_UNBROKEN: Lazy<IntCounter> = Lazy::new(|| {
655 0 : register_int_counter!(
656 0 : "pageserver_circuit_breaker_unbroken",
657 0 : "How many times a circuit breaker has been un-broken (recovered)"
658 0 : )
659 0 : .expect("failed to define a metric")
660 0 : });
661 :
662 388 : pub(crate) static COMPRESSION_IMAGE_INPUT_BYTES: Lazy<IntCounter> = Lazy::new(|| {
663 388 : register_int_counter!(
664 388 : "pageserver_compression_image_in_bytes_total",
665 388 : "Size of data written into image layers before compression"
666 388 : )
667 388 : .expect("failed to define a metric")
668 388 : });
669 :
670 388 : pub(crate) static COMPRESSION_IMAGE_INPUT_BYTES_CONSIDERED: Lazy<IntCounter> = Lazy::new(|| {
671 388 : register_int_counter!(
672 388 : "pageserver_compression_image_in_bytes_considered",
673 388 : "Size of potentially compressible data written into image layers before compression"
674 388 : )
675 388 : .expect("failed to define a metric")
676 388 : });
677 :
678 388 : pub(crate) static COMPRESSION_IMAGE_INPUT_BYTES_CHOSEN: Lazy<IntCounter> = Lazy::new(|| {
679 388 : register_int_counter!(
680 388 : "pageserver_compression_image_in_bytes_chosen",
681 388 : "Size of data whose compressed form was written into image layers"
682 388 : )
683 388 : .expect("failed to define a metric")
684 388 : });
685 :
686 388 : pub(crate) static COMPRESSION_IMAGE_OUTPUT_BYTES: Lazy<IntCounter> = Lazy::new(|| {
687 388 : register_int_counter!(
688 388 : "pageserver_compression_image_out_bytes_total",
689 388 : "Size of compressed image layer written"
690 388 : )
691 388 : .expect("failed to define a metric")
692 388 : });
693 :
694 20 : pub(crate) static RELSIZE_CACHE_ENTRIES: Lazy<UIntGauge> = Lazy::new(|| {
695 20 : register_uint_gauge!(
696 20 : "pageserver_relsize_cache_entries",
697 20 : "Number of entries in the relation size cache",
698 20 : )
699 20 : .expect("failed to define a metric")
700 20 : });
701 :
702 20 : pub(crate) static RELSIZE_CACHE_HITS: Lazy<IntCounter> = Lazy::new(|| {
703 20 : register_int_counter!("pageserver_relsize_cache_hits", "Relation size cache hits",)
704 20 : .expect("failed to define a metric")
705 20 : });
706 :
707 20 : pub(crate) static RELSIZE_CACHE_MISSES: Lazy<IntCounter> = Lazy::new(|| {
708 20 : register_int_counter!(
709 20 : "pageserver_relsize_cache_misses",
710 20 : "Relation size cache misses",
711 20 : )
712 20 : .expect("failed to define a metric")
713 20 : });
714 :
715 8 : pub(crate) static RELSIZE_CACHE_MISSES_OLD: Lazy<IntCounter> = Lazy::new(|| {
716 8 : register_int_counter!(
717 8 : "pageserver_relsize_cache_misses_old",
718 8 : "Relation size cache misses where the lookup LSN is older than the last relation update"
719 8 : )
720 8 : .expect("failed to define a metric")
721 8 : });
722 :
723 : pub(crate) mod initial_logical_size {
724 : use metrics::{register_int_counter, register_int_counter_vec, IntCounter, IntCounterVec};
725 : use once_cell::sync::Lazy;
726 :
727 : pub(crate) struct StartCalculation(IntCounterVec);
728 404 : pub(crate) static START_CALCULATION: Lazy<StartCalculation> = Lazy::new(|| {
729 404 : StartCalculation(
730 404 : register_int_counter_vec!(
731 404 : "pageserver_initial_logical_size_start_calculation",
732 404 : "Incremented each time we start an initial logical size calculation attempt. \
733 404 : The `circumstances` label provides some additional details.",
734 404 : &["attempt", "circumstances"]
735 404 : )
736 404 : .unwrap(),
737 404 : )
738 404 : });
739 :
740 : struct DropCalculation {
741 : first: IntCounter,
742 : retry: IntCounter,
743 : }
744 :
745 404 : static DROP_CALCULATION: Lazy<DropCalculation> = Lazy::new(|| {
746 404 : let vec = register_int_counter_vec!(
747 404 : "pageserver_initial_logical_size_drop_calculation",
748 404 : "Incremented each time we abort a started size calculation attmpt.",
749 404 : &["attempt"]
750 404 : )
751 404 : .unwrap();
752 404 : DropCalculation {
753 404 : first: vec.with_label_values(&["first"]),
754 404 : retry: vec.with_label_values(&["retry"]),
755 404 : }
756 404 : });
757 :
758 : pub(crate) struct Calculated {
759 : pub(crate) births: IntCounter,
760 : pub(crate) deaths: IntCounter,
761 : }
762 :
763 404 : pub(crate) static CALCULATED: Lazy<Calculated> = Lazy::new(|| Calculated {
764 404 : births: register_int_counter!(
765 404 : "pageserver_initial_logical_size_finish_calculation",
766 404 : "Incremented every time we finish calculation of initial logical size.\
767 404 : If everything is working well, this should happen at most once per Timeline object."
768 404 : )
769 404 : .unwrap(),
770 404 : deaths: register_int_counter!(
771 404 : "pageserver_initial_logical_size_drop_finished_calculation",
772 404 : "Incremented when we drop a finished initial logical size calculation result.\
773 404 : Mainly useful to turn pageserver_initial_logical_size_finish_calculation into a gauge."
774 404 : )
775 404 : .unwrap(),
776 404 : });
777 :
778 : pub(crate) struct OngoingCalculationGuard {
779 : inc_drop_calculation: Option<IntCounter>,
780 : }
781 :
782 : #[derive(strum_macros::IntoStaticStr)]
783 : pub(crate) enum StartCircumstances {
784 : EmptyInitial,
785 : SkippedConcurrencyLimiter,
786 : AfterBackgroundTasksRateLimit,
787 : }
788 :
789 : impl StartCalculation {
790 428 : pub(crate) fn first(&self, circumstances: StartCircumstances) -> OngoingCalculationGuard {
791 428 : let circumstances_label: &'static str = circumstances.into();
792 428 : self.0
793 428 : .with_label_values(&["first", circumstances_label])
794 428 : .inc();
795 428 : OngoingCalculationGuard {
796 428 : inc_drop_calculation: Some(DROP_CALCULATION.first.clone()),
797 428 : }
798 428 : }
799 0 : pub(crate) fn retry(&self, circumstances: StartCircumstances) -> OngoingCalculationGuard {
800 0 : let circumstances_label: &'static str = circumstances.into();
801 0 : self.0
802 0 : .with_label_values(&["retry", circumstances_label])
803 0 : .inc();
804 0 : OngoingCalculationGuard {
805 0 : inc_drop_calculation: Some(DROP_CALCULATION.retry.clone()),
806 0 : }
807 0 : }
808 : }
809 :
810 : impl Drop for OngoingCalculationGuard {
811 428 : fn drop(&mut self) {
812 428 : if let Some(counter) = self.inc_drop_calculation.take() {
813 0 : counter.inc();
814 428 : }
815 428 : }
816 : }
817 :
818 : impl OngoingCalculationGuard {
819 428 : pub(crate) fn calculation_result_saved(mut self) -> FinishedCalculationGuard {
820 428 : drop(self.inc_drop_calculation.take());
821 428 : CALCULATED.births.inc();
822 428 : FinishedCalculationGuard {
823 428 : inc_on_drop: CALCULATED.deaths.clone(),
824 428 : }
825 428 : }
826 : }
827 :
828 : pub(crate) struct FinishedCalculationGuard {
829 : inc_on_drop: IntCounter,
830 : }
831 :
832 : impl Drop for FinishedCalculationGuard {
833 12 : fn drop(&mut self) {
834 12 : self.inc_on_drop.inc();
835 12 : }
836 : }
837 :
838 : // context: https://github.com/neondatabase/neon/issues/5963
839 : pub(crate) static TIMELINES_WHERE_WALRECEIVER_GOT_APPROXIMATE_SIZE: Lazy<IntCounter> =
840 0 : Lazy::new(|| {
841 0 : register_int_counter!(
842 0 : "pageserver_initial_logical_size_timelines_where_walreceiver_got_approximate_size",
843 0 : "Counter for the following event: walreceiver calls\
844 0 : Timeline::get_current_logical_size() and it returns `Approximate` for the first time."
845 0 : )
846 0 : .unwrap()
847 0 : });
848 : }
849 :
850 0 : static DIRECTORY_ENTRIES_COUNT: Lazy<UIntGaugeVec> = Lazy::new(|| {
851 0 : register_uint_gauge_vec!(
852 0 : "pageserver_directory_entries_count",
853 0 : "Sum of the entries in pageserver-stored directory listings",
854 0 : &["tenant_id", "shard_id", "timeline_id"]
855 0 : )
856 0 : .expect("failed to define a metric")
857 0 : });
858 :
859 408 : pub(crate) static TENANT_STATE_METRIC: Lazy<UIntGaugeVec> = Lazy::new(|| {
860 408 : register_uint_gauge_vec!(
861 408 : "pageserver_tenant_states_count",
862 408 : "Count of tenants per state",
863 408 : &["state"]
864 408 : )
865 408 : .expect("Failed to register pageserver_tenant_states_count metric")
866 408 : });
867 :
868 : /// A set of broken tenants.
869 : ///
870 : /// These are expected to be so rare that a set is fine. Set as in a new timeseries per each broken
871 : /// tenant.
872 20 : pub(crate) static BROKEN_TENANTS_SET: Lazy<UIntGaugeVec> = Lazy::new(|| {
873 20 : register_uint_gauge_vec!(
874 20 : "pageserver_broken_tenants_count",
875 20 : "Set of broken tenants",
876 20 : &["tenant_id", "shard_id"]
877 20 : )
878 20 : .expect("Failed to register pageserver_tenant_states_count metric")
879 20 : });
880 :
881 12 : pub(crate) static TENANT_SYNTHETIC_SIZE_METRIC: Lazy<UIntGaugeVec> = Lazy::new(|| {
882 12 : register_uint_gauge_vec!(
883 12 : "pageserver_tenant_synthetic_cached_size_bytes",
884 12 : "Synthetic size of each tenant in bytes",
885 12 : &["tenant_id"]
886 12 : )
887 12 : .expect("Failed to register pageserver_tenant_synthetic_cached_size_bytes metric")
888 12 : });
889 :
890 0 : pub(crate) static EVICTION_ITERATION_DURATION: Lazy<HistogramVec> = Lazy::new(|| {
891 0 : register_histogram_vec!(
892 0 : "pageserver_eviction_iteration_duration_seconds_global",
893 0 : "Time spent on a single eviction iteration",
894 0 : &["period_secs", "threshold_secs"],
895 0 : STORAGE_OP_BUCKETS.into(),
896 0 : )
897 0 : .expect("failed to define a metric")
898 0 : });
899 :
900 404 : static EVICTIONS: Lazy<IntCounterVec> = Lazy::new(|| {
901 404 : register_int_counter_vec!(
902 404 : "pageserver_evictions",
903 404 : "Number of layers evicted from the pageserver",
904 404 : &["tenant_id", "shard_id", "timeline_id"]
905 404 : )
906 404 : .expect("failed to define a metric")
907 404 : });
908 :
909 404 : static EVICTIONS_WITH_LOW_RESIDENCE_DURATION: Lazy<IntCounterVec> = Lazy::new(|| {
910 404 : register_int_counter_vec!(
911 404 : "pageserver_evictions_with_low_residence_duration",
912 404 : "If a layer is evicted that was resident for less than `low_threshold`, it is counted to this counter. \
913 404 : Residence duration is determined using the `residence_duration_data_source`.",
914 404 : &["tenant_id", "shard_id", "timeline_id", "residence_duration_data_source", "low_threshold_secs"]
915 404 : )
916 404 : .expect("failed to define a metric")
917 404 : });
918 :
919 0 : pub(crate) static UNEXPECTED_ONDEMAND_DOWNLOADS: Lazy<IntCounter> = Lazy::new(|| {
920 0 : register_int_counter!(
921 0 : "pageserver_unexpected_ondemand_downloads_count",
922 0 : "Number of unexpected on-demand downloads. \
923 0 : We log more context for each increment, so, forgo any labels in this metric.",
924 0 : )
925 0 : .expect("failed to define a metric")
926 0 : });
927 :
928 : /// How long did we take to start up? Broken down by labels to describe
929 : /// different phases of startup.
930 0 : pub static STARTUP_DURATION: Lazy<GaugeVec> = Lazy::new(|| {
931 0 : register_gauge_vec!(
932 0 : "pageserver_startup_duration_seconds",
933 0 : "Time taken by phases of pageserver startup, in seconds",
934 0 : &["phase"]
935 0 : )
936 0 : .expect("Failed to register pageserver_startup_duration_seconds metric")
937 0 : });
938 :
939 0 : pub static STARTUP_IS_LOADING: Lazy<UIntGauge> = Lazy::new(|| {
940 0 : register_uint_gauge!(
941 0 : "pageserver_startup_is_loading",
942 0 : "1 while in initial startup load of tenants, 0 at other times"
943 0 : )
944 0 : .expect("Failed to register pageserver_startup_is_loading")
945 0 : });
946 :
947 396 : pub(crate) static TIMELINE_EPHEMERAL_BYTES: Lazy<UIntGauge> = Lazy::new(|| {
948 396 : register_uint_gauge!(
949 396 : "pageserver_timeline_ephemeral_bytes",
950 396 : "Total number of bytes in ephemeral layers, summed for all timelines. Approximate, lazily updated."
951 396 : )
952 396 : .expect("Failed to register metric")
953 396 : });
954 :
955 : /// Metrics related to the lifecycle of a [`crate::tenant::Tenant`] object: things
956 : /// like how long it took to load.
957 : ///
958 : /// Note that these are process-global metrics, _not_ per-tenant metrics. Per-tenant
959 : /// metrics are rather expensive, and usually fine grained stuff makes more sense
960 : /// at a timeline level than tenant level.
961 : pub(crate) struct TenantMetrics {
962 : /// How long did tenants take to go from construction to active state?
963 : pub(crate) activation: Histogram,
964 : pub(crate) preload: Histogram,
965 : pub(crate) attach: Histogram,
966 :
967 : /// How many tenants are included in the initial startup of the pagesrever?
968 : pub(crate) startup_scheduled: IntCounter,
969 : pub(crate) startup_complete: IntCounter,
970 : }
971 :
972 0 : pub(crate) static TENANT: Lazy<TenantMetrics> = Lazy::new(|| {
973 0 : TenantMetrics {
974 0 : activation: register_histogram!(
975 0 : "pageserver_tenant_activation_seconds",
976 0 : "Time taken by tenants to activate, in seconds",
977 0 : CRITICAL_OP_BUCKETS.into()
978 0 : )
979 0 : .expect("Failed to register metric"),
980 0 : preload: register_histogram!(
981 0 : "pageserver_tenant_preload_seconds",
982 0 : "Time taken by tenants to load remote metadata on startup/attach, in seconds",
983 0 : CRITICAL_OP_BUCKETS.into()
984 0 : )
985 0 : .expect("Failed to register metric"),
986 0 : attach: register_histogram!(
987 0 : "pageserver_tenant_attach_seconds",
988 0 : "Time taken by tenants to intialize, after remote metadata is already loaded",
989 0 : CRITICAL_OP_BUCKETS.into()
990 0 : )
991 0 : .expect("Failed to register metric"),
992 0 : startup_scheduled: register_int_counter!(
993 0 : "pageserver_tenant_startup_scheduled",
994 0 : "Number of tenants included in pageserver startup (doesn't count tenants attached later)"
995 0 : ).expect("Failed to register metric"),
996 0 : startup_complete: register_int_counter!(
997 0 : "pageserver_tenant_startup_complete",
998 0 : "Number of tenants that have completed warm-up, or activated on-demand during initial startup: \
999 0 : should eventually reach `pageserver_tenant_startup_scheduled_total`. Does not include broken \
1000 0 : tenants: such cases will lead to this metric never reaching the scheduled count."
1001 0 : ).expect("Failed to register metric"),
1002 0 : }
1003 0 : });
1004 :
1005 : /// Each `Timeline`'s [`EVICTIONS_WITH_LOW_RESIDENCE_DURATION`] metric.
1006 : #[derive(Debug)]
1007 : pub(crate) struct EvictionsWithLowResidenceDuration {
1008 : data_source: &'static str,
1009 : threshold: Duration,
1010 : counter: Option<IntCounter>,
1011 : }
1012 :
1013 : pub(crate) struct EvictionsWithLowResidenceDurationBuilder {
1014 : data_source: &'static str,
1015 : threshold: Duration,
1016 : }
1017 :
1018 : impl EvictionsWithLowResidenceDurationBuilder {
1019 896 : pub fn new(data_source: &'static str, threshold: Duration) -> Self {
1020 896 : Self {
1021 896 : data_source,
1022 896 : threshold,
1023 896 : }
1024 896 : }
1025 :
1026 896 : fn build(
1027 896 : &self,
1028 896 : tenant_id: &str,
1029 896 : shard_id: &str,
1030 896 : timeline_id: &str,
1031 896 : ) -> EvictionsWithLowResidenceDuration {
1032 896 : let counter = EVICTIONS_WITH_LOW_RESIDENCE_DURATION
1033 896 : .get_metric_with_label_values(&[
1034 896 : tenant_id,
1035 896 : shard_id,
1036 896 : timeline_id,
1037 896 : self.data_source,
1038 896 : &EvictionsWithLowResidenceDuration::threshold_label_value(self.threshold),
1039 896 : ])
1040 896 : .unwrap();
1041 896 : EvictionsWithLowResidenceDuration {
1042 896 : data_source: self.data_source,
1043 896 : threshold: self.threshold,
1044 896 : counter: Some(counter),
1045 896 : }
1046 896 : }
1047 : }
1048 :
1049 : impl EvictionsWithLowResidenceDuration {
1050 916 : fn threshold_label_value(threshold: Duration) -> String {
1051 916 : format!("{}", threshold.as_secs())
1052 916 : }
1053 :
1054 8 : pub fn observe(&self, observed_value: Duration) {
1055 8 : if observed_value < self.threshold {
1056 8 : self.counter
1057 8 : .as_ref()
1058 8 : .expect("nobody calls this function after `remove_from_vec`")
1059 8 : .inc();
1060 8 : }
1061 8 : }
1062 :
1063 0 : pub fn change_threshold(
1064 0 : &mut self,
1065 0 : tenant_id: &str,
1066 0 : shard_id: &str,
1067 0 : timeline_id: &str,
1068 0 : new_threshold: Duration,
1069 0 : ) {
1070 0 : if new_threshold == self.threshold {
1071 0 : return;
1072 0 : }
1073 0 : let mut with_new = EvictionsWithLowResidenceDurationBuilder::new(
1074 0 : self.data_source,
1075 0 : new_threshold,
1076 0 : )
1077 0 : .build(tenant_id, shard_id, timeline_id);
1078 0 : std::mem::swap(self, &mut with_new);
1079 0 : with_new.remove(tenant_id, shard_id, timeline_id);
1080 0 : }
1081 :
1082 : // This could be a `Drop` impl, but, we need the `tenant_id` and `timeline_id`.
1083 20 : fn remove(&mut self, tenant_id: &str, shard_id: &str, timeline_id: &str) {
1084 20 : let Some(_counter) = self.counter.take() else {
1085 0 : return;
1086 : };
1087 :
1088 20 : let threshold = Self::threshold_label_value(self.threshold);
1089 20 :
1090 20 : let removed = EVICTIONS_WITH_LOW_RESIDENCE_DURATION.remove_label_values(&[
1091 20 : tenant_id,
1092 20 : shard_id,
1093 20 : timeline_id,
1094 20 : self.data_source,
1095 20 : &threshold,
1096 20 : ]);
1097 20 :
1098 20 : match removed {
1099 0 : Err(e) => {
1100 0 : // this has been hit in staging as
1101 0 : // <https://neondatabase.sentry.io/issues/4142396994/>, but we don't know how.
1102 0 : // because we can be in the drop path already, don't risk:
1103 0 : // - "double-panic => illegal instruction" or
1104 0 : // - future "drop panick => abort"
1105 0 : //
1106 0 : // so just nag: (the error has the labels)
1107 0 : tracing::warn!("failed to remove EvictionsWithLowResidenceDuration, it was already removed? {e:#?}");
1108 : }
1109 : Ok(()) => {
1110 : // to help identify cases where we double-remove the same values, let's log all
1111 : // deletions?
1112 20 : tracing::info!("removed EvictionsWithLowResidenceDuration with {tenant_id}, {timeline_id}, {}, {threshold}", self.data_source);
1113 : }
1114 : }
1115 20 : }
1116 : }
1117 :
1118 : // Metrics collected on disk IO operations
1119 : //
1120 : // Roughly logarithmic scale.
1121 : const STORAGE_IO_TIME_BUCKETS: &[f64] = &[
1122 : 0.000030, // 30 usec
1123 : 0.001000, // 1000 usec
1124 : 0.030, // 30 ms
1125 : 1.000, // 1000 ms
1126 : 30.000, // 30000 ms
1127 : ];
1128 :
1129 : /// VirtualFile fs operation variants.
1130 : ///
1131 : /// Operations:
1132 : /// - open ([`std::fs::OpenOptions::open`])
1133 : /// - close (dropping [`crate::virtual_file::VirtualFile`])
1134 : /// - close-by-replace (close by replacement algorithm)
1135 : /// - read (`read_at`)
1136 : /// - write (`write_at`)
1137 : /// - seek (modify internal position or file length query)
1138 : /// - fsync ([`std::fs::File::sync_all`])
1139 : /// - metadata ([`std::fs::File::metadata`])
1140 : #[derive(
1141 0 : Debug, Clone, Copy, strum_macros::EnumCount, strum_macros::EnumIter, strum_macros::FromRepr,
1142 : )]
1143 : pub(crate) enum StorageIoOperation {
1144 : Open,
1145 : OpenAfterReplace,
1146 : Close,
1147 : CloseByReplace,
1148 : Read,
1149 : Write,
1150 : Seek,
1151 : Fsync,
1152 : Metadata,
1153 : }
1154 :
1155 : impl StorageIoOperation {
1156 4176 : pub fn as_str(&self) -> &'static str {
1157 4176 : match self {
1158 464 : StorageIoOperation::Open => "open",
1159 464 : StorageIoOperation::OpenAfterReplace => "open-after-replace",
1160 464 : StorageIoOperation::Close => "close",
1161 464 : StorageIoOperation::CloseByReplace => "close-by-replace",
1162 464 : StorageIoOperation::Read => "read",
1163 464 : StorageIoOperation::Write => "write",
1164 464 : StorageIoOperation::Seek => "seek",
1165 464 : StorageIoOperation::Fsync => "fsync",
1166 464 : StorageIoOperation::Metadata => "metadata",
1167 : }
1168 4176 : }
1169 : }
1170 :
1171 : /// Tracks time taken by fs operations near VirtualFile.
1172 : #[derive(Debug)]
1173 : pub(crate) struct StorageIoTime {
1174 : metrics: [Histogram; StorageIoOperation::COUNT],
1175 : }
1176 :
1177 : impl StorageIoTime {
1178 464 : fn new() -> Self {
1179 464 : let storage_io_histogram_vec = register_histogram_vec!(
1180 464 : "pageserver_io_operations_seconds",
1181 464 : "Time spent in IO operations",
1182 464 : &["operation"],
1183 464 : STORAGE_IO_TIME_BUCKETS.into()
1184 464 : )
1185 464 : .expect("failed to define a metric");
1186 4176 : let metrics = std::array::from_fn(|i| {
1187 4176 : let op = StorageIoOperation::from_repr(i).unwrap();
1188 4176 : storage_io_histogram_vec
1189 4176 : .get_metric_with_label_values(&[op.as_str()])
1190 4176 : .unwrap()
1191 4176 : });
1192 464 : Self { metrics }
1193 464 : }
1194 :
1195 4064697 : pub(crate) fn get(&self, op: StorageIoOperation) -> &Histogram {
1196 4064697 : &self.metrics[op as usize]
1197 4064697 : }
1198 : }
1199 :
1200 : pub(crate) static STORAGE_IO_TIME_METRIC: Lazy<StorageIoTime> = Lazy::new(StorageIoTime::new);
1201 :
1202 : const STORAGE_IO_SIZE_OPERATIONS: &[&str] = &["read", "write"];
1203 :
1204 : // Needed for the https://neonprod.grafana.net/d/5uK9tHL4k/picking-tenant-for-relocation?orgId=1
1205 456 : pub(crate) static STORAGE_IO_SIZE: Lazy<IntGaugeVec> = Lazy::new(|| {
1206 456 : register_int_gauge_vec!(
1207 456 : "pageserver_io_operations_bytes_total",
1208 456 : "Total amount of bytes read/written in IO operations",
1209 456 : &["operation", "tenant_id", "shard_id", "timeline_id"]
1210 456 : )
1211 456 : .expect("failed to define a metric")
1212 456 : });
1213 :
1214 : #[cfg(not(test))]
1215 : pub(crate) mod virtual_file_descriptor_cache {
1216 : use super::*;
1217 :
1218 0 : pub(crate) static SIZE_MAX: Lazy<UIntGauge> = Lazy::new(|| {
1219 0 : register_uint_gauge!(
1220 0 : "pageserver_virtual_file_descriptor_cache_size_max",
1221 0 : "Maximum number of open file descriptors in the cache."
1222 0 : )
1223 0 : .unwrap()
1224 0 : });
1225 :
1226 : // SIZE_CURRENT: derive it like so:
1227 : // ```
1228 : // sum (pageserver_io_operations_seconds_count{operation=~"^(open|open-after-replace)$")
1229 : // -ignoring(operation)
1230 : // sum(pageserver_io_operations_seconds_count{operation=~"^(close|close-by-replace)$"}
1231 : // ```
1232 : }
1233 :
1234 : #[cfg(not(test))]
1235 : pub(crate) mod virtual_file_io_engine {
1236 : use super::*;
1237 :
1238 0 : pub(crate) static KIND: Lazy<UIntGaugeVec> = Lazy::new(|| {
1239 0 : register_uint_gauge_vec!(
1240 0 : "pageserver_virtual_file_io_engine_kind",
1241 0 : "The configured io engine for VirtualFile",
1242 0 : &["kind"],
1243 0 : )
1244 0 : .unwrap()
1245 0 : });
1246 : }
1247 :
1248 : pub(crate) struct SmgrOpTimer(Option<SmgrOpTimerInner>);
1249 : pub(crate) struct SmgrOpTimerInner {
1250 : global_execution_latency_histo: Histogram,
1251 : per_timeline_execution_latency_histo: Option<Histogram>,
1252 :
1253 : global_batch_wait_time: Histogram,
1254 : per_timeline_batch_wait_time: Histogram,
1255 :
1256 : global_flush_in_progress_micros: IntCounter,
1257 : per_timeline_flush_in_progress_micros: IntCounter,
1258 :
1259 : throttling: Arc<tenant_throttling::Pagestream>,
1260 :
1261 : timings: SmgrOpTimerState,
1262 : }
1263 :
1264 : /// The stages of request processing are represented by the enum variants.
1265 : /// Used as part of [`SmgrOpTimerInner::timings`].
1266 : ///
1267 : /// Request processing calls into the `SmgrOpTimer::observe_*` methods at the
1268 : /// transition points.
1269 : /// These methods bump relevant counters and then update [`SmgrOpTimerInner::timings`]
1270 : /// to the next state.
1271 : ///
1272 : /// Each request goes through every stage, in all configurations.
1273 : ///
1274 : #[derive(Debug)]
1275 : enum SmgrOpTimerState {
1276 : Received {
1277 : // In the future, we may want to track the full time the request spent
1278 : // inside pageserver process (time spent in kernel buffers can't be tracked).
1279 : // `received_at` would be used for that.
1280 : #[allow(dead_code)]
1281 : received_at: Instant,
1282 : },
1283 : Throttling {
1284 : throttle_started_at: Instant,
1285 : },
1286 : Batching {
1287 : throttle_done_at: Instant,
1288 : },
1289 : Executing {
1290 : execution_started_at: Instant,
1291 : },
1292 : Flushing,
1293 : // NB: when adding observation points, remember to update the Drop impl.
1294 : }
1295 :
1296 : // NB: when adding observation points, remember to update the Drop impl.
1297 : impl SmgrOpTimer {
1298 : /// See [`SmgrOpTimerState`] for more context.
1299 0 : pub(crate) fn observe_throttle_start(&mut self, at: Instant) {
1300 0 : let Some(inner) = self.0.as_mut() else {
1301 0 : return;
1302 : };
1303 0 : let SmgrOpTimerState::Received { received_at: _ } = &mut inner.timings else {
1304 0 : return;
1305 : };
1306 0 : inner.throttling.count_accounted_start.inc();
1307 0 : inner.timings = SmgrOpTimerState::Throttling {
1308 0 : throttle_started_at: at,
1309 0 : };
1310 0 : }
1311 :
1312 : /// See [`SmgrOpTimerState`] for more context.
1313 0 : pub(crate) fn observe_throttle_done(&mut self, throttle: ThrottleResult) {
1314 0 : let Some(inner) = self.0.as_mut() else {
1315 0 : return;
1316 : };
1317 : let SmgrOpTimerState::Throttling {
1318 0 : throttle_started_at,
1319 0 : } = &inner.timings
1320 : else {
1321 0 : return;
1322 : };
1323 0 : inner.throttling.count_accounted_finish.inc();
1324 0 : match throttle {
1325 0 : ThrottleResult::NotThrottled { end } => {
1326 0 : inner.timings = SmgrOpTimerState::Batching {
1327 0 : throttle_done_at: end,
1328 0 : };
1329 0 : }
1330 0 : ThrottleResult::Throttled { end } => {
1331 0 : // update metrics
1332 0 : inner.throttling.count_throttled.inc();
1333 0 : inner
1334 0 : .throttling
1335 0 : .wait_time
1336 0 : .inc_by((end - *throttle_started_at).as_micros().try_into().unwrap());
1337 0 : // state transition
1338 0 : inner.timings = SmgrOpTimerState::Batching {
1339 0 : throttle_done_at: end,
1340 0 : };
1341 0 : }
1342 : }
1343 0 : }
1344 :
1345 : /// See [`SmgrOpTimerState`] for more context.
1346 0 : pub(crate) fn observe_execution_start(&mut self, at: Instant) {
1347 0 : let Some(inner) = self.0.as_mut() else {
1348 0 : return;
1349 : };
1350 0 : let SmgrOpTimerState::Batching { throttle_done_at } = &inner.timings else {
1351 0 : return;
1352 : };
1353 : // update metrics
1354 0 : let batch = at - *throttle_done_at;
1355 0 : inner.global_batch_wait_time.observe(batch.as_secs_f64());
1356 0 : inner
1357 0 : .per_timeline_batch_wait_time
1358 0 : .observe(batch.as_secs_f64());
1359 0 : // state transition
1360 0 : inner.timings = SmgrOpTimerState::Executing {
1361 0 : execution_started_at: at,
1362 0 : }
1363 0 : }
1364 :
1365 : /// For all but the first caller, this is a no-op.
1366 : /// The first callers receives Some, subsequent ones None.
1367 : ///
1368 : /// See [`SmgrOpTimerState`] for more context.
1369 0 : pub(crate) fn observe_execution_end(&mut self, at: Instant) -> Option<SmgrOpFlushInProgress> {
1370 : // NB: unlike the other observe_* methods, this one take()s.
1371 : #[allow(clippy::question_mark)] // maintain similar code pattern.
1372 0 : let Some(mut inner) = self.0.take() else {
1373 0 : return None;
1374 : };
1375 : let SmgrOpTimerState::Executing {
1376 0 : execution_started_at,
1377 0 : } = &inner.timings
1378 : else {
1379 0 : return None;
1380 : };
1381 : // update metrics
1382 0 : let execution = at - *execution_started_at;
1383 0 : inner
1384 0 : .global_execution_latency_histo
1385 0 : .observe(execution.as_secs_f64());
1386 0 : if let Some(per_timeline_execution_latency_histo) =
1387 0 : &inner.per_timeline_execution_latency_histo
1388 0 : {
1389 0 : per_timeline_execution_latency_histo.observe(execution.as_secs_f64());
1390 0 : }
1391 :
1392 : // state transition
1393 0 : inner.timings = SmgrOpTimerState::Flushing;
1394 0 :
1395 0 : // return the flush in progress object which
1396 0 : // will do the remaining metrics updates
1397 0 : let SmgrOpTimerInner {
1398 0 : global_flush_in_progress_micros,
1399 0 : per_timeline_flush_in_progress_micros,
1400 0 : ..
1401 0 : } = inner;
1402 0 : Some(SmgrOpFlushInProgress {
1403 0 : global_micros: global_flush_in_progress_micros,
1404 0 : per_timeline_micros: per_timeline_flush_in_progress_micros,
1405 0 : })
1406 0 : }
1407 : }
1408 :
1409 : /// The last stage of request processing is serializing and flushing the request
1410 : /// into the TCP connection. We want to make slow flushes observable
1411 : /// _while they are occuring_, so this struct provides a wrapper method [`Self::measure`]
1412 : /// to periodically bump the metric.
1413 : ///
1414 : /// If in the future we decide that we're not interested in live updates, we can
1415 : /// add another `observe_*` method to [`SmgrOpTimer`], follow the existing pattern there,
1416 : /// and remove this struct from the code base.
1417 : pub(crate) struct SmgrOpFlushInProgress {
1418 : global_micros: IntCounter,
1419 : per_timeline_micros: IntCounter,
1420 : }
1421 :
1422 : impl Drop for SmgrOpTimer {
1423 0 : fn drop(&mut self) {
1424 0 : // In case of early drop, update any of the remaining metrics with
1425 0 : // observations so that (started,finished) counter pairs balance out
1426 0 : // and all counters on the latency path have the the same number of
1427 0 : // observations.
1428 0 : // It's technically lying and it would be better if each metric had
1429 0 : // a separate label or similar for cancelled requests.
1430 0 : // But we don't have that right now and counter pairs balancing
1431 0 : // out is useful when using the metrics in panels and whatnot.
1432 0 : let now = Instant::now();
1433 0 : self.observe_throttle_start(now);
1434 0 : self.observe_throttle_done(ThrottleResult::NotThrottled { end: now });
1435 0 : self.observe_execution_start(now);
1436 0 : let maybe_flush_timer = self.observe_execution_end(now);
1437 0 : drop(maybe_flush_timer);
1438 0 : }
1439 : }
1440 :
1441 : impl SmgrOpFlushInProgress {
1442 0 : pub(crate) async fn measure<Fut, O>(self, mut started_at: Instant, mut fut: Fut) -> O
1443 0 : where
1444 0 : Fut: std::future::Future<Output = O>,
1445 0 : {
1446 0 : let mut fut = std::pin::pin!(fut);
1447 0 :
1448 0 : // Whenever observe_guard gets called, or dropped,
1449 0 : // it adds the time elapsed since its last call to metrics.
1450 0 : // Last call is tracked in `now`.
1451 0 : let mut observe_guard = scopeguard::guard(
1452 0 : || {
1453 0 : let now = Instant::now();
1454 0 : let elapsed = now - started_at;
1455 0 : self.global_micros
1456 0 : .inc_by(u64::try_from(elapsed.as_micros()).unwrap());
1457 0 : self.per_timeline_micros
1458 0 : .inc_by(u64::try_from(elapsed.as_micros()).unwrap());
1459 0 : started_at = now;
1460 0 : },
1461 0 : |mut observe| {
1462 0 : observe();
1463 0 : },
1464 0 : );
1465 :
1466 : loop {
1467 0 : match tokio::time::timeout(Duration::from_secs(10), &mut fut).await {
1468 0 : Ok(v) => return v,
1469 0 : Err(_timeout) => {
1470 0 : (*observe_guard)();
1471 0 : }
1472 : }
1473 : }
1474 0 : }
1475 : }
1476 :
1477 : #[derive(
1478 : Debug,
1479 : Clone,
1480 : Copy,
1481 : IntoStaticStr,
1482 : strum_macros::EnumCount,
1483 0 : strum_macros::EnumIter,
1484 : strum_macros::FromRepr,
1485 : enum_map::Enum,
1486 : )]
1487 : #[strum(serialize_all = "snake_case")]
1488 : pub enum SmgrQueryType {
1489 : GetRelExists,
1490 : GetRelSize,
1491 : GetPageAtLsn,
1492 : GetDbSize,
1493 : GetSlruSegment,
1494 : #[cfg(feature = "testing")]
1495 : Test,
1496 : }
1497 :
1498 : pub(crate) struct SmgrQueryTimePerTimeline {
1499 : global_started: [IntCounter; SmgrQueryType::COUNT],
1500 : global_latency: [Histogram; SmgrQueryType::COUNT],
1501 : per_timeline_getpage_started: IntCounter,
1502 : per_timeline_getpage_latency: Histogram,
1503 : global_batch_size: Histogram,
1504 : per_timeline_batch_size: Histogram,
1505 : global_flush_in_progress_micros: IntCounter,
1506 : per_timeline_flush_in_progress_micros: IntCounter,
1507 : global_batch_wait_time: Histogram,
1508 : per_timeline_batch_wait_time: Histogram,
1509 : throttling: Arc<tenant_throttling::Pagestream>,
1510 : }
1511 :
1512 404 : static SMGR_QUERY_STARTED_GLOBAL: Lazy<IntCounterVec> = Lazy::new(|| {
1513 404 : register_int_counter_vec!(
1514 404 : // it's a counter, but, name is prepared to extend it to a histogram of queue depth
1515 404 : "pageserver_smgr_query_started_global_count",
1516 404 : "Number of smgr queries started, aggregated by query type.",
1517 404 : &["smgr_query_type"],
1518 404 : )
1519 404 : .expect("failed to define a metric")
1520 404 : });
1521 :
1522 404 : static SMGR_QUERY_STARTED_PER_TENANT_TIMELINE: Lazy<IntCounterVec> = Lazy::new(|| {
1523 404 : register_int_counter_vec!(
1524 404 : // it's a counter, but, name is prepared to extend it to a histogram of queue depth
1525 404 : "pageserver_smgr_query_started_count",
1526 404 : "Number of smgr queries started, aggregated by query type and tenant/timeline.",
1527 404 : &["smgr_query_type", "tenant_id", "shard_id", "timeline_id"],
1528 404 : )
1529 404 : .expect("failed to define a metric")
1530 404 : });
1531 :
1532 : // Alias so all histograms recording per-timeline smgr timings use the same buckets.
1533 : static SMGR_QUERY_TIME_PER_TENANT_TIMELINE_BUCKETS: &[f64] = CRITICAL_OP_BUCKETS;
1534 :
1535 404 : static SMGR_QUERY_TIME_PER_TENANT_TIMELINE: Lazy<HistogramVec> = Lazy::new(|| {
1536 404 : register_histogram_vec!(
1537 404 : "pageserver_smgr_query_seconds",
1538 404 : "Time spent _executing_ smgr query handling, excluding batch and throttle delays.",
1539 404 : &["smgr_query_type", "tenant_id", "shard_id", "timeline_id"],
1540 404 : SMGR_QUERY_TIME_PER_TENANT_TIMELINE_BUCKETS.into(),
1541 404 : )
1542 404 : .expect("failed to define a metric")
1543 404 : });
1544 :
1545 404 : static SMGR_QUERY_TIME_GLOBAL_BUCKETS: Lazy<Vec<f64>> = Lazy::new(|| {
1546 404 : [
1547 404 : 1,
1548 404 : 10,
1549 404 : 20,
1550 404 : 40,
1551 404 : 60,
1552 404 : 80,
1553 404 : 100,
1554 404 : 200,
1555 404 : 300,
1556 404 : 400,
1557 404 : 500,
1558 404 : 600,
1559 404 : 700,
1560 404 : 800,
1561 404 : 900,
1562 404 : 1_000, // 1ms
1563 404 : 2_000,
1564 404 : 4_000,
1565 404 : 6_000,
1566 404 : 8_000,
1567 404 : 10_000, // 10ms
1568 404 : 20_000,
1569 404 : 40_000,
1570 404 : 60_000,
1571 404 : 80_000,
1572 404 : 100_000,
1573 404 : 200_000,
1574 404 : 400_000,
1575 404 : 600_000,
1576 404 : 800_000,
1577 404 : 1_000_000, // 1s
1578 404 : 2_000_000,
1579 404 : 4_000_000,
1580 404 : 6_000_000,
1581 404 : 8_000_000,
1582 404 : 10_000_000, // 10s
1583 404 : 20_000_000,
1584 404 : 50_000_000,
1585 404 : 100_000_000,
1586 404 : 200_000_000,
1587 404 : 1_000_000_000, // 1000s
1588 404 : ]
1589 404 : .into_iter()
1590 404 : .map(Duration::from_micros)
1591 16564 : .map(|d| d.as_secs_f64())
1592 404 : .collect()
1593 404 : });
1594 :
1595 404 : static SMGR_QUERY_TIME_GLOBAL: Lazy<HistogramVec> = Lazy::new(|| {
1596 404 : register_histogram_vec!(
1597 404 : "pageserver_smgr_query_seconds_global",
1598 404 : "Like pageserver_smgr_query_seconds, but aggregated to instance level.",
1599 404 : &["smgr_query_type"],
1600 404 : SMGR_QUERY_TIME_GLOBAL_BUCKETS.clone(),
1601 404 : )
1602 404 : .expect("failed to define a metric")
1603 404 : });
1604 :
1605 404 : static PAGE_SERVICE_BATCH_SIZE_BUCKETS_GLOBAL: Lazy<Vec<f64>> = Lazy::new(|| {
1606 404 : (1..=u32::try_from(Timeline::MAX_GET_VECTORED_KEYS).unwrap())
1607 12928 : .map(|v| v.into())
1608 404 : .collect()
1609 404 : });
1610 :
1611 404 : static PAGE_SERVICE_BATCH_SIZE_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
1612 404 : register_histogram!(
1613 404 : "pageserver_page_service_batch_size_global",
1614 404 : "Batch size of pageserver page service requests",
1615 404 : PAGE_SERVICE_BATCH_SIZE_BUCKETS_GLOBAL.clone(),
1616 404 : )
1617 404 : .expect("failed to define a metric")
1618 404 : });
1619 :
1620 404 : static PAGE_SERVICE_BATCH_SIZE_BUCKETS_PER_TIMELINE: Lazy<Vec<f64>> = Lazy::new(|| {
1621 404 : let mut buckets = Vec::new();
1622 2828 : for i in 0.. {
1623 2828 : let bucket = 1 << i;
1624 2828 : if bucket > u32::try_from(Timeline::MAX_GET_VECTORED_KEYS).unwrap() {
1625 404 : break;
1626 2424 : }
1627 2424 : buckets.push(bucket.into());
1628 : }
1629 404 : buckets
1630 404 : });
1631 :
1632 404 : static PAGE_SERVICE_BATCH_SIZE_PER_TENANT_TIMELINE: Lazy<HistogramVec> = Lazy::new(|| {
1633 404 : register_histogram_vec!(
1634 404 : "pageserver_page_service_batch_size",
1635 404 : "Batch size of pageserver page service requests",
1636 404 : &["tenant_id", "shard_id", "timeline_id"],
1637 404 : PAGE_SERVICE_BATCH_SIZE_BUCKETS_PER_TIMELINE.clone()
1638 404 : )
1639 404 : .expect("failed to define a metric")
1640 404 : });
1641 :
1642 0 : pub(crate) static PAGE_SERVICE_CONFIG_MAX_BATCH_SIZE: Lazy<IntGaugeVec> = Lazy::new(|| {
1643 0 : register_int_gauge_vec!(
1644 0 : "pageserver_page_service_config_max_batch_size",
1645 0 : "Configured maximum batch size for the server-side batching functionality of page_service. \
1646 0 : Labels expose more of the configuration parameters.",
1647 0 : &["mode", "execution"]
1648 0 : )
1649 0 : .expect("failed to define a metric")
1650 0 : });
1651 :
1652 0 : fn set_page_service_config_max_batch_size(conf: &PageServicePipeliningConfig) {
1653 0 : PAGE_SERVICE_CONFIG_MAX_BATCH_SIZE.reset();
1654 0 : let (label_values, value) = match conf {
1655 0 : PageServicePipeliningConfig::Serial => (["serial", "-"], 1),
1656 : PageServicePipeliningConfig::Pipelined(PageServicePipeliningConfigPipelined {
1657 0 : max_batch_size,
1658 0 : execution,
1659 0 : }) => {
1660 0 : let mode = "pipelined";
1661 0 : let execution = match execution {
1662 : PageServiceProtocolPipelinedExecutionStrategy::ConcurrentFutures => {
1663 0 : "concurrent-futures"
1664 : }
1665 0 : PageServiceProtocolPipelinedExecutionStrategy::Tasks => "tasks",
1666 : };
1667 0 : ([mode, execution], max_batch_size.get())
1668 : }
1669 : };
1670 0 : PAGE_SERVICE_CONFIG_MAX_BATCH_SIZE
1671 0 : .with_label_values(&label_values)
1672 0 : .set(value.try_into().unwrap());
1673 0 : }
1674 :
1675 404 : static PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS: Lazy<IntCounterVec> = Lazy::new(|| {
1676 404 : register_int_counter_vec!(
1677 404 : "pageserver_page_service_pagestream_flush_in_progress_micros",
1678 404 : "Counter that sums up the microseconds that a pagestream response was being flushed into the TCP connection. \
1679 404 : If the flush is particularly slow, this counter will be updated periodically to make slow flushes \
1680 404 : easily discoverable in monitoring. \
1681 404 : Hence, this is NOT a completion latency historgram.",
1682 404 : &["tenant_id", "shard_id", "timeline_id"],
1683 404 : )
1684 404 : .expect("failed to define a metric")
1685 404 : });
1686 :
1687 404 : static PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS_GLOBAL: Lazy<IntCounter> = Lazy::new(|| {
1688 404 : register_int_counter!(
1689 404 : "pageserver_page_service_pagestream_flush_in_progress_micros_global",
1690 404 : "Like pageserver_page_service_pagestream_flush_in_progress_seconds, but instance-wide.",
1691 404 : )
1692 404 : .expect("failed to define a metric")
1693 404 : });
1694 :
1695 404 : static PAGE_SERVICE_SMGR_BATCH_WAIT_TIME: Lazy<HistogramVec> = Lazy::new(|| {
1696 404 : register_histogram_vec!(
1697 404 : "pageserver_page_service_pagestream_batch_wait_time_seconds",
1698 404 : "Time a request spent waiting in its batch until the batch moved to throttle&execution.",
1699 404 : &["tenant_id", "shard_id", "timeline_id"],
1700 404 : SMGR_QUERY_TIME_PER_TENANT_TIMELINE_BUCKETS.into(),
1701 404 : )
1702 404 : .expect("failed to define a metric")
1703 404 : });
1704 :
1705 404 : static PAGE_SERVICE_SMGR_BATCH_WAIT_TIME_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
1706 404 : register_histogram!(
1707 404 : "pageserver_page_service_pagestream_batch_wait_time_seconds_global",
1708 404 : "Like pageserver_page_service_pagestream_batch_wait_time_seconds, but aggregated to instance level.",
1709 404 : SMGR_QUERY_TIME_GLOBAL_BUCKETS.to_vec(),
1710 404 : )
1711 404 : .expect("failed to define a metric")
1712 404 : });
1713 :
1714 : impl SmgrQueryTimePerTimeline {
1715 896 : pub(crate) fn new(
1716 896 : tenant_shard_id: &TenantShardId,
1717 896 : timeline_id: &TimelineId,
1718 896 : pagestream_throttle_metrics: Arc<tenant_throttling::Pagestream>,
1719 896 : ) -> Self {
1720 896 : let tenant_id = tenant_shard_id.tenant_id.to_string();
1721 896 : let shard_slug = format!("{}", tenant_shard_id.shard_slug());
1722 896 : let timeline_id = timeline_id.to_string();
1723 5376 : let global_started = std::array::from_fn(|i| {
1724 5376 : let op = SmgrQueryType::from_repr(i).unwrap();
1725 5376 : SMGR_QUERY_STARTED_GLOBAL
1726 5376 : .get_metric_with_label_values(&[op.into()])
1727 5376 : .unwrap()
1728 5376 : });
1729 5376 : let global_latency = std::array::from_fn(|i| {
1730 5376 : let op = SmgrQueryType::from_repr(i).unwrap();
1731 5376 : SMGR_QUERY_TIME_GLOBAL
1732 5376 : .get_metric_with_label_values(&[op.into()])
1733 5376 : .unwrap()
1734 5376 : });
1735 896 :
1736 896 : let per_timeline_getpage_started = SMGR_QUERY_STARTED_PER_TENANT_TIMELINE
1737 896 : .get_metric_with_label_values(&[
1738 896 : SmgrQueryType::GetPageAtLsn.into(),
1739 896 : &tenant_id,
1740 896 : &shard_slug,
1741 896 : &timeline_id,
1742 896 : ])
1743 896 : .unwrap();
1744 896 : let per_timeline_getpage_latency = SMGR_QUERY_TIME_PER_TENANT_TIMELINE
1745 896 : .get_metric_with_label_values(&[
1746 896 : SmgrQueryType::GetPageAtLsn.into(),
1747 896 : &tenant_id,
1748 896 : &shard_slug,
1749 896 : &timeline_id,
1750 896 : ])
1751 896 : .unwrap();
1752 896 :
1753 896 : let global_batch_size = PAGE_SERVICE_BATCH_SIZE_GLOBAL.clone();
1754 896 : let per_timeline_batch_size = PAGE_SERVICE_BATCH_SIZE_PER_TENANT_TIMELINE
1755 896 : .get_metric_with_label_values(&[&tenant_id, &shard_slug, &timeline_id])
1756 896 : .unwrap();
1757 896 :
1758 896 : let global_batch_wait_time = PAGE_SERVICE_SMGR_BATCH_WAIT_TIME_GLOBAL.clone();
1759 896 : let per_timeline_batch_wait_time = PAGE_SERVICE_SMGR_BATCH_WAIT_TIME
1760 896 : .get_metric_with_label_values(&[&tenant_id, &shard_slug, &timeline_id])
1761 896 : .unwrap();
1762 896 :
1763 896 : let global_flush_in_progress_micros =
1764 896 : PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS_GLOBAL.clone();
1765 896 : let per_timeline_flush_in_progress_micros = PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS
1766 896 : .get_metric_with_label_values(&[&tenant_id, &shard_slug, &timeline_id])
1767 896 : .unwrap();
1768 896 :
1769 896 : Self {
1770 896 : global_started,
1771 896 : global_latency,
1772 896 : per_timeline_getpage_latency,
1773 896 : per_timeline_getpage_started,
1774 896 : global_batch_size,
1775 896 : per_timeline_batch_size,
1776 896 : global_flush_in_progress_micros,
1777 896 : per_timeline_flush_in_progress_micros,
1778 896 : global_batch_wait_time,
1779 896 : per_timeline_batch_wait_time,
1780 896 : throttling: pagestream_throttle_metrics,
1781 896 : }
1782 896 : }
1783 0 : pub(crate) fn start_smgr_op(&self, op: SmgrQueryType, received_at: Instant) -> SmgrOpTimer {
1784 0 : self.global_started[op as usize].inc();
1785 :
1786 0 : let per_timeline_latency_histo = if matches!(op, SmgrQueryType::GetPageAtLsn) {
1787 0 : self.per_timeline_getpage_started.inc();
1788 0 : Some(self.per_timeline_getpage_latency.clone())
1789 : } else {
1790 0 : None
1791 : };
1792 :
1793 0 : SmgrOpTimer(Some(SmgrOpTimerInner {
1794 0 : global_execution_latency_histo: self.global_latency[op as usize].clone(),
1795 0 : per_timeline_execution_latency_histo: per_timeline_latency_histo,
1796 0 : global_flush_in_progress_micros: self.global_flush_in_progress_micros.clone(),
1797 0 : per_timeline_flush_in_progress_micros: self
1798 0 : .per_timeline_flush_in_progress_micros
1799 0 : .clone(),
1800 0 : global_batch_wait_time: self.global_batch_wait_time.clone(),
1801 0 : per_timeline_batch_wait_time: self.per_timeline_batch_wait_time.clone(),
1802 0 : throttling: self.throttling.clone(),
1803 0 : timings: SmgrOpTimerState::Received { received_at },
1804 0 : }))
1805 0 : }
1806 :
1807 : /// TODO: do something about this? seems odd, we have a similar call on SmgrOpTimer
1808 0 : pub(crate) fn observe_getpage_batch_start(&self, batch_size: usize) {
1809 0 : self.global_batch_size.observe(batch_size as f64);
1810 0 : self.per_timeline_batch_size.observe(batch_size as f64);
1811 0 : }
1812 : }
1813 :
1814 : // keep in sync with control plane Go code so that we can validate
1815 : // compute's basebackup_ms metric with our perspective in the context of SLI/SLO.
1816 0 : static COMPUTE_STARTUP_BUCKETS: Lazy<[f64; 28]> = Lazy::new(|| {
1817 0 : // Go code uses milliseconds. Variable is called `computeStartupBuckets`
1818 0 : [
1819 0 : 5, 10, 20, 30, 50, 70, 100, 120, 150, 200, 250, 300, 350, 400, 450, 500, 600, 800, 1000,
1820 0 : 1500, 2000, 2500, 3000, 5000, 10000, 20000, 40000, 60000,
1821 0 : ]
1822 0 : .map(|ms| (ms as f64) / 1000.0)
1823 0 : });
1824 :
1825 : pub(crate) struct BasebackupQueryTime {
1826 : ok: Histogram,
1827 : error: Histogram,
1828 : client_error: Histogram,
1829 : }
1830 :
1831 0 : pub(crate) static BASEBACKUP_QUERY_TIME: Lazy<BasebackupQueryTime> = Lazy::new(|| {
1832 0 : let vec = register_histogram_vec!(
1833 0 : "pageserver_basebackup_query_seconds",
1834 0 : "Histogram of basebackup queries durations, by result type",
1835 0 : &["result"],
1836 0 : COMPUTE_STARTUP_BUCKETS.to_vec(),
1837 0 : )
1838 0 : .expect("failed to define a metric");
1839 0 : BasebackupQueryTime {
1840 0 : ok: vec.get_metric_with_label_values(&["ok"]).unwrap(),
1841 0 : error: vec.get_metric_with_label_values(&["error"]).unwrap(),
1842 0 : client_error: vec.get_metric_with_label_values(&["client_error"]).unwrap(),
1843 0 : }
1844 0 : });
1845 :
1846 : pub(crate) struct BasebackupQueryTimeOngoingRecording<'a> {
1847 : parent: &'a BasebackupQueryTime,
1848 : start: std::time::Instant,
1849 : }
1850 :
1851 : impl BasebackupQueryTime {
1852 0 : pub(crate) fn start_recording(&self) -> BasebackupQueryTimeOngoingRecording<'_> {
1853 0 : let start = Instant::now();
1854 0 : BasebackupQueryTimeOngoingRecording {
1855 0 : parent: self,
1856 0 : start,
1857 0 : }
1858 0 : }
1859 : }
1860 :
1861 : impl BasebackupQueryTimeOngoingRecording<'_> {
1862 0 : pub(crate) fn observe<T>(self, res: &Result<T, QueryError>) {
1863 0 : let elapsed = self.start.elapsed().as_secs_f64();
1864 : // If you want to change categorize of a specific error, also change it in `log_query_error`.
1865 0 : let metric = match res {
1866 0 : Ok(_) => &self.parent.ok,
1867 0 : Err(QueryError::Disconnected(ConnectionError::Io(io_error)))
1868 0 : if is_expected_io_error(io_error) =>
1869 0 : {
1870 0 : &self.parent.client_error
1871 : }
1872 0 : Err(_) => &self.parent.error,
1873 : };
1874 0 : metric.observe(elapsed);
1875 0 : }
1876 : }
1877 :
1878 0 : pub(crate) static LIVE_CONNECTIONS: Lazy<IntCounterPairVec> = Lazy::new(|| {
1879 0 : register_int_counter_pair_vec!(
1880 0 : "pageserver_live_connections_started",
1881 0 : "Number of network connections that we started handling",
1882 0 : "pageserver_live_connections_finished",
1883 0 : "Number of network connections that we finished handling",
1884 0 : &["pageserver_connection_kind"]
1885 0 : )
1886 0 : .expect("failed to define a metric")
1887 0 : });
1888 :
1889 : #[derive(Clone, Copy, enum_map::Enum, IntoStaticStr)]
1890 : pub(crate) enum ComputeCommandKind {
1891 : PageStreamV3,
1892 : PageStreamV2,
1893 : Basebackup,
1894 : Fullbackup,
1895 : LeaseLsn,
1896 : }
1897 :
1898 : pub(crate) struct ComputeCommandCounters {
1899 : map: EnumMap<ComputeCommandKind, IntCounter>,
1900 : }
1901 :
1902 0 : pub(crate) static COMPUTE_COMMANDS_COUNTERS: Lazy<ComputeCommandCounters> = Lazy::new(|| {
1903 0 : let inner = register_int_counter_vec!(
1904 0 : "pageserver_compute_commands",
1905 0 : "Number of compute -> pageserver commands processed",
1906 0 : &["command"]
1907 0 : )
1908 0 : .expect("failed to define a metric");
1909 0 :
1910 0 : ComputeCommandCounters {
1911 0 : map: EnumMap::from_array(std::array::from_fn(|i| {
1912 0 : let command = ComputeCommandKind::from_usize(i);
1913 0 : let command_str: &'static str = command.into();
1914 0 : inner.with_label_values(&[command_str])
1915 0 : })),
1916 0 : }
1917 0 : });
1918 :
1919 : impl ComputeCommandCounters {
1920 0 : pub(crate) fn for_command(&self, command: ComputeCommandKind) -> &IntCounter {
1921 0 : &self.map[command]
1922 0 : }
1923 : }
1924 :
1925 : // remote storage metrics
1926 :
1927 396 : static REMOTE_TIMELINE_CLIENT_CALLS: Lazy<IntCounterPairVec> = Lazy::new(|| {
1928 396 : register_int_counter_pair_vec!(
1929 396 : "pageserver_remote_timeline_client_calls_started",
1930 396 : "Number of started calls to remote timeline client.",
1931 396 : "pageserver_remote_timeline_client_calls_finished",
1932 396 : "Number of finshed calls to remote timeline client.",
1933 396 : &[
1934 396 : "tenant_id",
1935 396 : "shard_id",
1936 396 : "timeline_id",
1937 396 : "file_kind",
1938 396 : "op_kind"
1939 396 : ],
1940 396 : )
1941 396 : .unwrap()
1942 396 : });
1943 :
1944 : static REMOTE_TIMELINE_CLIENT_BYTES_STARTED_COUNTER: Lazy<IntCounterVec> =
1945 392 : Lazy::new(|| {
1946 392 : register_int_counter_vec!(
1947 392 : "pageserver_remote_timeline_client_bytes_started",
1948 392 : "Incremented by the number of bytes associated with a remote timeline client operation. \
1949 392 : The increment happens when the operation is scheduled.",
1950 392 : &["tenant_id", "shard_id", "timeline_id", "file_kind", "op_kind"],
1951 392 : )
1952 392 : .expect("failed to define a metric")
1953 392 : });
1954 :
1955 392 : static REMOTE_TIMELINE_CLIENT_BYTES_FINISHED_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| {
1956 392 : register_int_counter_vec!(
1957 392 : "pageserver_remote_timeline_client_bytes_finished",
1958 392 : "Incremented by the number of bytes associated with a remote timeline client operation. \
1959 392 : The increment happens when the operation finishes (regardless of success/failure/shutdown).",
1960 392 : &["tenant_id", "shard_id", "timeline_id", "file_kind", "op_kind"],
1961 392 : )
1962 392 : .expect("failed to define a metric")
1963 392 : });
1964 :
1965 : pub(crate) struct TenantManagerMetrics {
1966 : tenant_slots_attached: UIntGauge,
1967 : tenant_slots_secondary: UIntGauge,
1968 : tenant_slots_inprogress: UIntGauge,
1969 : pub(crate) tenant_slot_writes: IntCounter,
1970 : pub(crate) unexpected_errors: IntCounter,
1971 : }
1972 :
1973 : impl TenantManagerMetrics {
1974 : /// Helpers for tracking slots. Note that these do not track the lifetime of TenantSlot objects
1975 : /// exactly: they track the lifetime of the slots _in the tenant map_.
1976 4 : pub(crate) fn slot_inserted(&self, slot: &TenantSlot) {
1977 4 : match slot {
1978 0 : TenantSlot::Attached(_) => {
1979 0 : self.tenant_slots_attached.inc();
1980 0 : }
1981 0 : TenantSlot::Secondary(_) => {
1982 0 : self.tenant_slots_secondary.inc();
1983 0 : }
1984 4 : TenantSlot::InProgress(_) => {
1985 4 : self.tenant_slots_inprogress.inc();
1986 4 : }
1987 : }
1988 4 : }
1989 :
1990 4 : pub(crate) fn slot_removed(&self, slot: &TenantSlot) {
1991 4 : match slot {
1992 4 : TenantSlot::Attached(_) => {
1993 4 : self.tenant_slots_attached.dec();
1994 4 : }
1995 0 : TenantSlot::Secondary(_) => {
1996 0 : self.tenant_slots_secondary.dec();
1997 0 : }
1998 0 : TenantSlot::InProgress(_) => {
1999 0 : self.tenant_slots_inprogress.dec();
2000 0 : }
2001 : }
2002 4 : }
2003 :
2004 : #[cfg(all(debug_assertions, not(test)))]
2005 0 : pub(crate) fn slots_total(&self) -> u64 {
2006 0 : self.tenant_slots_attached.get()
2007 0 : + self.tenant_slots_secondary.get()
2008 0 : + self.tenant_slots_inprogress.get()
2009 0 : }
2010 : }
2011 :
2012 4 : pub(crate) static TENANT_MANAGER: Lazy<TenantManagerMetrics> = Lazy::new(|| {
2013 4 : let tenant_slots = register_uint_gauge_vec!(
2014 4 : "pageserver_tenant_manager_slots",
2015 4 : "How many slots currently exist, including all attached, secondary and in-progress operations",
2016 4 : &["mode"]
2017 4 : )
2018 4 : .expect("failed to define a metric");
2019 4 : TenantManagerMetrics {
2020 4 : tenant_slots_attached: tenant_slots
2021 4 : .get_metric_with_label_values(&["attached"])
2022 4 : .unwrap(),
2023 4 : tenant_slots_secondary: tenant_slots
2024 4 : .get_metric_with_label_values(&["secondary"])
2025 4 : .unwrap(),
2026 4 : tenant_slots_inprogress: tenant_slots
2027 4 : .get_metric_with_label_values(&["inprogress"])
2028 4 : .unwrap(),
2029 4 : tenant_slot_writes: register_int_counter!(
2030 4 : "pageserver_tenant_manager_slot_writes",
2031 4 : "Writes to a tenant slot, including all of create/attach/detach/delete"
2032 4 : )
2033 4 : .expect("failed to define a metric"),
2034 4 : unexpected_errors: register_int_counter!(
2035 4 : "pageserver_tenant_manager_unexpected_errors_total",
2036 4 : "Number of unexpected conditions encountered: nonzero value indicates a non-fatal bug."
2037 4 : )
2038 4 : .expect("failed to define a metric"),
2039 4 : }
2040 4 : });
2041 :
2042 : pub(crate) struct DeletionQueueMetrics {
2043 : pub(crate) keys_submitted: IntCounter,
2044 : pub(crate) keys_dropped: IntCounter,
2045 : pub(crate) keys_executed: IntCounter,
2046 : pub(crate) keys_validated: IntCounter,
2047 : pub(crate) dropped_lsn_updates: IntCounter,
2048 : pub(crate) unexpected_errors: IntCounter,
2049 : pub(crate) remote_errors: IntCounterVec,
2050 : }
2051 66 : pub(crate) static DELETION_QUEUE: Lazy<DeletionQueueMetrics> = Lazy::new(|| {
2052 66 : DeletionQueueMetrics{
2053 66 :
2054 66 : keys_submitted: register_int_counter!(
2055 66 : "pageserver_deletion_queue_submitted_total",
2056 66 : "Number of objects submitted for deletion"
2057 66 : )
2058 66 : .expect("failed to define a metric"),
2059 66 :
2060 66 : keys_dropped: register_int_counter!(
2061 66 : "pageserver_deletion_queue_dropped_total",
2062 66 : "Number of object deletions dropped due to stale generation."
2063 66 : )
2064 66 : .expect("failed to define a metric"),
2065 66 :
2066 66 : keys_executed: register_int_counter!(
2067 66 : "pageserver_deletion_queue_executed_total",
2068 66 : "Number of objects deleted. Only includes objects that we actually deleted, sum with pageserver_deletion_queue_dropped_total for the total number of keys processed to completion"
2069 66 : )
2070 66 : .expect("failed to define a metric"),
2071 66 :
2072 66 : keys_validated: register_int_counter!(
2073 66 : "pageserver_deletion_queue_validated_total",
2074 66 : "Number of keys validated for deletion. Sum with pageserver_deletion_queue_dropped_total for the total number of keys that have passed through the validation stage."
2075 66 : )
2076 66 : .expect("failed to define a metric"),
2077 66 :
2078 66 : dropped_lsn_updates: register_int_counter!(
2079 66 : "pageserver_deletion_queue_dropped_lsn_updates_total",
2080 66 : "Updates to remote_consistent_lsn dropped due to stale generation number."
2081 66 : )
2082 66 : .expect("failed to define a metric"),
2083 66 : unexpected_errors: register_int_counter!(
2084 66 : "pageserver_deletion_queue_unexpected_errors_total",
2085 66 : "Number of unexpected condiions that may stall the queue: any value above zero is unexpected."
2086 66 : )
2087 66 : .expect("failed to define a metric"),
2088 66 : remote_errors: register_int_counter_vec!(
2089 66 : "pageserver_deletion_queue_remote_errors_total",
2090 66 : "Retryable remote I/O errors while executing deletions, for example 503 responses to DeleteObjects",
2091 66 : &["op_kind"],
2092 66 : )
2093 66 : .expect("failed to define a metric")
2094 66 : }
2095 66 : });
2096 :
2097 : pub(crate) struct SecondaryModeMetrics {
2098 : pub(crate) upload_heatmap: IntCounter,
2099 : pub(crate) upload_heatmap_errors: IntCounter,
2100 : pub(crate) upload_heatmap_duration: Histogram,
2101 : pub(crate) download_heatmap: IntCounter,
2102 : pub(crate) download_layer: IntCounter,
2103 : }
2104 0 : pub(crate) static SECONDARY_MODE: Lazy<SecondaryModeMetrics> = Lazy::new(|| {
2105 0 : SecondaryModeMetrics {
2106 0 : upload_heatmap: register_int_counter!(
2107 0 : "pageserver_secondary_upload_heatmap",
2108 0 : "Number of heatmaps written to remote storage by attached tenants"
2109 0 : )
2110 0 : .expect("failed to define a metric"),
2111 0 : upload_heatmap_errors: register_int_counter!(
2112 0 : "pageserver_secondary_upload_heatmap_errors",
2113 0 : "Failures writing heatmap to remote storage"
2114 0 : )
2115 0 : .expect("failed to define a metric"),
2116 0 : upload_heatmap_duration: register_histogram!(
2117 0 : "pageserver_secondary_upload_heatmap_duration",
2118 0 : "Time to build and upload a heatmap, including any waiting inside the remote storage client"
2119 0 : )
2120 0 : .expect("failed to define a metric"),
2121 0 : download_heatmap: register_int_counter!(
2122 0 : "pageserver_secondary_download_heatmap",
2123 0 : "Number of downloads of heatmaps by secondary mode locations, including when it hasn't changed"
2124 0 : )
2125 0 : .expect("failed to define a metric"),
2126 0 : download_layer: register_int_counter!(
2127 0 : "pageserver_secondary_download_layer",
2128 0 : "Number of downloads of layers by secondary mode locations"
2129 0 : )
2130 0 : .expect("failed to define a metric"),
2131 0 : }
2132 0 : });
2133 :
2134 0 : pub(crate) static SECONDARY_RESIDENT_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
2135 0 : register_uint_gauge_vec!(
2136 0 : "pageserver_secondary_resident_physical_size",
2137 0 : "The size of the layer files present in the pageserver's filesystem, for secondary locations.",
2138 0 : &["tenant_id", "shard_id"]
2139 0 : )
2140 0 : .expect("failed to define a metric")
2141 0 : });
2142 :
2143 0 : pub(crate) static NODE_UTILIZATION_SCORE: Lazy<UIntGauge> = Lazy::new(|| {
2144 0 : register_uint_gauge!(
2145 0 : "pageserver_utilization_score",
2146 0 : "The utilization score we report to the storage controller for scheduling, where 0 is empty, 1000000 is full, and anything above is considered overloaded",
2147 0 : )
2148 0 : .expect("failed to define a metric")
2149 0 : });
2150 :
2151 0 : pub(crate) static SECONDARY_HEATMAP_TOTAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
2152 0 : register_uint_gauge_vec!(
2153 0 : "pageserver_secondary_heatmap_total_size",
2154 0 : "The total size in bytes of all layers in the most recently downloaded heatmap.",
2155 0 : &["tenant_id", "shard_id"]
2156 0 : )
2157 0 : .expect("failed to define a metric")
2158 0 : });
2159 :
2160 : #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2161 : pub enum RemoteOpKind {
2162 : Upload,
2163 : Download,
2164 : Delete,
2165 : }
2166 : impl RemoteOpKind {
2167 30306 : pub fn as_str(&self) -> &'static str {
2168 30306 : match self {
2169 28568 : Self::Upload => "upload",
2170 136 : Self::Download => "download",
2171 1602 : Self::Delete => "delete",
2172 : }
2173 30306 : }
2174 : }
2175 :
2176 : #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
2177 : pub enum RemoteOpFileKind {
2178 : Layer,
2179 : Index,
2180 : }
2181 : impl RemoteOpFileKind {
2182 30306 : pub fn as_str(&self) -> &'static str {
2183 30306 : match self {
2184 21119 : Self::Layer => "layer",
2185 9187 : Self::Index => "index",
2186 : }
2187 30306 : }
2188 : }
2189 :
2190 392 : pub(crate) static REMOTE_OPERATION_TIME: Lazy<HistogramVec> = Lazy::new(|| {
2191 392 : register_histogram_vec!(
2192 392 : "pageserver_remote_operation_seconds",
2193 392 : "Time spent on remote storage operations. \
2194 392 : Grouped by tenant, timeline, operation_kind and status. \
2195 392 : Does not account for time spent waiting in remote timeline client's queues.",
2196 392 : &["file_kind", "op_kind", "status"]
2197 392 : )
2198 392 : .expect("failed to define a metric")
2199 392 : });
2200 :
2201 0 : pub(crate) static TENANT_TASK_EVENTS: Lazy<IntCounterVec> = Lazy::new(|| {
2202 0 : register_int_counter_vec!(
2203 0 : "pageserver_tenant_task_events",
2204 0 : "Number of task start/stop/fail events.",
2205 0 : &["event"],
2206 0 : )
2207 0 : .expect("Failed to register tenant_task_events metric")
2208 0 : });
2209 :
2210 : pub struct BackgroundLoopSemaphoreMetrics {
2211 : counters: EnumMap<BackgroundLoopKind, IntCounterPair>,
2212 : durations: EnumMap<BackgroundLoopKind, Histogram>,
2213 : waiting_tasks: EnumMap<BackgroundLoopKind, IntGauge>,
2214 : running_tasks: EnumMap<BackgroundLoopKind, IntGauge>,
2215 : }
2216 :
2217 : pub(crate) static BACKGROUND_LOOP_SEMAPHORE: Lazy<BackgroundLoopSemaphoreMetrics> =
2218 40 : Lazy::new(|| {
2219 40 : let counters = register_int_counter_pair_vec!(
2220 40 : "pageserver_background_loop_semaphore_wait_start_count",
2221 40 : "Counter for background loop concurrency-limiting semaphore acquire calls started",
2222 40 : "pageserver_background_loop_semaphore_wait_finish_count",
2223 40 : "Counter for background loop concurrency-limiting semaphore acquire calls finished",
2224 40 : &["task"],
2225 40 : )
2226 40 : .unwrap();
2227 40 :
2228 40 : let durations = register_histogram_vec!(
2229 40 : "pageserver_background_loop_semaphore_wait_seconds",
2230 40 : "Seconds spent waiting on background loop semaphore acquisition",
2231 40 : &["task"],
2232 40 : vec![0.01, 1.0, 5.0, 10.0, 30.0, 60.0, 180.0, 300.0, 600.0],
2233 40 : )
2234 40 : .unwrap();
2235 40 :
2236 40 : let waiting_tasks = register_int_gauge_vec!(
2237 40 : "pageserver_background_loop_semaphore_waiting_tasks",
2238 40 : "Number of background loop tasks waiting for semaphore",
2239 40 : &["task"],
2240 40 : )
2241 40 : .unwrap();
2242 40 :
2243 40 : let running_tasks = register_int_gauge_vec!(
2244 40 : "pageserver_background_loop_semaphore_running_tasks",
2245 40 : "Number of background loop tasks running concurrently",
2246 40 : &["task"],
2247 40 : )
2248 40 : .unwrap();
2249 40 :
2250 40 : BackgroundLoopSemaphoreMetrics {
2251 400 : counters: EnumMap::from_array(std::array::from_fn(|i| {
2252 400 : let kind = BackgroundLoopKind::from_usize(i);
2253 400 : counters.with_label_values(&[kind.into()])
2254 400 : })),
2255 400 : durations: EnumMap::from_array(std::array::from_fn(|i| {
2256 400 : let kind = BackgroundLoopKind::from_usize(i);
2257 400 : durations.with_label_values(&[kind.into()])
2258 400 : })),
2259 400 : waiting_tasks: EnumMap::from_array(std::array::from_fn(|i| {
2260 400 : let kind = BackgroundLoopKind::from_usize(i);
2261 400 : waiting_tasks.with_label_values(&[kind.into()])
2262 400 : })),
2263 400 : running_tasks: EnumMap::from_array(std::array::from_fn(|i| {
2264 400 : let kind = BackgroundLoopKind::from_usize(i);
2265 400 : running_tasks.with_label_values(&[kind.into()])
2266 400 : })),
2267 40 : }
2268 40 : });
2269 :
2270 : impl BackgroundLoopSemaphoreMetrics {
2271 : /// Starts recording semaphore metrics. Call `acquired()` on the returned recorder when the
2272 : /// semaphore is acquired, and drop it when the task completes or is cancelled.
2273 768 : pub(crate) fn record(
2274 768 : &self,
2275 768 : task: BackgroundLoopKind,
2276 768 : ) -> BackgroundLoopSemaphoreMetricsRecorder {
2277 768 : BackgroundLoopSemaphoreMetricsRecorder::start(self, task)
2278 768 : }
2279 : }
2280 :
2281 : /// Records metrics for a background task.
2282 : pub struct BackgroundLoopSemaphoreMetricsRecorder<'a> {
2283 : metrics: &'a BackgroundLoopSemaphoreMetrics,
2284 : task: BackgroundLoopKind,
2285 : start: Instant,
2286 : wait_counter_guard: Option<metrics::IntCounterPairGuard>,
2287 : }
2288 :
2289 : impl<'a> BackgroundLoopSemaphoreMetricsRecorder<'a> {
2290 : /// Starts recording semaphore metrics, by recording wait time and incrementing
2291 : /// `wait_start_count` and `waiting_tasks`.
2292 768 : fn start(metrics: &'a BackgroundLoopSemaphoreMetrics, task: BackgroundLoopKind) -> Self {
2293 768 : metrics.waiting_tasks[task].inc();
2294 768 : Self {
2295 768 : metrics,
2296 768 : task,
2297 768 : start: Instant::now(),
2298 768 : wait_counter_guard: Some(metrics.counters[task].guard()),
2299 768 : }
2300 768 : }
2301 :
2302 : /// Signals that the semaphore has been acquired, and updates relevant metrics.
2303 768 : pub fn acquired(&mut self) -> Duration {
2304 768 : let waited = self.start.elapsed();
2305 768 : self.wait_counter_guard.take().expect("already acquired");
2306 768 : self.metrics.durations[self.task].observe(waited.as_secs_f64());
2307 768 : self.metrics.waiting_tasks[self.task].dec();
2308 768 : self.metrics.running_tasks[self.task].inc();
2309 768 : waited
2310 768 : }
2311 : }
2312 :
2313 : impl Drop for BackgroundLoopSemaphoreMetricsRecorder<'_> {
2314 : /// The task either completed or was cancelled.
2315 768 : fn drop(&mut self) {
2316 768 : if self.wait_counter_guard.take().is_some() {
2317 0 : // Waiting.
2318 0 : self.metrics.durations[self.task].observe(self.start.elapsed().as_secs_f64());
2319 0 : self.metrics.waiting_tasks[self.task].dec();
2320 768 : } else {
2321 768 : // Running.
2322 768 : self.metrics.running_tasks[self.task].dec();
2323 768 : }
2324 768 : }
2325 : }
2326 :
2327 0 : pub(crate) static BACKGROUND_LOOP_PERIOD_OVERRUN_COUNT: Lazy<IntCounterVec> = Lazy::new(|| {
2328 0 : register_int_counter_vec!(
2329 0 : "pageserver_background_loop_period_overrun_count",
2330 0 : "Incremented whenever warn_when_period_overrun() logs a warning.",
2331 0 : &["task", "period"],
2332 0 : )
2333 0 : .expect("failed to define a metric")
2334 0 : });
2335 :
2336 : // walreceiver metrics
2337 :
2338 0 : pub(crate) static WALRECEIVER_STARTED_CONNECTIONS: Lazy<IntCounter> = Lazy::new(|| {
2339 0 : register_int_counter!(
2340 0 : "pageserver_walreceiver_started_connections_total",
2341 0 : "Number of started walreceiver connections"
2342 0 : )
2343 0 : .expect("failed to define a metric")
2344 0 : });
2345 :
2346 0 : pub(crate) static WALRECEIVER_ACTIVE_MANAGERS: Lazy<IntGauge> = Lazy::new(|| {
2347 0 : register_int_gauge!(
2348 0 : "pageserver_walreceiver_active_managers",
2349 0 : "Number of active walreceiver managers"
2350 0 : )
2351 0 : .expect("failed to define a metric")
2352 0 : });
2353 :
2354 0 : pub(crate) static WALRECEIVER_SWITCHES: Lazy<IntCounterVec> = Lazy::new(|| {
2355 0 : register_int_counter_vec!(
2356 0 : "pageserver_walreceiver_switches_total",
2357 0 : "Number of walreceiver manager change_connection calls",
2358 0 : &["reason"]
2359 0 : )
2360 0 : .expect("failed to define a metric")
2361 0 : });
2362 :
2363 0 : pub(crate) static WALRECEIVER_BROKER_UPDATES: Lazy<IntCounter> = Lazy::new(|| {
2364 0 : register_int_counter!(
2365 0 : "pageserver_walreceiver_broker_updates_total",
2366 0 : "Number of received broker updates in walreceiver"
2367 0 : )
2368 0 : .expect("failed to define a metric")
2369 0 : });
2370 :
2371 4 : pub(crate) static WALRECEIVER_CANDIDATES_EVENTS: Lazy<IntCounterVec> = Lazy::new(|| {
2372 4 : register_int_counter_vec!(
2373 4 : "pageserver_walreceiver_candidates_events_total",
2374 4 : "Number of walreceiver candidate events",
2375 4 : &["event"]
2376 4 : )
2377 4 : .expect("failed to define a metric")
2378 4 : });
2379 :
2380 : pub(crate) static WALRECEIVER_CANDIDATES_ADDED: Lazy<IntCounter> =
2381 0 : Lazy::new(|| WALRECEIVER_CANDIDATES_EVENTS.with_label_values(&["add"]));
2382 :
2383 : pub(crate) static WALRECEIVER_CANDIDATES_REMOVED: Lazy<IntCounter> =
2384 4 : Lazy::new(|| WALRECEIVER_CANDIDATES_EVENTS.with_label_values(&["remove"]));
2385 :
2386 : // Metrics collected on WAL redo operations
2387 : //
2388 : // We collect the time spent in actual WAL redo ('redo'), and time waiting
2389 : // for access to the postgres process ('wait') since there is only one for
2390 : // each tenant.
2391 :
2392 : /// Time buckets are small because we want to be able to measure the
2393 : /// smallest redo processing times. These buckets allow us to measure down
2394 : /// to 5us, which equates to 200'000 pages/sec, which equates to 1.6GB/sec.
2395 : /// This is much better than the previous 5ms aka 200 pages/sec aka 1.6MB/sec.
2396 : ///
2397 : /// Values up to 1s are recorded because metrics show that we have redo
2398 : /// durations and lock times larger than 0.250s.
2399 : macro_rules! redo_histogram_time_buckets {
2400 : () => {
2401 : vec![
2402 : 0.000_005, 0.000_010, 0.000_025, 0.000_050, 0.000_100, 0.000_250, 0.000_500, 0.001_000,
2403 : 0.002_500, 0.005_000, 0.010_000, 0.025_000, 0.050_000, 0.100_000, 0.250_000, 0.500_000,
2404 : 1.000_000,
2405 : ]
2406 : };
2407 : }
2408 :
2409 : /// While we're at it, also measure the amount of records replayed in each
2410 : /// operation. We have a global 'total replayed' counter, but that's not
2411 : /// as useful as 'what is the skew for how many records we replay in one
2412 : /// operation'.
2413 : macro_rules! redo_histogram_count_buckets {
2414 : () => {
2415 : vec![0.0, 1.0, 2.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0]
2416 : };
2417 : }
2418 :
2419 : macro_rules! redo_bytes_histogram_count_buckets {
2420 : () => {
2421 : // powers of (2^.5), from 2^4.5 to 2^15 (22 buckets)
2422 : // rounded up to the next multiple of 8 to capture any MAXALIGNed record of that size, too.
2423 : vec![
2424 : 24.0, 32.0, 48.0, 64.0, 96.0, 128.0, 184.0, 256.0, 368.0, 512.0, 728.0, 1024.0, 1456.0,
2425 : 2048.0, 2904.0, 4096.0, 5800.0, 8192.0, 11592.0, 16384.0, 23176.0, 32768.0,
2426 : ]
2427 : };
2428 : }
2429 :
2430 : pub(crate) struct WalIngestMetrics {
2431 : pub(crate) bytes_received: IntCounter,
2432 : pub(crate) records_received: IntCounter,
2433 : pub(crate) records_observed: IntCounter,
2434 : pub(crate) records_committed: IntCounter,
2435 : pub(crate) records_filtered: IntCounter,
2436 : pub(crate) values_committed_metadata_images: IntCounter,
2437 : pub(crate) values_committed_metadata_deltas: IntCounter,
2438 : pub(crate) values_committed_data_images: IntCounter,
2439 : pub(crate) values_committed_data_deltas: IntCounter,
2440 : pub(crate) gap_blocks_zeroed_on_rel_extend: IntCounter,
2441 : }
2442 :
2443 : impl WalIngestMetrics {
2444 0 : pub(crate) fn inc_values_committed(&self, stats: &DatadirModificationStats) {
2445 0 : if stats.metadata_images > 0 {
2446 0 : self.values_committed_metadata_images
2447 0 : .inc_by(stats.metadata_images);
2448 0 : }
2449 0 : if stats.metadata_deltas > 0 {
2450 0 : self.values_committed_metadata_deltas
2451 0 : .inc_by(stats.metadata_deltas);
2452 0 : }
2453 0 : if stats.data_images > 0 {
2454 0 : self.values_committed_data_images.inc_by(stats.data_images);
2455 0 : }
2456 0 : if stats.data_deltas > 0 {
2457 0 : self.values_committed_data_deltas.inc_by(stats.data_deltas);
2458 0 : }
2459 0 : }
2460 : }
2461 :
2462 20 : pub(crate) static WAL_INGEST: Lazy<WalIngestMetrics> = Lazy::new(|| {
2463 20 : let values_committed = register_int_counter_vec!(
2464 20 : "pageserver_wal_ingest_values_committed",
2465 20 : "Number of values committed to pageserver storage from WAL records",
2466 20 : &["class", "kind"],
2467 20 : )
2468 20 : .expect("failed to define a metric");
2469 20 :
2470 20 : WalIngestMetrics {
2471 20 : bytes_received: register_int_counter!(
2472 20 : "pageserver_wal_ingest_bytes_received",
2473 20 : "Bytes of WAL ingested from safekeepers",
2474 20 : )
2475 20 : .unwrap(),
2476 20 : records_received: register_int_counter!(
2477 20 : "pageserver_wal_ingest_records_received",
2478 20 : "Number of WAL records received from safekeepers"
2479 20 : )
2480 20 : .expect("failed to define a metric"),
2481 20 : records_observed: register_int_counter!(
2482 20 : "pageserver_wal_ingest_records_observed",
2483 20 : "Number of WAL records observed from safekeepers. These are metadata only records for shard 0."
2484 20 : )
2485 20 : .expect("failed to define a metric"),
2486 20 : records_committed: register_int_counter!(
2487 20 : "pageserver_wal_ingest_records_committed",
2488 20 : "Number of WAL records which resulted in writes to pageserver storage"
2489 20 : )
2490 20 : .expect("failed to define a metric"),
2491 20 : records_filtered: register_int_counter!(
2492 20 : "pageserver_wal_ingest_records_filtered",
2493 20 : "Number of WAL records filtered out due to sharding"
2494 20 : )
2495 20 : .expect("failed to define a metric"),
2496 20 : values_committed_metadata_images: values_committed.with_label_values(&["metadata", "image"]),
2497 20 : values_committed_metadata_deltas: values_committed.with_label_values(&["metadata", "delta"]),
2498 20 : values_committed_data_images: values_committed.with_label_values(&["data", "image"]),
2499 20 : values_committed_data_deltas: values_committed.with_label_values(&["data", "delta"]),
2500 20 : gap_blocks_zeroed_on_rel_extend: register_int_counter!(
2501 20 : "pageserver_gap_blocks_zeroed_on_rel_extend",
2502 20 : "Total number of zero gap blocks written on relation extends"
2503 20 : )
2504 20 : .expect("failed to define a metric"),
2505 20 : }
2506 20 : });
2507 :
2508 404 : pub(crate) static PAGESERVER_TIMELINE_WAL_RECORDS_RECEIVED: Lazy<IntCounterVec> = Lazy::new(|| {
2509 404 : register_int_counter_vec!(
2510 404 : "pageserver_timeline_wal_records_received",
2511 404 : "Number of WAL records received per shard",
2512 404 : &["tenant_id", "shard_id", "timeline_id"]
2513 404 : )
2514 404 : .expect("failed to define a metric")
2515 404 : });
2516 :
2517 12 : pub(crate) static WAL_REDO_TIME: Lazy<Histogram> = Lazy::new(|| {
2518 12 : register_histogram!(
2519 12 : "pageserver_wal_redo_seconds",
2520 12 : "Time spent on WAL redo",
2521 12 : redo_histogram_time_buckets!()
2522 12 : )
2523 12 : .expect("failed to define a metric")
2524 12 : });
2525 :
2526 12 : pub(crate) static WAL_REDO_RECORDS_HISTOGRAM: Lazy<Histogram> = Lazy::new(|| {
2527 12 : register_histogram!(
2528 12 : "pageserver_wal_redo_records_histogram",
2529 12 : "Histogram of number of records replayed per redo in the Postgres WAL redo process",
2530 12 : redo_histogram_count_buckets!(),
2531 12 : )
2532 12 : .expect("failed to define a metric")
2533 12 : });
2534 :
2535 12 : pub(crate) static WAL_REDO_BYTES_HISTOGRAM: Lazy<Histogram> = Lazy::new(|| {
2536 12 : register_histogram!(
2537 12 : "pageserver_wal_redo_bytes_histogram",
2538 12 : "Histogram of number of records replayed per redo sent to Postgres",
2539 12 : redo_bytes_histogram_count_buckets!(),
2540 12 : )
2541 12 : .expect("failed to define a metric")
2542 12 : });
2543 :
2544 : // FIXME: isn't this already included by WAL_REDO_RECORDS_HISTOGRAM which has _count?
2545 12 : pub(crate) static WAL_REDO_RECORD_COUNTER: Lazy<IntCounter> = Lazy::new(|| {
2546 12 : register_int_counter!(
2547 12 : "pageserver_replayed_wal_records_total",
2548 12 : "Number of WAL records replayed in WAL redo process"
2549 12 : )
2550 12 : .unwrap()
2551 12 : });
2552 :
2553 : #[rustfmt::skip]
2554 16 : pub(crate) static WAL_REDO_PROCESS_LAUNCH_DURATION_HISTOGRAM: Lazy<Histogram> = Lazy::new(|| {
2555 16 : register_histogram!(
2556 16 : "pageserver_wal_redo_process_launch_duration",
2557 16 : "Histogram of the duration of successful WalRedoProcess::launch calls",
2558 16 : vec![
2559 16 : 0.0002, 0.0004, 0.0006, 0.0008, 0.0010,
2560 16 : 0.0020, 0.0040, 0.0060, 0.0080, 0.0100,
2561 16 : 0.0200, 0.0400, 0.0600, 0.0800, 0.1000,
2562 16 : 0.2000, 0.4000, 0.6000, 0.8000, 1.0000,
2563 16 : 1.5000, 2.0000, 2.5000, 3.0000, 4.0000, 10.0000
2564 16 : ],
2565 16 : )
2566 16 : .expect("failed to define a metric")
2567 16 : });
2568 :
2569 : pub(crate) struct WalRedoProcessCounters {
2570 : pub(crate) started: IntCounter,
2571 : pub(crate) killed_by_cause: EnumMap<WalRedoKillCause, IntCounter>,
2572 : pub(crate) active_stderr_logger_tasks_started: IntCounter,
2573 : pub(crate) active_stderr_logger_tasks_finished: IntCounter,
2574 : }
2575 :
2576 : #[derive(Debug, enum_map::Enum, strum_macros::IntoStaticStr)]
2577 : pub(crate) enum WalRedoKillCause {
2578 : WalRedoProcessDrop,
2579 : NoLeakChildDrop,
2580 : Startup,
2581 : }
2582 :
2583 : impl Default for WalRedoProcessCounters {
2584 16 : fn default() -> Self {
2585 16 : let started = register_int_counter!(
2586 16 : "pageserver_wal_redo_process_started_total",
2587 16 : "Number of WAL redo processes started",
2588 16 : )
2589 16 : .unwrap();
2590 16 :
2591 16 : let killed = register_int_counter_vec!(
2592 16 : "pageserver_wal_redo_process_stopped_total",
2593 16 : "Number of WAL redo processes stopped",
2594 16 : &["cause"],
2595 16 : )
2596 16 : .unwrap();
2597 16 :
2598 16 : let active_stderr_logger_tasks_started = register_int_counter!(
2599 16 : "pageserver_walredo_stderr_logger_tasks_started_total",
2600 16 : "Number of active walredo stderr logger tasks that have started",
2601 16 : )
2602 16 : .unwrap();
2603 16 :
2604 16 : let active_stderr_logger_tasks_finished = register_int_counter!(
2605 16 : "pageserver_walredo_stderr_logger_tasks_finished_total",
2606 16 : "Number of active walredo stderr logger tasks that have finished",
2607 16 : )
2608 16 : .unwrap();
2609 16 :
2610 16 : Self {
2611 16 : started,
2612 48 : killed_by_cause: EnumMap::from_array(std::array::from_fn(|i| {
2613 48 : let cause = WalRedoKillCause::from_usize(i);
2614 48 : let cause_str: &'static str = cause.into();
2615 48 : killed.with_label_values(&[cause_str])
2616 48 : })),
2617 16 : active_stderr_logger_tasks_started,
2618 16 : active_stderr_logger_tasks_finished,
2619 16 : }
2620 16 : }
2621 : }
2622 :
2623 : pub(crate) static WAL_REDO_PROCESS_COUNTERS: Lazy<WalRedoProcessCounters> =
2624 : Lazy::new(WalRedoProcessCounters::default);
2625 :
2626 : /// Similar to `prometheus::HistogramTimer` but does not record on drop.
2627 : pub(crate) struct StorageTimeMetricsTimer {
2628 : metrics: StorageTimeMetrics,
2629 : start: Instant,
2630 : }
2631 :
2632 : impl StorageTimeMetricsTimer {
2633 4324 : fn new(metrics: StorageTimeMetrics) -> Self {
2634 4324 : Self {
2635 4324 : metrics,
2636 4324 : start: Instant::now(),
2637 4324 : }
2638 4324 : }
2639 :
2640 : /// Returns the elapsed duration of the timer.
2641 4324 : pub fn elapsed(&self) -> Duration {
2642 4324 : self.start.elapsed()
2643 4324 : }
2644 :
2645 : /// Record the time from creation to now and return it.
2646 4324 : pub fn stop_and_record(self) -> Duration {
2647 4324 : let duration = self.elapsed();
2648 4324 : let seconds = duration.as_secs_f64();
2649 4324 : self.metrics.timeline_sum.inc_by(seconds);
2650 4324 : self.metrics.timeline_count.inc();
2651 4324 : self.metrics.global_histogram.observe(seconds);
2652 4324 : duration
2653 4324 : }
2654 :
2655 : /// Turns this timer into a timer, which will always record -- usually this means recording
2656 : /// regardless an early `?` path was taken in a function.
2657 8 : pub(crate) fn record_on_drop(self) -> AlwaysRecordingStorageTimeMetricsTimer {
2658 8 : AlwaysRecordingStorageTimeMetricsTimer(Some(self))
2659 8 : }
2660 : }
2661 :
2662 : pub(crate) struct AlwaysRecordingStorageTimeMetricsTimer(Option<StorageTimeMetricsTimer>);
2663 :
2664 : impl Drop for AlwaysRecordingStorageTimeMetricsTimer {
2665 8 : fn drop(&mut self) {
2666 8 : if let Some(inner) = self.0.take() {
2667 8 : inner.stop_and_record();
2668 8 : }
2669 8 : }
2670 : }
2671 :
2672 : impl AlwaysRecordingStorageTimeMetricsTimer {
2673 : /// Returns the elapsed duration of the timer.
2674 0 : pub fn elapsed(&self) -> Duration {
2675 0 : self.0.as_ref().expect("not dropped yet").elapsed()
2676 0 : }
2677 : }
2678 :
2679 : /// Timing facilities for an globally histogrammed metric, which is supported by per tenant and
2680 : /// timeline total sum and count.
2681 : #[derive(Clone, Debug)]
2682 : pub(crate) struct StorageTimeMetrics {
2683 : /// Sum of f64 seconds, per operation, tenant_id and timeline_id
2684 : timeline_sum: Counter,
2685 : /// Number of oeprations, per operation, tenant_id and timeline_id
2686 : timeline_count: IntCounter,
2687 : /// Global histogram having only the "operation" label.
2688 : global_histogram: Histogram,
2689 : }
2690 :
2691 : impl StorageTimeMetrics {
2692 8064 : pub fn new(
2693 8064 : operation: StorageTimeOperation,
2694 8064 : tenant_id: &str,
2695 8064 : shard_id: &str,
2696 8064 : timeline_id: &str,
2697 8064 : ) -> Self {
2698 8064 : let operation: &'static str = operation.into();
2699 8064 :
2700 8064 : let timeline_sum = STORAGE_TIME_SUM_PER_TIMELINE
2701 8064 : .get_metric_with_label_values(&[operation, tenant_id, shard_id, timeline_id])
2702 8064 : .unwrap();
2703 8064 : let timeline_count = STORAGE_TIME_COUNT_PER_TIMELINE
2704 8064 : .get_metric_with_label_values(&[operation, tenant_id, shard_id, timeline_id])
2705 8064 : .unwrap();
2706 8064 : let global_histogram = STORAGE_TIME_GLOBAL
2707 8064 : .get_metric_with_label_values(&[operation])
2708 8064 : .unwrap();
2709 8064 :
2710 8064 : StorageTimeMetrics {
2711 8064 : timeline_sum,
2712 8064 : timeline_count,
2713 8064 : global_histogram,
2714 8064 : }
2715 8064 : }
2716 :
2717 : /// Starts timing a new operation.
2718 : ///
2719 : /// Note: unlike `prometheus::HistogramTimer` the returned timer does not record on drop.
2720 4324 : pub fn start_timer(&self) -> StorageTimeMetricsTimer {
2721 4324 : StorageTimeMetricsTimer::new(self.clone())
2722 4324 : }
2723 : }
2724 :
2725 : #[derive(Debug)]
2726 : pub(crate) struct TimelineMetrics {
2727 : tenant_id: String,
2728 : shard_id: String,
2729 : timeline_id: String,
2730 : pub flush_time_histo: StorageTimeMetrics,
2731 : pub flush_delay_histo: StorageTimeMetrics,
2732 : pub flush_wait_upload_time_gauge: Gauge,
2733 : pub compact_time_histo: StorageTimeMetrics,
2734 : pub create_images_time_histo: StorageTimeMetrics,
2735 : pub logical_size_histo: StorageTimeMetrics,
2736 : pub imitate_logical_size_histo: StorageTimeMetrics,
2737 : pub load_layer_map_histo: StorageTimeMetrics,
2738 : pub garbage_collect_histo: StorageTimeMetrics,
2739 : pub find_gc_cutoffs_histo: StorageTimeMetrics,
2740 : pub last_record_lsn_gauge: IntGauge,
2741 : pub disk_consistent_lsn_gauge: IntGauge,
2742 : pub pitr_history_size: UIntGauge,
2743 : pub archival_size: UIntGauge,
2744 : pub layers_per_read: Histogram,
2745 : pub standby_horizon_gauge: IntGauge,
2746 : pub resident_physical_size_gauge: UIntGauge,
2747 : pub visible_physical_size_gauge: UIntGauge,
2748 : /// copy of LayeredTimeline.current_logical_size
2749 : pub current_logical_size_gauge: UIntGauge,
2750 : pub aux_file_size_gauge: IntGauge,
2751 : pub directory_entries_count_gauge: Lazy<UIntGauge, Box<dyn Send + Fn() -> UIntGauge>>,
2752 : pub evictions: IntCounter,
2753 : pub evictions_with_low_residence_duration: std::sync::RwLock<EvictionsWithLowResidenceDuration>,
2754 : /// Number of valid LSN leases.
2755 : pub valid_lsn_lease_count_gauge: UIntGauge,
2756 : pub wal_records_received: IntCounter,
2757 : shutdown: std::sync::atomic::AtomicBool,
2758 : }
2759 :
2760 : impl TimelineMetrics {
2761 896 : pub fn new(
2762 896 : tenant_shard_id: &TenantShardId,
2763 896 : timeline_id_raw: &TimelineId,
2764 896 : evictions_with_low_residence_duration_builder: EvictionsWithLowResidenceDurationBuilder,
2765 896 : ) -> Self {
2766 896 : let tenant_id = tenant_shard_id.tenant_id.to_string();
2767 896 : let shard_id = format!("{}", tenant_shard_id.shard_slug());
2768 896 : let timeline_id = timeline_id_raw.to_string();
2769 896 : let flush_time_histo = StorageTimeMetrics::new(
2770 896 : StorageTimeOperation::LayerFlush,
2771 896 : &tenant_id,
2772 896 : &shard_id,
2773 896 : &timeline_id,
2774 896 : );
2775 896 : let flush_delay_histo = StorageTimeMetrics::new(
2776 896 : StorageTimeOperation::LayerFlushDelay,
2777 896 : &tenant_id,
2778 896 : &shard_id,
2779 896 : &timeline_id,
2780 896 : );
2781 896 : let flush_wait_upload_time_gauge = FLUSH_WAIT_UPLOAD_TIME
2782 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2783 896 : .unwrap();
2784 896 : let compact_time_histo = StorageTimeMetrics::new(
2785 896 : StorageTimeOperation::Compact,
2786 896 : &tenant_id,
2787 896 : &shard_id,
2788 896 : &timeline_id,
2789 896 : );
2790 896 : let create_images_time_histo = StorageTimeMetrics::new(
2791 896 : StorageTimeOperation::CreateImages,
2792 896 : &tenant_id,
2793 896 : &shard_id,
2794 896 : &timeline_id,
2795 896 : );
2796 896 : let logical_size_histo = StorageTimeMetrics::new(
2797 896 : StorageTimeOperation::LogicalSize,
2798 896 : &tenant_id,
2799 896 : &shard_id,
2800 896 : &timeline_id,
2801 896 : );
2802 896 : let imitate_logical_size_histo = StorageTimeMetrics::new(
2803 896 : StorageTimeOperation::ImitateLogicalSize,
2804 896 : &tenant_id,
2805 896 : &shard_id,
2806 896 : &timeline_id,
2807 896 : );
2808 896 : let load_layer_map_histo = StorageTimeMetrics::new(
2809 896 : StorageTimeOperation::LoadLayerMap,
2810 896 : &tenant_id,
2811 896 : &shard_id,
2812 896 : &timeline_id,
2813 896 : );
2814 896 : let garbage_collect_histo = StorageTimeMetrics::new(
2815 896 : StorageTimeOperation::Gc,
2816 896 : &tenant_id,
2817 896 : &shard_id,
2818 896 : &timeline_id,
2819 896 : );
2820 896 : let find_gc_cutoffs_histo = StorageTimeMetrics::new(
2821 896 : StorageTimeOperation::FindGcCutoffs,
2822 896 : &tenant_id,
2823 896 : &shard_id,
2824 896 : &timeline_id,
2825 896 : );
2826 896 : let last_record_lsn_gauge = LAST_RECORD_LSN
2827 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2828 896 : .unwrap();
2829 896 :
2830 896 : let disk_consistent_lsn_gauge = DISK_CONSISTENT_LSN
2831 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2832 896 : .unwrap();
2833 896 :
2834 896 : let pitr_history_size = PITR_HISTORY_SIZE
2835 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2836 896 : .unwrap();
2837 896 :
2838 896 : let archival_size = TIMELINE_ARCHIVE_SIZE
2839 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2840 896 : .unwrap();
2841 896 :
2842 896 : let layers_per_read = LAYERS_PER_READ
2843 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2844 896 : .unwrap();
2845 896 :
2846 896 : let standby_horizon_gauge = STANDBY_HORIZON
2847 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2848 896 : .unwrap();
2849 896 : let resident_physical_size_gauge = RESIDENT_PHYSICAL_SIZE
2850 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2851 896 : .unwrap();
2852 896 : let visible_physical_size_gauge = VISIBLE_PHYSICAL_SIZE
2853 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2854 896 : .unwrap();
2855 896 : // TODO: we shouldn't expose this metric
2856 896 : let current_logical_size_gauge = CURRENT_LOGICAL_SIZE
2857 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2858 896 : .unwrap();
2859 896 : let aux_file_size_gauge = AUX_FILE_SIZE
2860 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2861 896 : .unwrap();
2862 896 : // TODO use impl Trait syntax here once we have ability to use it: https://github.com/rust-lang/rust/issues/63065
2863 896 : let directory_entries_count_gauge_closure = {
2864 896 : let tenant_shard_id = *tenant_shard_id;
2865 896 : let timeline_id_raw = *timeline_id_raw;
2866 0 : move || {
2867 0 : let tenant_id = tenant_shard_id.tenant_id.to_string();
2868 0 : let shard_id = format!("{}", tenant_shard_id.shard_slug());
2869 0 : let timeline_id = timeline_id_raw.to_string();
2870 0 : let gauge: UIntGauge = DIRECTORY_ENTRIES_COUNT
2871 0 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2872 0 : .unwrap();
2873 0 : gauge
2874 0 : }
2875 : };
2876 896 : let directory_entries_count_gauge: Lazy<UIntGauge, Box<dyn Send + Fn() -> UIntGauge>> =
2877 896 : Lazy::new(Box::new(directory_entries_count_gauge_closure));
2878 896 : let evictions = EVICTIONS
2879 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2880 896 : .unwrap();
2881 896 : let evictions_with_low_residence_duration = evictions_with_low_residence_duration_builder
2882 896 : .build(&tenant_id, &shard_id, &timeline_id);
2883 896 :
2884 896 : let valid_lsn_lease_count_gauge = VALID_LSN_LEASE_COUNT
2885 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2886 896 : .unwrap();
2887 896 :
2888 896 : let wal_records_received = PAGESERVER_TIMELINE_WAL_RECORDS_RECEIVED
2889 896 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2890 896 : .unwrap();
2891 896 :
2892 896 : TimelineMetrics {
2893 896 : tenant_id,
2894 896 : shard_id,
2895 896 : timeline_id,
2896 896 : flush_time_histo,
2897 896 : flush_delay_histo,
2898 896 : flush_wait_upload_time_gauge,
2899 896 : compact_time_histo,
2900 896 : create_images_time_histo,
2901 896 : logical_size_histo,
2902 896 : imitate_logical_size_histo,
2903 896 : garbage_collect_histo,
2904 896 : find_gc_cutoffs_histo,
2905 896 : load_layer_map_histo,
2906 896 : last_record_lsn_gauge,
2907 896 : disk_consistent_lsn_gauge,
2908 896 : pitr_history_size,
2909 896 : archival_size,
2910 896 : layers_per_read,
2911 896 : standby_horizon_gauge,
2912 896 : resident_physical_size_gauge,
2913 896 : visible_physical_size_gauge,
2914 896 : current_logical_size_gauge,
2915 896 : aux_file_size_gauge,
2916 896 : directory_entries_count_gauge,
2917 896 : evictions,
2918 896 : evictions_with_low_residence_duration: std::sync::RwLock::new(
2919 896 : evictions_with_low_residence_duration,
2920 896 : ),
2921 896 : valid_lsn_lease_count_gauge,
2922 896 : wal_records_received,
2923 896 : shutdown: std::sync::atomic::AtomicBool::default(),
2924 896 : }
2925 896 : }
2926 :
2927 3172 : pub(crate) fn record_new_file_metrics(&self, sz: u64) {
2928 3172 : self.resident_physical_size_add(sz);
2929 3172 : }
2930 :
2931 1084 : pub(crate) fn resident_physical_size_sub(&self, sz: u64) {
2932 1084 : self.resident_physical_size_gauge.sub(sz);
2933 1084 : crate::metrics::RESIDENT_PHYSICAL_SIZE_GLOBAL.sub(sz);
2934 1084 : }
2935 :
2936 3444 : pub(crate) fn resident_physical_size_add(&self, sz: u64) {
2937 3444 : self.resident_physical_size_gauge.add(sz);
2938 3444 : crate::metrics::RESIDENT_PHYSICAL_SIZE_GLOBAL.add(sz);
2939 3444 : }
2940 :
2941 20 : pub(crate) fn resident_physical_size_get(&self) -> u64 {
2942 20 : self.resident_physical_size_gauge.get()
2943 20 : }
2944 :
2945 2348 : pub(crate) fn flush_wait_upload_time_gauge_add(&self, duration: f64) {
2946 2348 : self.flush_wait_upload_time_gauge.add(duration);
2947 2348 : crate::metrics::FLUSH_WAIT_UPLOAD_TIME
2948 2348 : .get_metric_with_label_values(&[&self.tenant_id, &self.shard_id, &self.timeline_id])
2949 2348 : .unwrap()
2950 2348 : .add(duration);
2951 2348 : }
2952 :
2953 : /// Generates TIMELINE_LAYER labels for a persistent layer.
2954 5252 : fn make_layer_labels(&self, layer_desc: &PersistentLayerDesc) -> [&str; 5] {
2955 5252 : let level = match LayerMap::is_l0(&layer_desc.key_range, layer_desc.is_delta()) {
2956 2848 : true => LayerLevel::L0,
2957 2404 : false => LayerLevel::L1,
2958 : };
2959 5252 : let kind = match layer_desc.is_delta() {
2960 4398 : true => LayerKind::Delta,
2961 854 : false => LayerKind::Image,
2962 : };
2963 5252 : [
2964 5252 : &self.tenant_id,
2965 5252 : &self.shard_id,
2966 5252 : &self.timeline_id,
2967 5252 : level.into(),
2968 5252 : kind.into(),
2969 5252 : ]
2970 5252 : }
2971 :
2972 : /// Generates TIMELINE_LAYER labels for a frozen ephemeral layer.
2973 4696 : fn make_frozen_layer_labels(&self, _layer: &InMemoryLayer) -> [&str; 5] {
2974 4696 : [
2975 4696 : &self.tenant_id,
2976 4696 : &self.shard_id,
2977 4696 : &self.timeline_id,
2978 4696 : LayerLevel::Frozen.into(),
2979 4696 : LayerKind::Delta.into(), // by definition
2980 4696 : ]
2981 4696 : }
2982 :
2983 : /// Removes a frozen ephemeral layer to TIMELINE_LAYER metrics.
2984 2348 : pub fn dec_frozen_layer(&self, layer: &InMemoryLayer) {
2985 2348 : assert!(matches!(layer.info(), InMemoryLayerInfo::Frozen { .. }));
2986 2348 : let labels = self.make_frozen_layer_labels(layer);
2987 2348 : let size = layer.try_len().expect("frozen layer should have no writer");
2988 2348 : TIMELINE_LAYER_COUNT
2989 2348 : .get_metric_with_label_values(&labels)
2990 2348 : .unwrap()
2991 2348 : .dec();
2992 2348 : TIMELINE_LAYER_SIZE
2993 2348 : .get_metric_with_label_values(&labels)
2994 2348 : .unwrap()
2995 2348 : .sub(size);
2996 2348 : }
2997 :
2998 : /// Adds a frozen ephemeral layer to TIMELINE_LAYER metrics.
2999 2348 : pub fn inc_frozen_layer(&self, layer: &InMemoryLayer) {
3000 2348 : assert!(matches!(layer.info(), InMemoryLayerInfo::Frozen { .. }));
3001 2348 : let labels = self.make_frozen_layer_labels(layer);
3002 2348 : let size = layer.try_len().expect("frozen layer should have no writer");
3003 2348 : TIMELINE_LAYER_COUNT
3004 2348 : .get_metric_with_label_values(&labels)
3005 2348 : .unwrap()
3006 2348 : .inc();
3007 2348 : TIMELINE_LAYER_SIZE
3008 2348 : .get_metric_with_label_values(&labels)
3009 2348 : .unwrap()
3010 2348 : .add(size);
3011 2348 : }
3012 :
3013 : /// Removes a persistent layer from TIMELINE_LAYER metrics.
3014 1384 : pub fn dec_layer(&self, layer_desc: &PersistentLayerDesc) {
3015 1384 : let labels = self.make_layer_labels(layer_desc);
3016 1384 : TIMELINE_LAYER_COUNT
3017 1384 : .get_metric_with_label_values(&labels)
3018 1384 : .unwrap()
3019 1384 : .dec();
3020 1384 : TIMELINE_LAYER_SIZE
3021 1384 : .get_metric_with_label_values(&labels)
3022 1384 : .unwrap()
3023 1384 : .sub(layer_desc.file_size);
3024 1384 : }
3025 :
3026 : /// Adds a persistent layer to TIMELINE_LAYER metrics.
3027 3868 : pub fn inc_layer(&self, layer_desc: &PersistentLayerDesc) {
3028 3868 : let labels = self.make_layer_labels(layer_desc);
3029 3868 : TIMELINE_LAYER_COUNT
3030 3868 : .get_metric_with_label_values(&labels)
3031 3868 : .unwrap()
3032 3868 : .inc();
3033 3868 : TIMELINE_LAYER_SIZE
3034 3868 : .get_metric_with_label_values(&labels)
3035 3868 : .unwrap()
3036 3868 : .add(layer_desc.file_size);
3037 3868 : }
3038 :
3039 20 : pub(crate) fn shutdown(&self) {
3040 20 : let was_shutdown = self
3041 20 : .shutdown
3042 20 : .swap(true, std::sync::atomic::Ordering::Relaxed);
3043 20 :
3044 20 : if was_shutdown {
3045 : // this happens on tenant deletion because tenant first shuts down timelines, then
3046 : // invokes timeline deletion which first shuts down the timeline again.
3047 : // TODO: this can be removed once https://github.com/neondatabase/neon/issues/5080
3048 0 : return;
3049 20 : }
3050 20 :
3051 20 : let tenant_id = &self.tenant_id;
3052 20 : let timeline_id = &self.timeline_id;
3053 20 : let shard_id = &self.shard_id;
3054 20 : let _ = LAST_RECORD_LSN.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3055 20 : let _ = DISK_CONSISTENT_LSN.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3056 20 : let _ = FLUSH_WAIT_UPLOAD_TIME.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3057 20 : let _ = STANDBY_HORIZON.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3058 20 : {
3059 20 : RESIDENT_PHYSICAL_SIZE_GLOBAL.sub(self.resident_physical_size_get());
3060 20 : let _ = RESIDENT_PHYSICAL_SIZE.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3061 20 : }
3062 20 : let _ = VISIBLE_PHYSICAL_SIZE.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3063 20 : let _ = CURRENT_LOGICAL_SIZE.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3064 20 : if let Some(metric) = Lazy::get(&DIRECTORY_ENTRIES_COUNT) {
3065 0 : let _ = metric.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3066 20 : }
3067 :
3068 20 : let _ = TIMELINE_ARCHIVE_SIZE.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3069 20 : let _ = PITR_HISTORY_SIZE.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3070 :
3071 80 : for ref level in LayerLevel::iter() {
3072 180 : for ref kind in LayerKind::iter() {
3073 120 : let labels: [&str; 5] =
3074 120 : [tenant_id, shard_id, timeline_id, level.into(), kind.into()];
3075 120 : let _ = TIMELINE_LAYER_SIZE.remove_label_values(&labels);
3076 120 : let _ = TIMELINE_LAYER_COUNT.remove_label_values(&labels);
3077 120 : }
3078 : }
3079 :
3080 20 : let _ = LAYERS_PER_READ.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3081 20 :
3082 20 : let _ = EVICTIONS.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3083 20 : let _ = AUX_FILE_SIZE.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3084 20 : let _ = VALID_LSN_LEASE_COUNT.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3085 20 :
3086 20 : self.evictions_with_low_residence_duration
3087 20 : .write()
3088 20 : .unwrap()
3089 20 : .remove(tenant_id, shard_id, timeline_id);
3090 :
3091 : // The following metrics are born outside of the TimelineMetrics lifecycle but still
3092 : // removed at the end of it. The idea is to have the metrics outlive the
3093 : // entity during which they're observed, e.g., the smgr metrics shall
3094 : // outlive an individual smgr connection, but not the timeline.
3095 :
3096 200 : for op in StorageTimeOperation::VARIANTS {
3097 180 : let _ = STORAGE_TIME_SUM_PER_TIMELINE.remove_label_values(&[
3098 180 : op,
3099 180 : tenant_id,
3100 180 : shard_id,
3101 180 : timeline_id,
3102 180 : ]);
3103 180 : let _ = STORAGE_TIME_COUNT_PER_TIMELINE.remove_label_values(&[
3104 180 : op,
3105 180 : tenant_id,
3106 180 : shard_id,
3107 180 : timeline_id,
3108 180 : ]);
3109 180 : }
3110 :
3111 60 : for op in STORAGE_IO_SIZE_OPERATIONS {
3112 40 : let _ = STORAGE_IO_SIZE.remove_label_values(&[op, tenant_id, shard_id, timeline_id]);
3113 40 : }
3114 :
3115 20 : let _ = SMGR_QUERY_STARTED_PER_TENANT_TIMELINE.remove_label_values(&[
3116 20 : SmgrQueryType::GetPageAtLsn.into(),
3117 20 : tenant_id,
3118 20 : shard_id,
3119 20 : timeline_id,
3120 20 : ]);
3121 20 : let _ = SMGR_QUERY_TIME_PER_TENANT_TIMELINE.remove_label_values(&[
3122 20 : SmgrQueryType::GetPageAtLsn.into(),
3123 20 : tenant_id,
3124 20 : shard_id,
3125 20 : timeline_id,
3126 20 : ]);
3127 20 : let _ = PAGE_SERVICE_BATCH_SIZE_PER_TENANT_TIMELINE.remove_label_values(&[
3128 20 : tenant_id,
3129 20 : shard_id,
3130 20 : timeline_id,
3131 20 : ]);
3132 20 : let _ = PAGESERVER_TIMELINE_WAL_RECORDS_RECEIVED.remove_label_values(&[
3133 20 : tenant_id,
3134 20 : shard_id,
3135 20 : timeline_id,
3136 20 : ]);
3137 20 : let _ = PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS.remove_label_values(&[
3138 20 : tenant_id,
3139 20 : shard_id,
3140 20 : timeline_id,
3141 20 : ]);
3142 20 : let _ = PAGE_SERVICE_SMGR_BATCH_WAIT_TIME.remove_label_values(&[
3143 20 : tenant_id,
3144 20 : shard_id,
3145 20 : timeline_id,
3146 20 : ]);
3147 20 : }
3148 : }
3149 :
3150 12 : pub(crate) fn remove_tenant_metrics(tenant_shard_id: &TenantShardId) {
3151 12 : // Only shard zero deals in synthetic sizes
3152 12 : if tenant_shard_id.is_shard_zero() {
3153 12 : let tid = tenant_shard_id.tenant_id.to_string();
3154 12 : let _ = TENANT_SYNTHETIC_SIZE_METRIC.remove_label_values(&[&tid]);
3155 12 : }
3156 :
3157 12 : tenant_throttling::remove_tenant_metrics(tenant_shard_id);
3158 12 :
3159 12 : // we leave the BROKEN_TENANTS_SET entry if any
3160 12 : }
3161 :
3162 : /// Maintain a per timeline gauge in addition to the global gauge.
3163 : pub(crate) struct PerTimelineRemotePhysicalSizeGauge {
3164 : last_set: AtomicU64,
3165 : gauge: UIntGauge,
3166 : }
3167 :
3168 : impl PerTimelineRemotePhysicalSizeGauge {
3169 916 : fn new(per_timeline_gauge: UIntGauge) -> Self {
3170 916 : Self {
3171 916 : last_set: AtomicU64::new(0),
3172 916 : gauge: per_timeline_gauge,
3173 916 : }
3174 916 : }
3175 3890 : pub(crate) fn set(&self, sz: u64) {
3176 3890 : self.gauge.set(sz);
3177 3890 : let prev = self.last_set.swap(sz, std::sync::atomic::Ordering::Relaxed);
3178 3890 : if sz < prev {
3179 67 : REMOTE_PHYSICAL_SIZE_GLOBAL.sub(prev - sz);
3180 3823 : } else {
3181 3823 : REMOTE_PHYSICAL_SIZE_GLOBAL.add(sz - prev);
3182 3823 : };
3183 3890 : }
3184 4 : pub(crate) fn get(&self) -> u64 {
3185 4 : self.gauge.get()
3186 4 : }
3187 : }
3188 :
3189 : impl Drop for PerTimelineRemotePhysicalSizeGauge {
3190 40 : fn drop(&mut self) {
3191 40 : REMOTE_PHYSICAL_SIZE_GLOBAL.sub(self.last_set.load(std::sync::atomic::Ordering::Relaxed));
3192 40 : }
3193 : }
3194 :
3195 : pub(crate) struct RemoteTimelineClientMetrics {
3196 : tenant_id: String,
3197 : shard_id: String,
3198 : timeline_id: String,
3199 : pub(crate) remote_physical_size_gauge: PerTimelineRemotePhysicalSizeGauge,
3200 : calls: Mutex<HashMap<(&'static str, &'static str), IntCounterPair>>,
3201 : bytes_started_counter: Mutex<HashMap<(&'static str, &'static str), IntCounter>>,
3202 : bytes_finished_counter: Mutex<HashMap<(&'static str, &'static str), IntCounter>>,
3203 : pub(crate) projected_remote_consistent_lsn_gauge: UIntGauge,
3204 : }
3205 :
3206 : impl RemoteTimelineClientMetrics {
3207 916 : pub fn new(tenant_shard_id: &TenantShardId, timeline_id: &TimelineId) -> Self {
3208 916 : let tenant_id_str = tenant_shard_id.tenant_id.to_string();
3209 916 : let shard_id_str = format!("{}", tenant_shard_id.shard_slug());
3210 916 : let timeline_id_str = timeline_id.to_string();
3211 916 :
3212 916 : let remote_physical_size_gauge = PerTimelineRemotePhysicalSizeGauge::new(
3213 916 : REMOTE_PHYSICAL_SIZE
3214 916 : .get_metric_with_label_values(&[&tenant_id_str, &shard_id_str, &timeline_id_str])
3215 916 : .unwrap(),
3216 916 : );
3217 916 :
3218 916 : let projected_remote_consistent_lsn_gauge = PROJECTED_REMOTE_CONSISTENT_LSN
3219 916 : .get_metric_with_label_values(&[&tenant_id_str, &shard_id_str, &timeline_id_str])
3220 916 : .unwrap();
3221 916 :
3222 916 : RemoteTimelineClientMetrics {
3223 916 : tenant_id: tenant_id_str,
3224 916 : shard_id: shard_id_str,
3225 916 : timeline_id: timeline_id_str,
3226 916 : calls: Mutex::new(HashMap::default()),
3227 916 : bytes_started_counter: Mutex::new(HashMap::default()),
3228 916 : bytes_finished_counter: Mutex::new(HashMap::default()),
3229 916 : remote_physical_size_gauge,
3230 916 : projected_remote_consistent_lsn_gauge,
3231 916 : }
3232 916 : }
3233 :
3234 6049 : pub fn remote_operation_time(
3235 6049 : &self,
3236 6049 : file_kind: &RemoteOpFileKind,
3237 6049 : op_kind: &RemoteOpKind,
3238 6049 : status: &'static str,
3239 6049 : ) -> Histogram {
3240 6049 : let key = (file_kind.as_str(), op_kind.as_str(), status);
3241 6049 : REMOTE_OPERATION_TIME
3242 6049 : .get_metric_with_label_values(&[key.0, key.1, key.2])
3243 6049 : .unwrap()
3244 6049 : }
3245 :
3246 14262 : fn calls_counter_pair(
3247 14262 : &self,
3248 14262 : file_kind: &RemoteOpFileKind,
3249 14262 : op_kind: &RemoteOpKind,
3250 14262 : ) -> IntCounterPair {
3251 14262 : let mut guard = self.calls.lock().unwrap();
3252 14262 : let key = (file_kind.as_str(), op_kind.as_str());
3253 14262 : let metric = guard.entry(key).or_insert_with(move || {
3254 1647 : REMOTE_TIMELINE_CLIENT_CALLS
3255 1647 : .get_metric_with_label_values(&[
3256 1647 : &self.tenant_id,
3257 1647 : &self.shard_id,
3258 1647 : &self.timeline_id,
3259 1647 : key.0,
3260 1647 : key.1,
3261 1647 : ])
3262 1647 : .unwrap()
3263 14262 : });
3264 14262 : metric.clone()
3265 14262 : }
3266 :
3267 3492 : fn bytes_started_counter(
3268 3492 : &self,
3269 3492 : file_kind: &RemoteOpFileKind,
3270 3492 : op_kind: &RemoteOpKind,
3271 3492 : ) -> IntCounter {
3272 3492 : let mut guard = self.bytes_started_counter.lock().unwrap();
3273 3492 : let key = (file_kind.as_str(), op_kind.as_str());
3274 3492 : let metric = guard.entry(key).or_insert_with(move || {
3275 648 : REMOTE_TIMELINE_CLIENT_BYTES_STARTED_COUNTER
3276 648 : .get_metric_with_label_values(&[
3277 648 : &self.tenant_id,
3278 648 : &self.shard_id,
3279 648 : &self.timeline_id,
3280 648 : key.0,
3281 648 : key.1,
3282 648 : ])
3283 648 : .unwrap()
3284 3492 : });
3285 3492 : metric.clone()
3286 3492 : }
3287 :
3288 6479 : fn bytes_finished_counter(
3289 6479 : &self,
3290 6479 : file_kind: &RemoteOpFileKind,
3291 6479 : op_kind: &RemoteOpKind,
3292 6479 : ) -> IntCounter {
3293 6479 : let mut guard = self.bytes_finished_counter.lock().unwrap();
3294 6479 : let key = (file_kind.as_str(), op_kind.as_str());
3295 6479 : let metric = guard.entry(key).or_insert_with(move || {
3296 648 : REMOTE_TIMELINE_CLIENT_BYTES_FINISHED_COUNTER
3297 648 : .get_metric_with_label_values(&[
3298 648 : &self.tenant_id,
3299 648 : &self.shard_id,
3300 648 : &self.timeline_id,
3301 648 : key.0,
3302 648 : key.1,
3303 648 : ])
3304 648 : .unwrap()
3305 6479 : });
3306 6479 : metric.clone()
3307 6479 : }
3308 : }
3309 :
3310 : #[cfg(test)]
3311 : impl RemoteTimelineClientMetrics {
3312 12 : pub fn get_bytes_started_counter_value(
3313 12 : &self,
3314 12 : file_kind: &RemoteOpFileKind,
3315 12 : op_kind: &RemoteOpKind,
3316 12 : ) -> Option<u64> {
3317 12 : let guard = self.bytes_started_counter.lock().unwrap();
3318 12 : let key = (file_kind.as_str(), op_kind.as_str());
3319 12 : guard.get(&key).map(|counter| counter.get())
3320 12 : }
3321 :
3322 12 : pub fn get_bytes_finished_counter_value(
3323 12 : &self,
3324 12 : file_kind: &RemoteOpFileKind,
3325 12 : op_kind: &RemoteOpKind,
3326 12 : ) -> Option<u64> {
3327 12 : let guard = self.bytes_finished_counter.lock().unwrap();
3328 12 : let key = (file_kind.as_str(), op_kind.as_str());
3329 12 : guard.get(&key).map(|counter| counter.get())
3330 12 : }
3331 : }
3332 :
3333 : /// See [`RemoteTimelineClientMetrics::call_begin`].
3334 : #[must_use]
3335 : pub(crate) struct RemoteTimelineClientCallMetricGuard {
3336 : /// Decremented on drop.
3337 : calls_counter_pair: Option<IntCounterPair>,
3338 : /// If Some(), this references the bytes_finished metric, and we increment it by the given `u64` on drop.
3339 : bytes_finished: Option<(IntCounter, u64)>,
3340 : }
3341 :
3342 : impl RemoteTimelineClientCallMetricGuard {
3343 : /// Consume this guard object without performing the metric updates it would do on `drop()`.
3344 : /// The caller vouches to do the metric updates manually.
3345 7628 : pub fn will_decrement_manually(mut self) {
3346 7628 : let RemoteTimelineClientCallMetricGuard {
3347 7628 : calls_counter_pair,
3348 7628 : bytes_finished,
3349 7628 : } = &mut self;
3350 7628 : calls_counter_pair.take();
3351 7628 : bytes_finished.take();
3352 7628 : }
3353 : }
3354 :
3355 : impl Drop for RemoteTimelineClientCallMetricGuard {
3356 7696 : fn drop(&mut self) {
3357 7696 : let RemoteTimelineClientCallMetricGuard {
3358 7696 : calls_counter_pair,
3359 7696 : bytes_finished,
3360 7696 : } = self;
3361 7696 : if let Some(guard) = calls_counter_pair.take() {
3362 68 : guard.dec();
3363 7628 : }
3364 7696 : if let Some((bytes_finished_metric, value)) = bytes_finished {
3365 0 : bytes_finished_metric.inc_by(*value);
3366 7696 : }
3367 7696 : }
3368 : }
3369 :
3370 : /// The enum variants communicate to the [`RemoteTimelineClientMetrics`] whether to
3371 : /// track the byte size of this call in applicable metric(s).
3372 : pub(crate) enum RemoteTimelineClientMetricsCallTrackSize {
3373 : /// Do not account for this call's byte size in any metrics.
3374 : /// The `reason` field is there to make the call sites self-documenting
3375 : /// about why they don't need the metric.
3376 : DontTrackSize { reason: &'static str },
3377 : /// Track the byte size of the call in applicable metric(s).
3378 : Bytes(u64),
3379 : }
3380 :
3381 : impl RemoteTimelineClientMetrics {
3382 : /// Update the metrics that change when a call to the remote timeline client instance starts.
3383 : ///
3384 : /// Drop the returned guard object once the operation is finished to updates corresponding metrics that track completions.
3385 : /// Or, use [`RemoteTimelineClientCallMetricGuard::will_decrement_manually`] and [`call_end`](Self::call_end) if that
3386 : /// is more suitable.
3387 : /// Never do both.
3388 7696 : pub(crate) fn call_begin(
3389 7696 : &self,
3390 7696 : file_kind: &RemoteOpFileKind,
3391 7696 : op_kind: &RemoteOpKind,
3392 7696 : size: RemoteTimelineClientMetricsCallTrackSize,
3393 7696 : ) -> RemoteTimelineClientCallMetricGuard {
3394 7696 : let calls_counter_pair = self.calls_counter_pair(file_kind, op_kind);
3395 7696 : calls_counter_pair.inc();
3396 :
3397 7696 : let bytes_finished = match size {
3398 4204 : RemoteTimelineClientMetricsCallTrackSize::DontTrackSize { reason: _reason } => {
3399 4204 : // nothing to do
3400 4204 : None
3401 : }
3402 3492 : RemoteTimelineClientMetricsCallTrackSize::Bytes(size) => {
3403 3492 : self.bytes_started_counter(file_kind, op_kind).inc_by(size);
3404 3492 : let finished_counter = self.bytes_finished_counter(file_kind, op_kind);
3405 3492 : Some((finished_counter, size))
3406 : }
3407 : };
3408 7696 : RemoteTimelineClientCallMetricGuard {
3409 7696 : calls_counter_pair: Some(calls_counter_pair),
3410 7696 : bytes_finished,
3411 7696 : }
3412 7696 : }
3413 :
3414 : /// Manually udpate the metrics that track completions, instead of using the guard object.
3415 : /// Using the guard object is generally preferable.
3416 : /// See [`call_begin`](Self::call_begin) for more context.
3417 6566 : pub(crate) fn call_end(
3418 6566 : &self,
3419 6566 : file_kind: &RemoteOpFileKind,
3420 6566 : op_kind: &RemoteOpKind,
3421 6566 : size: RemoteTimelineClientMetricsCallTrackSize,
3422 6566 : ) {
3423 6566 : let calls_counter_pair = self.calls_counter_pair(file_kind, op_kind);
3424 6566 : calls_counter_pair.dec();
3425 6566 : match size {
3426 3579 : RemoteTimelineClientMetricsCallTrackSize::DontTrackSize { reason: _reason } => {}
3427 2987 : RemoteTimelineClientMetricsCallTrackSize::Bytes(size) => {
3428 2987 : self.bytes_finished_counter(file_kind, op_kind).inc_by(size);
3429 2987 : }
3430 : }
3431 6566 : }
3432 : }
3433 :
3434 : impl Drop for RemoteTimelineClientMetrics {
3435 40 : fn drop(&mut self) {
3436 40 : let RemoteTimelineClientMetrics {
3437 40 : tenant_id,
3438 40 : shard_id,
3439 40 : timeline_id,
3440 40 : remote_physical_size_gauge,
3441 40 : calls,
3442 40 : bytes_started_counter,
3443 40 : bytes_finished_counter,
3444 40 : projected_remote_consistent_lsn_gauge,
3445 40 : } = self;
3446 48 : for ((a, b), _) in calls.get_mut().unwrap().drain() {
3447 48 : let mut res = [Ok(()), Ok(())];
3448 48 : REMOTE_TIMELINE_CLIENT_CALLS
3449 48 : .remove_label_values(&mut res, &[tenant_id, shard_id, timeline_id, a, b]);
3450 48 : // don't care about results
3451 48 : }
3452 40 : for ((a, b), _) in bytes_started_counter.get_mut().unwrap().drain() {
3453 12 : let _ = REMOTE_TIMELINE_CLIENT_BYTES_STARTED_COUNTER.remove_label_values(&[
3454 12 : tenant_id,
3455 12 : shard_id,
3456 12 : timeline_id,
3457 12 : a,
3458 12 : b,
3459 12 : ]);
3460 12 : }
3461 40 : for ((a, b), _) in bytes_finished_counter.get_mut().unwrap().drain() {
3462 12 : let _ = REMOTE_TIMELINE_CLIENT_BYTES_FINISHED_COUNTER.remove_label_values(&[
3463 12 : tenant_id,
3464 12 : shard_id,
3465 12 : timeline_id,
3466 12 : a,
3467 12 : b,
3468 12 : ]);
3469 12 : }
3470 40 : {
3471 40 : let _ = remote_physical_size_gauge; // use to avoid 'unused' warning in desctructuring above
3472 40 : let _ = REMOTE_PHYSICAL_SIZE.remove_label_values(&[tenant_id, shard_id, timeline_id]);
3473 40 : }
3474 40 : {
3475 40 : let _ = projected_remote_consistent_lsn_gauge;
3476 40 : let _ = PROJECTED_REMOTE_CONSISTENT_LSN.remove_label_values(&[
3477 40 : tenant_id,
3478 40 : shard_id,
3479 40 : timeline_id,
3480 40 : ]);
3481 40 : }
3482 40 : }
3483 : }
3484 :
3485 : /// Wrapper future that measures the time spent by a remote storage operation,
3486 : /// and records the time and success/failure as a prometheus metric.
3487 : pub(crate) trait MeasureRemoteOp: Sized {
3488 6458 : fn measure_remote_op(
3489 6458 : self,
3490 6458 : file_kind: RemoteOpFileKind,
3491 6458 : op: RemoteOpKind,
3492 6458 : metrics: Arc<RemoteTimelineClientMetrics>,
3493 6458 : ) -> MeasuredRemoteOp<Self> {
3494 6458 : let start = Instant::now();
3495 6458 : MeasuredRemoteOp {
3496 6458 : inner: self,
3497 6458 : file_kind,
3498 6458 : op,
3499 6458 : start,
3500 6458 : metrics,
3501 6458 : }
3502 6458 : }
3503 : }
3504 :
3505 : impl<T: Sized> MeasureRemoteOp for T {}
3506 :
3507 : pin_project! {
3508 : pub(crate) struct MeasuredRemoteOp<F>
3509 : {
3510 : #[pin]
3511 : inner: F,
3512 : file_kind: RemoteOpFileKind,
3513 : op: RemoteOpKind,
3514 : start: Instant,
3515 : metrics: Arc<RemoteTimelineClientMetrics>,
3516 : }
3517 : }
3518 :
3519 : impl<F: Future<Output = Result<O, E>>, O, E> Future for MeasuredRemoteOp<F> {
3520 : type Output = Result<O, E>;
3521 :
3522 97712 : fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3523 97712 : let this = self.project();
3524 97712 : let poll_result = this.inner.poll(cx);
3525 97712 : if let Poll::Ready(ref res) = poll_result {
3526 6049 : let duration = this.start.elapsed();
3527 6049 : let status = if res.is_ok() { &"success" } else { &"failure" };
3528 6049 : this.metrics
3529 6049 : .remote_operation_time(this.file_kind, this.op, status)
3530 6049 : .observe(duration.as_secs_f64());
3531 91663 : }
3532 97712 : poll_result
3533 97712 : }
3534 : }
3535 :
3536 : pub mod tokio_epoll_uring {
3537 : use std::{
3538 : collections::HashMap,
3539 : sync::{Arc, Mutex},
3540 : };
3541 :
3542 : use metrics::{register_histogram, register_int_counter, Histogram, LocalHistogram, UIntGauge};
3543 : use once_cell::sync::Lazy;
3544 :
3545 : /// Shared storage for tokio-epoll-uring thread local metrics.
3546 : pub(crate) static THREAD_LOCAL_METRICS_STORAGE: Lazy<ThreadLocalMetricsStorage> =
3547 234 : Lazy::new(|| {
3548 234 : let slots_submission_queue_depth = register_histogram!(
3549 234 : "pageserver_tokio_epoll_uring_slots_submission_queue_depth",
3550 234 : "The slots waiters queue depth of each tokio_epoll_uring system",
3551 234 : vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0],
3552 234 : )
3553 234 : .expect("failed to define a metric");
3554 234 : ThreadLocalMetricsStorage {
3555 234 : observers: Mutex::new(HashMap::new()),
3556 234 : slots_submission_queue_depth,
3557 234 : }
3558 234 : });
3559 :
3560 : pub struct ThreadLocalMetricsStorage {
3561 : /// List of thread local metrics observers.
3562 : observers: Mutex<HashMap<u64, Arc<ThreadLocalMetrics>>>,
3563 : /// A histogram shared between all thread local systems
3564 : /// for collecting slots submission queue depth.
3565 : slots_submission_queue_depth: Histogram,
3566 : }
3567 :
3568 : /// Each thread-local [`tokio_epoll_uring::System`] gets one of these as its
3569 : /// [`tokio_epoll_uring::metrics::PerSystemMetrics`] generic.
3570 : ///
3571 : /// The System makes observations into [`Self`] and periodically, the collector
3572 : /// comes along and flushes [`Self`] into the shared storage [`THREAD_LOCAL_METRICS_STORAGE`].
3573 : ///
3574 : /// [`LocalHistogram`] is `!Send`, so, we need to put it behind a [`Mutex`].
3575 : /// But except for the periodic flush, the lock is uncontended so there's no waiting
3576 : /// for cache coherence protocol to get an exclusive cache line.
3577 : pub struct ThreadLocalMetrics {
3578 : /// Local observer of thread local tokio-epoll-uring system's slots waiters queue depth.
3579 : slots_submission_queue_depth: Mutex<LocalHistogram>,
3580 : }
3581 :
3582 : impl ThreadLocalMetricsStorage {
3583 : /// Registers a new thread local system. Returns a thread local metrics observer.
3584 1108 : pub fn register_system(&self, id: u64) -> Arc<ThreadLocalMetrics> {
3585 1108 : let per_system_metrics = Arc::new(ThreadLocalMetrics::new(
3586 1108 : self.slots_submission_queue_depth.local(),
3587 1108 : ));
3588 1108 : let mut g = self.observers.lock().unwrap();
3589 1108 : g.insert(id, Arc::clone(&per_system_metrics));
3590 1108 : per_system_metrics
3591 1108 : }
3592 :
3593 : /// Removes metrics observer for a thread local system.
3594 : /// This should be called before dropping a thread local system.
3595 234 : pub fn remove_system(&self, id: u64) {
3596 234 : let mut g = self.observers.lock().unwrap();
3597 234 : g.remove(&id);
3598 234 : }
3599 :
3600 : /// Flush all thread local metrics to the shared storage.
3601 0 : pub fn flush_thread_local_metrics(&self) {
3602 0 : let g = self.observers.lock().unwrap();
3603 0 : g.values().for_each(|local| {
3604 0 : local.flush();
3605 0 : });
3606 0 : }
3607 : }
3608 :
3609 : impl ThreadLocalMetrics {
3610 1108 : pub fn new(slots_submission_queue_depth: LocalHistogram) -> Self {
3611 1108 : ThreadLocalMetrics {
3612 1108 : slots_submission_queue_depth: Mutex::new(slots_submission_queue_depth),
3613 1108 : }
3614 1108 : }
3615 :
3616 : /// Flushes the thread local metrics to shared aggregator.
3617 0 : pub fn flush(&self) {
3618 0 : let Self {
3619 0 : slots_submission_queue_depth,
3620 0 : } = self;
3621 0 : slots_submission_queue_depth.lock().unwrap().flush();
3622 0 : }
3623 : }
3624 :
3625 : impl tokio_epoll_uring::metrics::PerSystemMetrics for ThreadLocalMetrics {
3626 1834085 : fn observe_slots_submission_queue_depth(&self, queue_depth: u64) {
3627 1834085 : let Self {
3628 1834085 : slots_submission_queue_depth,
3629 1834085 : } = self;
3630 1834085 : slots_submission_queue_depth
3631 1834085 : .lock()
3632 1834085 : .unwrap()
3633 1834085 : .observe(queue_depth as f64);
3634 1834085 : }
3635 : }
3636 :
3637 : pub struct Collector {
3638 : descs: Vec<metrics::core::Desc>,
3639 : systems_created: UIntGauge,
3640 : systems_destroyed: UIntGauge,
3641 : thread_local_metrics_storage: &'static ThreadLocalMetricsStorage,
3642 : }
3643 :
3644 : impl metrics::core::Collector for Collector {
3645 0 : fn desc(&self) -> Vec<&metrics::core::Desc> {
3646 0 : self.descs.iter().collect()
3647 0 : }
3648 :
3649 0 : fn collect(&self) -> Vec<metrics::proto::MetricFamily> {
3650 0 : let mut mfs = Vec::with_capacity(Self::NMETRICS);
3651 0 : let tokio_epoll_uring::metrics::GlobalMetrics {
3652 0 : systems_created,
3653 0 : systems_destroyed,
3654 0 : } = tokio_epoll_uring::metrics::global();
3655 0 : self.systems_created.set(systems_created);
3656 0 : mfs.extend(self.systems_created.collect());
3657 0 : self.systems_destroyed.set(systems_destroyed);
3658 0 : mfs.extend(self.systems_destroyed.collect());
3659 0 :
3660 0 : self.thread_local_metrics_storage
3661 0 : .flush_thread_local_metrics();
3662 0 :
3663 0 : mfs.extend(
3664 0 : self.thread_local_metrics_storage
3665 0 : .slots_submission_queue_depth
3666 0 : .collect(),
3667 0 : );
3668 0 : mfs
3669 0 : }
3670 : }
3671 :
3672 : impl Collector {
3673 : const NMETRICS: usize = 3;
3674 :
3675 : #[allow(clippy::new_without_default)]
3676 0 : pub fn new() -> Self {
3677 0 : let mut descs = Vec::new();
3678 0 :
3679 0 : let systems_created = UIntGauge::new(
3680 0 : "pageserver_tokio_epoll_uring_systems_created",
3681 0 : "counter of tokio-epoll-uring systems that were created",
3682 0 : )
3683 0 : .unwrap();
3684 0 : descs.extend(
3685 0 : metrics::core::Collector::desc(&systems_created)
3686 0 : .into_iter()
3687 0 : .cloned(),
3688 0 : );
3689 0 :
3690 0 : let systems_destroyed = UIntGauge::new(
3691 0 : "pageserver_tokio_epoll_uring_systems_destroyed",
3692 0 : "counter of tokio-epoll-uring systems that were destroyed",
3693 0 : )
3694 0 : .unwrap();
3695 0 : descs.extend(
3696 0 : metrics::core::Collector::desc(&systems_destroyed)
3697 0 : .into_iter()
3698 0 : .cloned(),
3699 0 : );
3700 0 :
3701 0 : Self {
3702 0 : descs,
3703 0 : systems_created,
3704 0 : systems_destroyed,
3705 0 : thread_local_metrics_storage: &THREAD_LOCAL_METRICS_STORAGE,
3706 0 : }
3707 0 : }
3708 : }
3709 :
3710 234 : pub(crate) static THREAD_LOCAL_LAUNCH_SUCCESSES: Lazy<metrics::IntCounter> = Lazy::new(|| {
3711 234 : register_int_counter!(
3712 234 : "pageserver_tokio_epoll_uring_pageserver_thread_local_launch_success_count",
3713 234 : "Number of times where thread_local_system creation spanned multiple executor threads",
3714 234 : )
3715 234 : .unwrap()
3716 234 : });
3717 :
3718 0 : pub(crate) static THREAD_LOCAL_LAUNCH_FAILURES: Lazy<metrics::IntCounter> = Lazy::new(|| {
3719 0 : register_int_counter!(
3720 0 : "pageserver_tokio_epoll_uring_pageserver_thread_local_launch_failures_count",
3721 0 : "Number of times thread_local_system creation failed and was retried after back-off.",
3722 0 : )
3723 0 : .unwrap()
3724 0 : });
3725 : }
3726 :
3727 : pub(crate) mod tenant_throttling {
3728 : use metrics::{register_int_counter_vec, IntCounter};
3729 : use once_cell::sync::Lazy;
3730 : use utils::shard::TenantShardId;
3731 :
3732 : pub(crate) struct GlobalAndPerTenantIntCounter {
3733 : global: IntCounter,
3734 : per_tenant: IntCounter,
3735 : }
3736 :
3737 : impl GlobalAndPerTenantIntCounter {
3738 : #[inline(always)]
3739 0 : pub(crate) fn inc(&self) {
3740 0 : self.inc_by(1)
3741 0 : }
3742 : #[inline(always)]
3743 0 : pub(crate) fn inc_by(&self, n: u64) {
3744 0 : self.global.inc_by(n);
3745 0 : self.per_tenant.inc_by(n);
3746 0 : }
3747 : }
3748 :
3749 : pub(crate) struct Metrics<const KIND: usize> {
3750 : pub(super) count_accounted_start: GlobalAndPerTenantIntCounter,
3751 : pub(super) count_accounted_finish: GlobalAndPerTenantIntCounter,
3752 : pub(super) wait_time: GlobalAndPerTenantIntCounter,
3753 : pub(super) count_throttled: GlobalAndPerTenantIntCounter,
3754 : }
3755 :
3756 408 : static COUNT_ACCOUNTED_START: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3757 408 : register_int_counter_vec!(
3758 408 : "pageserver_tenant_throttling_count_accounted_start_global",
3759 408 : "Count of tenant throttling starts, by kind of throttle.",
3760 408 : &["kind"]
3761 408 : )
3762 408 : .unwrap()
3763 408 : });
3764 408 : static COUNT_ACCOUNTED_START_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3765 408 : register_int_counter_vec!(
3766 408 : "pageserver_tenant_throttling_count_accounted_start",
3767 408 : "Count of tenant throttling starts, by kind of throttle.",
3768 408 : &["kind", "tenant_id", "shard_id"]
3769 408 : )
3770 408 : .unwrap()
3771 408 : });
3772 408 : static COUNT_ACCOUNTED_FINISH: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3773 408 : register_int_counter_vec!(
3774 408 : "pageserver_tenant_throttling_count_accounted_finish_global",
3775 408 : "Count of tenant throttling finishes, by kind of throttle.",
3776 408 : &["kind"]
3777 408 : )
3778 408 : .unwrap()
3779 408 : });
3780 408 : static COUNT_ACCOUNTED_FINISH_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3781 408 : register_int_counter_vec!(
3782 408 : "pageserver_tenant_throttling_count_accounted_finish",
3783 408 : "Count of tenant throttling finishes, by kind of throttle.",
3784 408 : &["kind", "tenant_id", "shard_id"]
3785 408 : )
3786 408 : .unwrap()
3787 408 : });
3788 408 : static WAIT_USECS: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3789 408 : register_int_counter_vec!(
3790 408 : "pageserver_tenant_throttling_wait_usecs_sum_global",
3791 408 : "Sum of microseconds that spent waiting throttle by kind of throttle.",
3792 408 : &["kind"]
3793 408 : )
3794 408 : .unwrap()
3795 408 : });
3796 408 : static WAIT_USECS_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3797 408 : register_int_counter_vec!(
3798 408 : "pageserver_tenant_throttling_wait_usecs_sum",
3799 408 : "Sum of microseconds that spent waiting throttle by kind of throttle.",
3800 408 : &["kind", "tenant_id", "shard_id"]
3801 408 : )
3802 408 : .unwrap()
3803 408 : });
3804 :
3805 408 : static WAIT_COUNT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3806 408 : register_int_counter_vec!(
3807 408 : "pageserver_tenant_throttling_count_global",
3808 408 : "Count of tenant throttlings, by kind of throttle.",
3809 408 : &["kind"]
3810 408 : )
3811 408 : .unwrap()
3812 408 : });
3813 408 : static WAIT_COUNT_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3814 408 : register_int_counter_vec!(
3815 408 : "pageserver_tenant_throttling_count",
3816 408 : "Count of tenant throttlings, by kind of throttle.",
3817 408 : &["kind", "tenant_id", "shard_id"]
3818 408 : )
3819 408 : .unwrap()
3820 408 : });
3821 :
3822 : const KINDS: &[&str] = &["pagestream"];
3823 : pub type Pagestream = Metrics<0>;
3824 :
3825 : impl<const KIND: usize> Metrics<KIND> {
3826 444 : pub(crate) fn new(tenant_shard_id: &TenantShardId) -> Self {
3827 444 : let per_tenant_label_values = &[
3828 444 : KINDS[KIND],
3829 444 : &tenant_shard_id.tenant_id.to_string(),
3830 444 : &tenant_shard_id.shard_slug().to_string(),
3831 444 : ];
3832 444 : Metrics {
3833 444 : count_accounted_start: {
3834 444 : GlobalAndPerTenantIntCounter {
3835 444 : global: COUNT_ACCOUNTED_START.with_label_values(&[KINDS[KIND]]),
3836 444 : per_tenant: COUNT_ACCOUNTED_START_PER_TENANT
3837 444 : .with_label_values(per_tenant_label_values),
3838 444 : }
3839 444 : },
3840 444 : count_accounted_finish: {
3841 444 : GlobalAndPerTenantIntCounter {
3842 444 : global: COUNT_ACCOUNTED_FINISH.with_label_values(&[KINDS[KIND]]),
3843 444 : per_tenant: COUNT_ACCOUNTED_FINISH_PER_TENANT
3844 444 : .with_label_values(per_tenant_label_values),
3845 444 : }
3846 444 : },
3847 444 : wait_time: {
3848 444 : GlobalAndPerTenantIntCounter {
3849 444 : global: WAIT_USECS.with_label_values(&[KINDS[KIND]]),
3850 444 : per_tenant: WAIT_USECS_PER_TENANT
3851 444 : .with_label_values(per_tenant_label_values),
3852 444 : }
3853 444 : },
3854 444 : count_throttled: {
3855 444 : GlobalAndPerTenantIntCounter {
3856 444 : global: WAIT_COUNT.with_label_values(&[KINDS[KIND]]),
3857 444 : per_tenant: WAIT_COUNT_PER_TENANT
3858 444 : .with_label_values(per_tenant_label_values),
3859 444 : }
3860 444 : },
3861 444 : }
3862 444 : }
3863 : }
3864 :
3865 0 : pub(crate) fn preinitialize_global_metrics() {
3866 0 : Lazy::force(&COUNT_ACCOUNTED_START);
3867 0 : Lazy::force(&COUNT_ACCOUNTED_FINISH);
3868 0 : Lazy::force(&WAIT_USECS);
3869 0 : Lazy::force(&WAIT_COUNT);
3870 0 : }
3871 :
3872 12 : pub(crate) fn remove_tenant_metrics(tenant_shard_id: &TenantShardId) {
3873 48 : for m in &[
3874 12 : &COUNT_ACCOUNTED_START_PER_TENANT,
3875 12 : &COUNT_ACCOUNTED_FINISH_PER_TENANT,
3876 12 : &WAIT_USECS_PER_TENANT,
3877 12 : &WAIT_COUNT_PER_TENANT,
3878 12 : ] {
3879 96 : for kind in KINDS {
3880 48 : let _ = m.remove_label_values(&[
3881 48 : kind,
3882 48 : &tenant_shard_id.tenant_id.to_string(),
3883 48 : &tenant_shard_id.shard_slug().to_string(),
3884 48 : ]);
3885 48 : }
3886 : }
3887 12 : }
3888 : }
3889 :
3890 : pub(crate) mod disk_usage_based_eviction {
3891 : use super::*;
3892 :
3893 : pub(crate) struct Metrics {
3894 : pub(crate) tenant_collection_time: Histogram,
3895 : pub(crate) tenant_layer_count: Histogram,
3896 : pub(crate) layers_collected: IntCounter,
3897 : pub(crate) layers_selected: IntCounter,
3898 : pub(crate) layers_evicted: IntCounter,
3899 : }
3900 :
3901 : impl Default for Metrics {
3902 0 : fn default() -> Self {
3903 0 : let tenant_collection_time = register_histogram!(
3904 0 : "pageserver_disk_usage_based_eviction_tenant_collection_seconds",
3905 0 : "Time spent collecting layers from a tenant -- not normalized by collected layer amount",
3906 0 : vec![0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0]
3907 0 : )
3908 0 : .unwrap();
3909 0 :
3910 0 : let tenant_layer_count = register_histogram!(
3911 0 : "pageserver_disk_usage_based_eviction_tenant_collected_layers",
3912 0 : "Amount of layers gathered from a tenant",
3913 0 : vec![5.0, 50.0, 500.0, 5000.0, 50000.0]
3914 0 : )
3915 0 : .unwrap();
3916 0 :
3917 0 : let layers_collected = register_int_counter!(
3918 0 : "pageserver_disk_usage_based_eviction_collected_layers_total",
3919 0 : "Amount of layers collected"
3920 0 : )
3921 0 : .unwrap();
3922 0 :
3923 0 : let layers_selected = register_int_counter!(
3924 0 : "pageserver_disk_usage_based_eviction_select_layers_total",
3925 0 : "Amount of layers selected"
3926 0 : )
3927 0 : .unwrap();
3928 0 :
3929 0 : let layers_evicted = register_int_counter!(
3930 0 : "pageserver_disk_usage_based_eviction_evicted_layers_total",
3931 0 : "Amount of layers successfully evicted"
3932 0 : )
3933 0 : .unwrap();
3934 0 :
3935 0 : Self {
3936 0 : tenant_collection_time,
3937 0 : tenant_layer_count,
3938 0 : layers_collected,
3939 0 : layers_selected,
3940 0 : layers_evicted,
3941 0 : }
3942 0 : }
3943 : }
3944 :
3945 : pub(crate) static METRICS: Lazy<Metrics> = Lazy::new(Metrics::default);
3946 : }
3947 :
3948 396 : static TOKIO_EXECUTOR_THREAD_COUNT: Lazy<UIntGaugeVec> = Lazy::new(|| {
3949 396 : register_uint_gauge_vec!(
3950 396 : "pageserver_tokio_executor_thread_configured_count",
3951 396 : "Total number of configued tokio executor threads in the process.
3952 396 : The `setup` label denotes whether we're running with multiple runtimes or a single runtime.",
3953 396 : &["setup"],
3954 396 : )
3955 396 : .unwrap()
3956 396 : });
3957 :
3958 396 : pub(crate) fn set_tokio_runtime_setup(setup: &str, num_threads: NonZeroUsize) {
3959 : static SERIALIZE: std::sync::Mutex<()> = std::sync::Mutex::new(());
3960 396 : let _guard = SERIALIZE.lock().unwrap();
3961 396 : TOKIO_EXECUTOR_THREAD_COUNT.reset();
3962 396 : TOKIO_EXECUTOR_THREAD_COUNT
3963 396 : .get_metric_with_label_values(&[setup])
3964 396 : .unwrap()
3965 396 : .set(u64::try_from(num_threads.get()).unwrap());
3966 396 : }
3967 :
3968 0 : pub fn preinitialize_metrics(conf: &'static PageServerConf) {
3969 0 : set_page_service_config_max_batch_size(&conf.page_service_pipelining);
3970 0 :
3971 0 : // Python tests need these and on some we do alerting.
3972 0 : //
3973 0 : // FIXME(4813): make it so that we have no top level metrics as this fn will easily fall out of
3974 0 : // order:
3975 0 : // - global metrics reside in a Lazy<PageserverMetrics>
3976 0 : // - access via crate::metrics::PS_METRICS.some_metric.inc()
3977 0 : // - could move the statics into TimelineMetrics::new()?
3978 0 :
3979 0 : // counters
3980 0 : [
3981 0 : &UNEXPECTED_ONDEMAND_DOWNLOADS,
3982 0 : &WALRECEIVER_STARTED_CONNECTIONS,
3983 0 : &WALRECEIVER_BROKER_UPDATES,
3984 0 : &WALRECEIVER_CANDIDATES_ADDED,
3985 0 : &WALRECEIVER_CANDIDATES_REMOVED,
3986 0 : &tokio_epoll_uring::THREAD_LOCAL_LAUNCH_FAILURES,
3987 0 : &tokio_epoll_uring::THREAD_LOCAL_LAUNCH_SUCCESSES,
3988 0 : &REMOTE_ONDEMAND_DOWNLOADED_LAYERS,
3989 0 : &REMOTE_ONDEMAND_DOWNLOADED_BYTES,
3990 0 : &CIRCUIT_BREAKERS_BROKEN,
3991 0 : &CIRCUIT_BREAKERS_UNBROKEN,
3992 0 : &PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS_GLOBAL,
3993 0 : ]
3994 0 : .into_iter()
3995 0 : .for_each(|c| {
3996 0 : Lazy::force(c);
3997 0 : });
3998 0 :
3999 0 : // Deletion queue stats
4000 0 : Lazy::force(&DELETION_QUEUE);
4001 0 :
4002 0 : // Tenant stats
4003 0 : Lazy::force(&TENANT);
4004 0 :
4005 0 : // Tenant manager stats
4006 0 : Lazy::force(&TENANT_MANAGER);
4007 0 :
4008 0 : Lazy::force(&crate::tenant::storage_layer::layer::LAYER_IMPL_METRICS);
4009 0 : Lazy::force(&disk_usage_based_eviction::METRICS);
4010 :
4011 0 : for state_name in pageserver_api::models::TenantState::VARIANTS {
4012 0 : // initialize the metric for all gauges, otherwise the time series might seemingly show
4013 0 : // values from last restart.
4014 0 : TENANT_STATE_METRIC.with_label_values(&[state_name]).set(0);
4015 0 : }
4016 :
4017 : // countervecs
4018 0 : [
4019 0 : &BACKGROUND_LOOP_PERIOD_OVERRUN_COUNT,
4020 0 : &SMGR_QUERY_STARTED_GLOBAL,
4021 0 : ]
4022 0 : .into_iter()
4023 0 : .for_each(|c| {
4024 0 : Lazy::force(c);
4025 0 : });
4026 0 :
4027 0 : // gauges
4028 0 : WALRECEIVER_ACTIVE_MANAGERS.get();
4029 0 :
4030 0 : // histograms
4031 0 : [
4032 0 : &LAYERS_PER_READ_GLOBAL,
4033 0 : &DELTAS_PER_READ_GLOBAL,
4034 0 : &WAIT_LSN_TIME,
4035 0 : &WAL_REDO_TIME,
4036 0 : &WAL_REDO_RECORDS_HISTOGRAM,
4037 0 : &WAL_REDO_BYTES_HISTOGRAM,
4038 0 : &WAL_REDO_PROCESS_LAUNCH_DURATION_HISTOGRAM,
4039 0 : &PAGE_SERVICE_BATCH_SIZE_GLOBAL,
4040 0 : &PAGE_SERVICE_SMGR_BATCH_WAIT_TIME_GLOBAL,
4041 0 : ]
4042 0 : .into_iter()
4043 0 : .for_each(|h| {
4044 0 : Lazy::force(h);
4045 0 : });
4046 0 :
4047 0 : // Custom
4048 0 : Lazy::force(&BASEBACKUP_QUERY_TIME);
4049 0 : Lazy::force(&COMPUTE_COMMANDS_COUNTERS);
4050 0 : Lazy::force(&tokio_epoll_uring::THREAD_LOCAL_METRICS_STORAGE);
4051 0 :
4052 0 : tenant_throttling::preinitialize_global_metrics();
4053 0 : }
|