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 400 : pub(crate) static STORAGE_TIME_SUM_PER_TIMELINE: Lazy<CounterVec> = Lazy::new(|| {
90 400 : register_counter_vec!(
91 400 : "pageserver_storage_operations_seconds_sum",
92 400 : "Total time spent on storage operations with operation, tenant and timeline dimensions",
93 400 : &["operation", "tenant_id", "shard_id", "timeline_id"],
94 400 : )
95 400 : .expect("failed to define a metric")
96 400 : });
97 :
98 400 : pub(crate) static STORAGE_TIME_COUNT_PER_TIMELINE: Lazy<IntCounterVec> = Lazy::new(|| {
99 400 : register_int_counter_vec!(
100 400 : "pageserver_storage_operations_seconds_count",
101 400 : "Count of storage operations with operation, tenant and timeline dimensions",
102 400 : &["operation", "tenant_id", "shard_id", "timeline_id"],
103 400 : )
104 400 : .expect("failed to define a metric")
105 400 : });
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 400 : pub(crate) static STORAGE_TIME_GLOBAL: Lazy<HistogramVec> = Lazy::new(|| {
111 400 : register_histogram_vec!(
112 400 : "pageserver_storage_operations_seconds_global",
113 400 : "Time spent on storage operations",
114 400 : &["operation"],
115 400 : STORAGE_OP_BUCKETS.into(),
116 400 : )
117 400 : .expect("failed to define a metric")
118 400 : });
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 400 : pub(crate) static LAYERS_PER_READ: Lazy<HistogramVec> = Lazy::new(|| {
127 400 : register_histogram_vec!(
128 400 : "pageserver_layers_per_read",
129 400 : "Layers visited to serve a single read (read amplification). In a batch, all visited layers count towards every read.",
130 400 : &["tenant_id", "shard_id", "timeline_id"],
131 400 : // Low resolution to reduce cardinality.
132 400 : vec![1.0, 5.0, 10.0, 25.0, 50.0, 100.0],
133 400 : )
134 400 : .expect("failed to define a metric")
135 400 : });
136 :
137 392 : pub(crate) static LAYERS_PER_READ_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
138 392 : register_histogram!(
139 392 : "pageserver_layers_per_read_global",
140 392 : "Layers visited to serve a single read (read amplification). In a batch, all visited layers count towards every read.",
141 392 : vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0],
142 392 : )
143 392 : .expect("failed to define a metric")
144 392 : });
145 :
146 392 : pub(crate) static DELTAS_PER_READ_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
147 392 : // We expect this to be low because of Postgres checkpoints. Let's see if that holds.
148 392 : register_histogram!(
149 392 : "pageserver_deltas_per_read_global",
150 392 : "Number of delta pages applied to image page per read",
151 392 : vec![0.0, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0],
152 392 : )
153 392 : .expect("failed to define a metric")
154 392 : });
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 39308 : pub(crate) fn for_task_kind(&self, task_kind: TaskKind) -> Option<&Histogram> {
197 39308 : self.map[task_kind].as_ref()
198 39308 : }
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 384 : pub(crate) static GET_VECTORED_LATENCY: Lazy<GetVectoredLatency> = Lazy::new(|| {
229 384 : let inner = register_histogram_vec!(
230 384 : "pageserver_get_vectored_seconds",
231 384 : "Time spent in get_vectored.",
232 384 : &["task_kind"],
233 384 : CRITICAL_OP_BUCKETS.into(),
234 384 : )
235 384 : .expect("failed to define a metric");
236 384 :
237 384 : GetVectoredLatency {
238 11904 : map: EnumMap::from_array(std::array::from_fn(|task_kind_idx| {
239 11904 : let task_kind = TaskKind::from_usize(task_kind_idx);
240 11904 :
241 11904 : if GetVectoredLatency::TRACKED_TASK_KINDS.contains(&task_kind) {
242 768 : let task_kind = task_kind.into();
243 768 : Some(inner.with_label_values(&[task_kind]))
244 : } else {
245 11136 : None
246 : }
247 11904 : })),
248 384 : }
249 384 : });
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 2328528 : pub(crate) fn for_ctx(&self, ctx: &RequestContext) -> &PageCacheMetricsForTaskKind {
327 2328528 : &self.map[ctx.task_kind()][ctx.page_content_kind()]
328 2328528 : }
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 63684 : 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 63684 : match outcome {
407 3248 : Outcome::FoundSlotUnused { iters } => dry!("found_empty", iters),
408 60436 : Outcome::FoundSlotEvicted { iters } => {
409 60436 : 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 63684 : }
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 400 : static FLUSH_WAIT_UPLOAD_TIME: Lazy<GaugeVec> = Lazy::new(|| {
452 400 : register_gauge_vec!(
453 400 : "pageserver_flush_wait_upload_seconds",
454 400 : "Time spent waiting for preceding uploads during layer flush",
455 400 : &["tenant_id", "shard_id", "timeline_id"]
456 400 : )
457 400 : .expect("failed to define a metric")
458 400 : });
459 :
460 400 : static LAST_RECORD_LSN: Lazy<IntGaugeVec> = Lazy::new(|| {
461 400 : register_int_gauge_vec!(
462 400 : "pageserver_last_record_lsn",
463 400 : "Last record LSN grouped by timeline",
464 400 : &["tenant_id", "shard_id", "timeline_id"]
465 400 : )
466 400 : .expect("failed to define a metric")
467 400 : });
468 :
469 400 : static DISK_CONSISTENT_LSN: Lazy<IntGaugeVec> = Lazy::new(|| {
470 400 : register_int_gauge_vec!(
471 400 : "pageserver_disk_consistent_lsn",
472 400 : "Disk consistent LSN grouped by timeline",
473 400 : &["tenant_id", "shard_id", "timeline_id"]
474 400 : )
475 400 : .expect("failed to define a metric")
476 400 : });
477 :
478 400 : pub(crate) static PROJECTED_REMOTE_CONSISTENT_LSN: Lazy<UIntGaugeVec> = Lazy::new(|| {
479 400 : register_uint_gauge_vec!(
480 400 : "pageserver_projected_remote_consistent_lsn",
481 400 : "Projected remote consistent LSN grouped by timeline",
482 400 : &["tenant_id", "shard_id", "timeline_id"]
483 400 : )
484 400 : .expect("failed to define a metric")
485 400 : });
486 :
487 400 : static PITR_HISTORY_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
488 400 : register_uint_gauge_vec!(
489 400 : "pageserver_pitr_history_size",
490 400 : "Data written since PITR cutoff on this timeline",
491 400 : &["tenant_id", "shard_id", "timeline_id"]
492 400 : )
493 400 : .expect("failed to define a metric")
494 400 : });
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 392 : static TIMELINE_LAYER_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
524 392 : register_uint_gauge_vec!(
525 392 : "pageserver_layer_bytes",
526 392 : "Sum of frozen, L0, and L1 layer physical sizes in bytes (excluding the open ephemeral layer)",
527 392 : &["tenant_id", "shard_id", "timeline_id", "level", "kind"]
528 392 : )
529 392 : .expect("failed to define a metric")
530 392 : });
531 :
532 392 : static TIMELINE_LAYER_COUNT: Lazy<UIntGaugeVec> = Lazy::new(|| {
533 392 : register_uint_gauge_vec!(
534 392 : "pageserver_layer_count",
535 392 : "Number of frozen, L0, and L1 layers (excluding the open ephemeral layer)",
536 392 : &["tenant_id", "shard_id", "timeline_id", "level", "kind"]
537 392 : )
538 392 : .expect("failed to define a metric")
539 392 : });
540 :
541 400 : static TIMELINE_ARCHIVE_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
542 400 : register_uint_gauge_vec!(
543 400 : "pageserver_archive_size",
544 400 : "Timeline's logical size if it is considered eligible for archival (outside PITR window), else zero",
545 400 : &["tenant_id", "shard_id", "timeline_id"]
546 400 : )
547 400 : .expect("failed to define a metric")
548 400 : });
549 :
550 400 : static STANDBY_HORIZON: Lazy<IntGaugeVec> = Lazy::new(|| {
551 400 : register_int_gauge_vec!(
552 400 : "pageserver_standby_horizon",
553 400 : "Standby apply LSN for which GC is hold off, by timeline.",
554 400 : &["tenant_id", "shard_id", "timeline_id"]
555 400 : )
556 400 : .expect("failed to define a metric")
557 400 : });
558 :
559 400 : static RESIDENT_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
560 400 : register_uint_gauge_vec!(
561 400 : "pageserver_resident_physical_size",
562 400 : "The size of the layer files present in the pageserver's filesystem, for attached locations.",
563 400 : &["tenant_id", "shard_id", "timeline_id"]
564 400 : )
565 400 : .expect("failed to define a metric")
566 400 : });
567 :
568 400 : static VISIBLE_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
569 400 : register_uint_gauge_vec!(
570 400 : "pageserver_visible_physical_size",
571 400 : "The size of the layer files present in the pageserver's filesystem.",
572 400 : &["tenant_id", "shard_id", "timeline_id"]
573 400 : )
574 400 : .expect("failed to define a metric")
575 400 : });
576 :
577 392 : pub(crate) static RESIDENT_PHYSICAL_SIZE_GLOBAL: Lazy<UIntGauge> = Lazy::new(|| {
578 392 : register_uint_gauge!(
579 392 : "pageserver_resident_physical_size_global",
580 392 : "Like `pageserver_resident_physical_size`, but without tenant/timeline dimensions."
581 392 : )
582 392 : .expect("failed to define a metric")
583 392 : });
584 :
585 400 : static REMOTE_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
586 400 : register_uint_gauge_vec!(
587 400 : "pageserver_remote_physical_size",
588 400 : "The size of the layer files present in the remote storage that are listed in the remote index_part.json.",
589 400 : // Corollary: If any files are missing from the index part, they won't be included here.
590 400 : &["tenant_id", "shard_id", "timeline_id"]
591 400 : )
592 400 : .expect("failed to define a metric")
593 400 : });
594 :
595 400 : static REMOTE_PHYSICAL_SIZE_GLOBAL: Lazy<UIntGauge> = Lazy::new(|| {
596 400 : register_uint_gauge!(
597 400 : "pageserver_remote_physical_size_global",
598 400 : "Like `pageserver_remote_physical_size`, but without tenant/timeline dimensions."
599 400 : )
600 400 : .expect("failed to define a metric")
601 400 : });
602 :
603 8 : pub(crate) static REMOTE_ONDEMAND_DOWNLOADED_LAYERS: Lazy<IntCounter> = Lazy::new(|| {
604 8 : register_int_counter!(
605 8 : "pageserver_remote_ondemand_downloaded_layers_total",
606 8 : "Total on-demand downloaded layers"
607 8 : )
608 8 : .unwrap()
609 8 : });
610 :
611 8 : pub(crate) static REMOTE_ONDEMAND_DOWNLOADED_BYTES: Lazy<IntCounter> = Lazy::new(|| {
612 8 : register_int_counter!(
613 8 : "pageserver_remote_ondemand_downloaded_bytes_total",
614 8 : "Total bytes of layers on-demand downloaded",
615 8 : )
616 8 : .unwrap()
617 8 : });
618 :
619 400 : static CURRENT_LOGICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
620 400 : register_uint_gauge_vec!(
621 400 : "pageserver_current_logical_size",
622 400 : "Current logical size grouped by timeline",
623 400 : &["tenant_id", "shard_id", "timeline_id"]
624 400 : )
625 400 : .expect("failed to define current logical size metric")
626 400 : });
627 :
628 400 : static AUX_FILE_SIZE: Lazy<IntGaugeVec> = Lazy::new(|| {
629 400 : register_int_gauge_vec!(
630 400 : "pageserver_aux_file_estimated_size",
631 400 : "The size of all aux files for a timeline in aux file v2 store.",
632 400 : &["tenant_id", "shard_id", "timeline_id"]
633 400 : )
634 400 : .expect("failed to define a metric")
635 400 : });
636 :
637 400 : static VALID_LSN_LEASE_COUNT: Lazy<UIntGaugeVec> = Lazy::new(|| {
638 400 : register_uint_gauge_vec!(
639 400 : "pageserver_valid_lsn_lease_count",
640 400 : "The number of valid leases after refreshing gc info.",
641 400 : &["tenant_id", "shard_id", "timeline_id"],
642 400 : )
643 400 : .expect("failed to define a metric")
644 400 : });
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 384 : pub(crate) static COMPRESSION_IMAGE_INPUT_BYTES: Lazy<IntCounter> = Lazy::new(|| {
663 384 : register_int_counter!(
664 384 : "pageserver_compression_image_in_bytes_total",
665 384 : "Size of data written into image layers before compression"
666 384 : )
667 384 : .expect("failed to define a metric")
668 384 : });
669 :
670 384 : pub(crate) static COMPRESSION_IMAGE_INPUT_BYTES_CONSIDERED: Lazy<IntCounter> = Lazy::new(|| {
671 384 : register_int_counter!(
672 384 : "pageserver_compression_image_in_bytes_considered",
673 384 : "Size of potentially compressible data written into image layers before compression"
674 384 : )
675 384 : .expect("failed to define a metric")
676 384 : });
677 :
678 384 : pub(crate) static COMPRESSION_IMAGE_INPUT_BYTES_CHOSEN: Lazy<IntCounter> = Lazy::new(|| {
679 384 : register_int_counter!(
680 384 : "pageserver_compression_image_in_bytes_chosen",
681 384 : "Size of data whose compressed form was written into image layers"
682 384 : )
683 384 : .expect("failed to define a metric")
684 384 : });
685 :
686 384 : pub(crate) static COMPRESSION_IMAGE_OUTPUT_BYTES: Lazy<IntCounter> = Lazy::new(|| {
687 384 : register_int_counter!(
688 384 : "pageserver_compression_image_out_bytes_total",
689 384 : "Size of compressed image layer written"
690 384 : )
691 384 : .expect("failed to define a metric")
692 384 : });
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 400 : pub(crate) static START_CALCULATION: Lazy<StartCalculation> = Lazy::new(|| {
729 400 : StartCalculation(
730 400 : register_int_counter_vec!(
731 400 : "pageserver_initial_logical_size_start_calculation",
732 400 : "Incremented each time we start an initial logical size calculation attempt. \
733 400 : The `circumstances` label provides some additional details.",
734 400 : &["attempt", "circumstances"]
735 400 : )
736 400 : .unwrap(),
737 400 : )
738 400 : });
739 :
740 : struct DropCalculation {
741 : first: IntCounter,
742 : retry: IntCounter,
743 : }
744 :
745 400 : static DROP_CALCULATION: Lazy<DropCalculation> = Lazy::new(|| {
746 400 : let vec = register_int_counter_vec!(
747 400 : "pageserver_initial_logical_size_drop_calculation",
748 400 : "Incremented each time we abort a started size calculation attmpt.",
749 400 : &["attempt"]
750 400 : )
751 400 : .unwrap();
752 400 : DropCalculation {
753 400 : first: vec.with_label_values(&["first"]),
754 400 : retry: vec.with_label_values(&["retry"]),
755 400 : }
756 400 : });
757 :
758 : pub(crate) struct Calculated {
759 : pub(crate) births: IntCounter,
760 : pub(crate) deaths: IntCounter,
761 : }
762 :
763 400 : pub(crate) static CALCULATED: Lazy<Calculated> = Lazy::new(|| Calculated {
764 400 : births: register_int_counter!(
765 400 : "pageserver_initial_logical_size_finish_calculation",
766 400 : "Incremented every time we finish calculation of initial logical size.\
767 400 : If everything is working well, this should happen at most once per Timeline object."
768 400 : )
769 400 : .unwrap(),
770 400 : deaths: register_int_counter!(
771 400 : "pageserver_initial_logical_size_drop_finished_calculation",
772 400 : "Incremented when we drop a finished initial logical size calculation result.\
773 400 : Mainly useful to turn pageserver_initial_logical_size_finish_calculation into a gauge."
774 400 : )
775 400 : .unwrap(),
776 400 : });
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 424 : pub(crate) fn first(&self, circumstances: StartCircumstances) -> OngoingCalculationGuard {
791 424 : let circumstances_label: &'static str = circumstances.into();
792 424 : self.0
793 424 : .with_label_values(&["first", circumstances_label])
794 424 : .inc();
795 424 : OngoingCalculationGuard {
796 424 : inc_drop_calculation: Some(DROP_CALCULATION.first.clone()),
797 424 : }
798 424 : }
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 424 : fn drop(&mut self) {
812 424 : if let Some(counter) = self.inc_drop_calculation.take() {
813 0 : counter.inc();
814 424 : }
815 424 : }
816 : }
817 :
818 : impl OngoingCalculationGuard {
819 424 : pub(crate) fn calculation_result_saved(mut self) -> FinishedCalculationGuard {
820 424 : drop(self.inc_drop_calculation.take());
821 424 : CALCULATED.births.inc();
822 424 : FinishedCalculationGuard {
823 424 : inc_on_drop: CALCULATED.deaths.clone(),
824 424 : }
825 424 : }
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 404 : pub(crate) static TENANT_STATE_METRIC: Lazy<UIntGaugeVec> = Lazy::new(|| {
860 404 : register_uint_gauge_vec!(
861 404 : "pageserver_tenant_states_count",
862 404 : "Count of tenants per state",
863 404 : &["state"]
864 404 : )
865 404 : .expect("Failed to register pageserver_tenant_states_count metric")
866 404 : });
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 400 : static EVICTIONS: Lazy<IntCounterVec> = Lazy::new(|| {
901 400 : register_int_counter_vec!(
902 400 : "pageserver_evictions",
903 400 : "Number of layers evicted from the pageserver",
904 400 : &["tenant_id", "shard_id", "timeline_id"]
905 400 : )
906 400 : .expect("failed to define a metric")
907 400 : });
908 :
909 400 : static EVICTIONS_WITH_LOW_RESIDENCE_DURATION: Lazy<IntCounterVec> = Lazy::new(|| {
910 400 : register_int_counter_vec!(
911 400 : "pageserver_evictions_with_low_residence_duration",
912 400 : "If a layer is evicted that was resident for less than `low_threshold`, it is counted to this counter. \
913 400 : Residence duration is determined using the `residence_duration_data_source`.",
914 400 : &["tenant_id", "shard_id", "timeline_id", "residence_duration_data_source", "low_threshold_secs"]
915 400 : )
916 400 : .expect("failed to define a metric")
917 400 : });
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 392 : pub(crate) static TIMELINE_EPHEMERAL_BYTES: Lazy<UIntGauge> = Lazy::new(|| {
948 392 : register_uint_gauge!(
949 392 : "pageserver_timeline_ephemeral_bytes",
950 392 : "Total number of bytes in ephemeral layers, summed for all timelines. Approximate, lazily updated."
951 392 : )
952 392 : .expect("Failed to register metric")
953 392 : });
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 892 : pub fn new(data_source: &'static str, threshold: Duration) -> Self {
1020 892 : Self {
1021 892 : data_source,
1022 892 : threshold,
1023 892 : }
1024 892 : }
1025 :
1026 892 : fn build(
1027 892 : &self,
1028 892 : tenant_id: &str,
1029 892 : shard_id: &str,
1030 892 : timeline_id: &str,
1031 892 : ) -> EvictionsWithLowResidenceDuration {
1032 892 : let counter = EVICTIONS_WITH_LOW_RESIDENCE_DURATION
1033 892 : .get_metric_with_label_values(&[
1034 892 : tenant_id,
1035 892 : shard_id,
1036 892 : timeline_id,
1037 892 : self.data_source,
1038 892 : &EvictionsWithLowResidenceDuration::threshold_label_value(self.threshold),
1039 892 : ])
1040 892 : .unwrap();
1041 892 : EvictionsWithLowResidenceDuration {
1042 892 : data_source: self.data_source,
1043 892 : threshold: self.threshold,
1044 892 : counter: Some(counter),
1045 892 : }
1046 892 : }
1047 : }
1048 :
1049 : impl EvictionsWithLowResidenceDuration {
1050 912 : fn threshold_label_value(threshold: Duration) -> String {
1051 912 : format!("{}", threshold.as_secs())
1052 912 : }
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 4140 : pub fn as_str(&self) -> &'static str {
1157 4140 : match self {
1158 460 : StorageIoOperation::Open => "open",
1159 460 : StorageIoOperation::OpenAfterReplace => "open-after-replace",
1160 460 : StorageIoOperation::Close => "close",
1161 460 : StorageIoOperation::CloseByReplace => "close-by-replace",
1162 460 : StorageIoOperation::Read => "read",
1163 460 : StorageIoOperation::Write => "write",
1164 460 : StorageIoOperation::Seek => "seek",
1165 460 : StorageIoOperation::Fsync => "fsync",
1166 460 : StorageIoOperation::Metadata => "metadata",
1167 : }
1168 4140 : }
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 460 : fn new() -> Self {
1179 460 : let storage_io_histogram_vec = register_histogram_vec!(
1180 460 : "pageserver_io_operations_seconds",
1181 460 : "Time spent in IO operations",
1182 460 : &["operation"],
1183 460 : STORAGE_IO_TIME_BUCKETS.into()
1184 460 : )
1185 460 : .expect("failed to define a metric");
1186 4140 : let metrics = std::array::from_fn(|i| {
1187 4140 : let op = StorageIoOperation::from_repr(i).unwrap();
1188 4140 : storage_io_histogram_vec
1189 4140 : .get_metric_with_label_values(&[op.as_str()])
1190 4140 : .unwrap()
1191 4140 : });
1192 460 : Self { metrics }
1193 460 : }
1194 :
1195 4063708 : pub(crate) fn get(&self, op: StorageIoOperation) -> &Histogram {
1196 4063708 : &self.metrics[op as usize]
1197 4063708 : }
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 452 : pub(crate) static STORAGE_IO_SIZE: Lazy<IntGaugeVec> = Lazy::new(|| {
1206 452 : register_int_gauge_vec!(
1207 452 : "pageserver_io_operations_bytes_total",
1208 452 : "Total amount of bytes read/written in IO operations",
1209 452 : &["operation", "tenant_id", "shard_id", "timeline_id"]
1210 452 : )
1211 452 : .expect("failed to define a metric")
1212 452 : });
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 400 : static SMGR_QUERY_STARTED_GLOBAL: Lazy<IntCounterVec> = Lazy::new(|| {
1513 400 : register_int_counter_vec!(
1514 400 : // it's a counter, but, name is prepared to extend it to a histogram of queue depth
1515 400 : "pageserver_smgr_query_started_global_count",
1516 400 : "Number of smgr queries started, aggregated by query type.",
1517 400 : &["smgr_query_type"],
1518 400 : )
1519 400 : .expect("failed to define a metric")
1520 400 : });
1521 :
1522 400 : static SMGR_QUERY_STARTED_PER_TENANT_TIMELINE: Lazy<IntCounterVec> = Lazy::new(|| {
1523 400 : register_int_counter_vec!(
1524 400 : // it's a counter, but, name is prepared to extend it to a histogram of queue depth
1525 400 : "pageserver_smgr_query_started_count",
1526 400 : "Number of smgr queries started, aggregated by query type and tenant/timeline.",
1527 400 : &["smgr_query_type", "tenant_id", "shard_id", "timeline_id"],
1528 400 : )
1529 400 : .expect("failed to define a metric")
1530 400 : });
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 400 : static SMGR_QUERY_TIME_PER_TENANT_TIMELINE: Lazy<HistogramVec> = Lazy::new(|| {
1536 400 : register_histogram_vec!(
1537 400 : "pageserver_smgr_query_seconds",
1538 400 : "Time spent _executing_ smgr query handling, excluding batch and throttle delays.",
1539 400 : &["smgr_query_type", "tenant_id", "shard_id", "timeline_id"],
1540 400 : SMGR_QUERY_TIME_PER_TENANT_TIMELINE_BUCKETS.into(),
1541 400 : )
1542 400 : .expect("failed to define a metric")
1543 400 : });
1544 :
1545 400 : static SMGR_QUERY_TIME_GLOBAL_BUCKETS: Lazy<Vec<f64>> = Lazy::new(|| {
1546 400 : [
1547 400 : 1,
1548 400 : 10,
1549 400 : 20,
1550 400 : 40,
1551 400 : 60,
1552 400 : 80,
1553 400 : 100,
1554 400 : 200,
1555 400 : 300,
1556 400 : 400,
1557 400 : 500,
1558 400 : 600,
1559 400 : 700,
1560 400 : 800,
1561 400 : 900,
1562 400 : 1_000, // 1ms
1563 400 : 2_000,
1564 400 : 4_000,
1565 400 : 6_000,
1566 400 : 8_000,
1567 400 : 10_000, // 10ms
1568 400 : 20_000,
1569 400 : 40_000,
1570 400 : 60_000,
1571 400 : 80_000,
1572 400 : 100_000,
1573 400 : 200_000,
1574 400 : 400_000,
1575 400 : 600_000,
1576 400 : 800_000,
1577 400 : 1_000_000, // 1s
1578 400 : 2_000_000,
1579 400 : 4_000_000,
1580 400 : 6_000_000,
1581 400 : 8_000_000,
1582 400 : 10_000_000, // 10s
1583 400 : 20_000_000,
1584 400 : 50_000_000,
1585 400 : 100_000_000,
1586 400 : 200_000_000,
1587 400 : 1_000_000_000, // 1000s
1588 400 : ]
1589 400 : .into_iter()
1590 400 : .map(Duration::from_micros)
1591 16400 : .map(|d| d.as_secs_f64())
1592 400 : .collect()
1593 400 : });
1594 :
1595 400 : static SMGR_QUERY_TIME_GLOBAL: Lazy<HistogramVec> = Lazy::new(|| {
1596 400 : register_histogram_vec!(
1597 400 : "pageserver_smgr_query_seconds_global",
1598 400 : "Like pageserver_smgr_query_seconds, but aggregated to instance level.",
1599 400 : &["smgr_query_type"],
1600 400 : SMGR_QUERY_TIME_GLOBAL_BUCKETS.clone(),
1601 400 : )
1602 400 : .expect("failed to define a metric")
1603 400 : });
1604 :
1605 400 : static PAGE_SERVICE_BATCH_SIZE_BUCKETS_GLOBAL: Lazy<Vec<f64>> = Lazy::new(|| {
1606 400 : (1..=u32::try_from(Timeline::MAX_GET_VECTORED_KEYS).unwrap())
1607 12800 : .map(|v| v.into())
1608 400 : .collect()
1609 400 : });
1610 :
1611 400 : static PAGE_SERVICE_BATCH_SIZE_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
1612 400 : register_histogram!(
1613 400 : "pageserver_page_service_batch_size_global",
1614 400 : "Batch size of pageserver page service requests",
1615 400 : PAGE_SERVICE_BATCH_SIZE_BUCKETS_GLOBAL.clone(),
1616 400 : )
1617 400 : .expect("failed to define a metric")
1618 400 : });
1619 :
1620 400 : static PAGE_SERVICE_BATCH_SIZE_BUCKETS_PER_TIMELINE: Lazy<Vec<f64>> = Lazy::new(|| {
1621 400 : let mut buckets = Vec::new();
1622 2800 : for i in 0.. {
1623 2800 : let bucket = 1 << i;
1624 2800 : if bucket > u32::try_from(Timeline::MAX_GET_VECTORED_KEYS).unwrap() {
1625 400 : break;
1626 2400 : }
1627 2400 : buckets.push(bucket.into());
1628 : }
1629 400 : buckets
1630 400 : });
1631 :
1632 400 : static PAGE_SERVICE_BATCH_SIZE_PER_TENANT_TIMELINE: Lazy<HistogramVec> = Lazy::new(|| {
1633 400 : register_histogram_vec!(
1634 400 : "pageserver_page_service_batch_size",
1635 400 : "Batch size of pageserver page service requests",
1636 400 : &["tenant_id", "shard_id", "timeline_id"],
1637 400 : PAGE_SERVICE_BATCH_SIZE_BUCKETS_PER_TIMELINE.clone()
1638 400 : )
1639 400 : .expect("failed to define a metric")
1640 400 : });
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 400 : static PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS: Lazy<IntCounterVec> = Lazy::new(|| {
1676 400 : register_int_counter_vec!(
1677 400 : "pageserver_page_service_pagestream_flush_in_progress_micros",
1678 400 : "Counter that sums up the microseconds that a pagestream response was being flushed into the TCP connection. \
1679 400 : If the flush is particularly slow, this counter will be updated periodically to make slow flushes \
1680 400 : easily discoverable in monitoring. \
1681 400 : Hence, this is NOT a completion latency historgram.",
1682 400 : &["tenant_id", "shard_id", "timeline_id"],
1683 400 : )
1684 400 : .expect("failed to define a metric")
1685 400 : });
1686 :
1687 400 : static PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS_GLOBAL: Lazy<IntCounter> = Lazy::new(|| {
1688 400 : register_int_counter!(
1689 400 : "pageserver_page_service_pagestream_flush_in_progress_micros_global",
1690 400 : "Like pageserver_page_service_pagestream_flush_in_progress_seconds, but instance-wide.",
1691 400 : )
1692 400 : .expect("failed to define a metric")
1693 400 : });
1694 :
1695 400 : static PAGE_SERVICE_SMGR_BATCH_WAIT_TIME: Lazy<HistogramVec> = Lazy::new(|| {
1696 400 : register_histogram_vec!(
1697 400 : "pageserver_page_service_pagestream_batch_wait_time_seconds",
1698 400 : "Time a request spent waiting in its batch until the batch moved to throttle&execution.",
1699 400 : &["tenant_id", "shard_id", "timeline_id"],
1700 400 : SMGR_QUERY_TIME_PER_TENANT_TIMELINE_BUCKETS.into(),
1701 400 : )
1702 400 : .expect("failed to define a metric")
1703 400 : });
1704 :
1705 400 : static PAGE_SERVICE_SMGR_BATCH_WAIT_TIME_GLOBAL: Lazy<Histogram> = Lazy::new(|| {
1706 400 : register_histogram!(
1707 400 : "pageserver_page_service_pagestream_batch_wait_time_seconds_global",
1708 400 : "Like pageserver_page_service_pagestream_batch_wait_time_seconds, but aggregated to instance level.",
1709 400 : SMGR_QUERY_TIME_GLOBAL_BUCKETS.to_vec(),
1710 400 : )
1711 400 : .expect("failed to define a metric")
1712 400 : });
1713 :
1714 : impl SmgrQueryTimePerTimeline {
1715 892 : pub(crate) fn new(
1716 892 : tenant_shard_id: &TenantShardId,
1717 892 : timeline_id: &TimelineId,
1718 892 : pagestream_throttle_metrics: Arc<tenant_throttling::Pagestream>,
1719 892 : ) -> Self {
1720 892 : let tenant_id = tenant_shard_id.tenant_id.to_string();
1721 892 : let shard_slug = format!("{}", tenant_shard_id.shard_slug());
1722 892 : let timeline_id = timeline_id.to_string();
1723 5352 : let global_started = std::array::from_fn(|i| {
1724 5352 : let op = SmgrQueryType::from_repr(i).unwrap();
1725 5352 : SMGR_QUERY_STARTED_GLOBAL
1726 5352 : .get_metric_with_label_values(&[op.into()])
1727 5352 : .unwrap()
1728 5352 : });
1729 5352 : let global_latency = std::array::from_fn(|i| {
1730 5352 : let op = SmgrQueryType::from_repr(i).unwrap();
1731 5352 : SMGR_QUERY_TIME_GLOBAL
1732 5352 : .get_metric_with_label_values(&[op.into()])
1733 5352 : .unwrap()
1734 5352 : });
1735 892 :
1736 892 : let per_timeline_getpage_started = SMGR_QUERY_STARTED_PER_TENANT_TIMELINE
1737 892 : .get_metric_with_label_values(&[
1738 892 : SmgrQueryType::GetPageAtLsn.into(),
1739 892 : &tenant_id,
1740 892 : &shard_slug,
1741 892 : &timeline_id,
1742 892 : ])
1743 892 : .unwrap();
1744 892 : let per_timeline_getpage_latency = SMGR_QUERY_TIME_PER_TENANT_TIMELINE
1745 892 : .get_metric_with_label_values(&[
1746 892 : SmgrQueryType::GetPageAtLsn.into(),
1747 892 : &tenant_id,
1748 892 : &shard_slug,
1749 892 : &timeline_id,
1750 892 : ])
1751 892 : .unwrap();
1752 892 :
1753 892 : let global_batch_size = PAGE_SERVICE_BATCH_SIZE_GLOBAL.clone();
1754 892 : let per_timeline_batch_size = PAGE_SERVICE_BATCH_SIZE_PER_TENANT_TIMELINE
1755 892 : .get_metric_with_label_values(&[&tenant_id, &shard_slug, &timeline_id])
1756 892 : .unwrap();
1757 892 :
1758 892 : let global_batch_wait_time = PAGE_SERVICE_SMGR_BATCH_WAIT_TIME_GLOBAL.clone();
1759 892 : let per_timeline_batch_wait_time = PAGE_SERVICE_SMGR_BATCH_WAIT_TIME
1760 892 : .get_metric_with_label_values(&[&tenant_id, &shard_slug, &timeline_id])
1761 892 : .unwrap();
1762 892 :
1763 892 : let global_flush_in_progress_micros =
1764 892 : PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS_GLOBAL.clone();
1765 892 : let per_timeline_flush_in_progress_micros = PAGE_SERVICE_SMGR_FLUSH_INPROGRESS_MICROS
1766 892 : .get_metric_with_label_values(&[&tenant_id, &shard_slug, &timeline_id])
1767 892 : .unwrap();
1768 892 :
1769 892 : Self {
1770 892 : global_started,
1771 892 : global_latency,
1772 892 : per_timeline_getpage_latency,
1773 892 : per_timeline_getpage_started,
1774 892 : global_batch_size,
1775 892 : per_timeline_batch_size,
1776 892 : global_flush_in_progress_micros,
1777 892 : per_timeline_flush_in_progress_micros,
1778 892 : global_batch_wait_time,
1779 892 : per_timeline_batch_wait_time,
1780 892 : throttling: pagestream_throttle_metrics,
1781 892 : }
1782 892 : }
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 392 : static REMOTE_TIMELINE_CLIENT_CALLS: Lazy<IntCounterPairVec> = Lazy::new(|| {
1928 392 : register_int_counter_pair_vec!(
1929 392 : "pageserver_remote_timeline_client_calls_started",
1930 392 : "Number of started calls to remote timeline client.",
1931 392 : "pageserver_remote_timeline_client_calls_finished",
1932 392 : "Number of finshed calls to remote timeline client.",
1933 392 : &[
1934 392 : "tenant_id",
1935 392 : "shard_id",
1936 392 : "timeline_id",
1937 392 : "file_kind",
1938 392 : "op_kind"
1939 392 : ],
1940 392 : )
1941 392 : .unwrap()
1942 392 : });
1943 :
1944 : static REMOTE_TIMELINE_CLIENT_BYTES_STARTED_COUNTER: Lazy<IntCounterVec> =
1945 388 : Lazy::new(|| {
1946 388 : register_int_counter_vec!(
1947 388 : "pageserver_remote_timeline_client_bytes_started",
1948 388 : "Incremented by the number of bytes associated with a remote timeline client operation. \
1949 388 : The increment happens when the operation is scheduled.",
1950 388 : &["tenant_id", "shard_id", "timeline_id", "file_kind", "op_kind"],
1951 388 : )
1952 388 : .expect("failed to define a metric")
1953 388 : });
1954 :
1955 388 : static REMOTE_TIMELINE_CLIENT_BYTES_FINISHED_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| {
1956 388 : register_int_counter_vec!(
1957 388 : "pageserver_remote_timeline_client_bytes_finished",
1958 388 : "Incremented by the number of bytes associated with a remote timeline client operation. \
1959 388 : The increment happens when the operation finishes (regardless of success/failure/shutdown).",
1960 388 : &["tenant_id", "shard_id", "timeline_id", "file_kind", "op_kind"],
1961 388 : )
1962 388 : .expect("failed to define a metric")
1963 388 : });
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 63 : pub(crate) static DELETION_QUEUE: Lazy<DeletionQueueMetrics> = Lazy::new(|| {
2052 63 : DeletionQueueMetrics{
2053 63 :
2054 63 : keys_submitted: register_int_counter!(
2055 63 : "pageserver_deletion_queue_submitted_total",
2056 63 : "Number of objects submitted for deletion"
2057 63 : )
2058 63 : .expect("failed to define a metric"),
2059 63 :
2060 63 : keys_dropped: register_int_counter!(
2061 63 : "pageserver_deletion_queue_dropped_total",
2062 63 : "Number of object deletions dropped due to stale generation."
2063 63 : )
2064 63 : .expect("failed to define a metric"),
2065 63 :
2066 63 : keys_executed: register_int_counter!(
2067 63 : "pageserver_deletion_queue_executed_total",
2068 63 : "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 63 : )
2070 63 : .expect("failed to define a metric"),
2071 63 :
2072 63 : keys_validated: register_int_counter!(
2073 63 : "pageserver_deletion_queue_validated_total",
2074 63 : "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 63 : )
2076 63 : .expect("failed to define a metric"),
2077 63 :
2078 63 : dropped_lsn_updates: register_int_counter!(
2079 63 : "pageserver_deletion_queue_dropped_lsn_updates_total",
2080 63 : "Updates to remote_consistent_lsn dropped due to stale generation number."
2081 63 : )
2082 63 : .expect("failed to define a metric"),
2083 63 : unexpected_errors: register_int_counter!(
2084 63 : "pageserver_deletion_queue_unexpected_errors_total",
2085 63 : "Number of unexpected condiions that may stall the queue: any value above zero is unexpected."
2086 63 : )
2087 63 : .expect("failed to define a metric"),
2088 63 : remote_errors: register_int_counter_vec!(
2089 63 : "pageserver_deletion_queue_remote_errors_total",
2090 63 : "Retryable remote I/O errors while executing deletions, for example 503 responses to DeleteObjects",
2091 63 : &["op_kind"],
2092 63 : )
2093 63 : .expect("failed to define a metric")
2094 63 : }
2095 63 : });
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 30350 : pub fn as_str(&self) -> &'static str {
2168 30350 : match self {
2169 28653 : Self::Upload => "upload",
2170 104 : Self::Download => "download",
2171 1593 : Self::Delete => "delete",
2172 : }
2173 30350 : }
2174 : }
2175 :
2176 : #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
2177 : pub enum RemoteOpFileKind {
2178 : Layer,
2179 : Index,
2180 : }
2181 : impl RemoteOpFileKind {
2182 30350 : pub fn as_str(&self) -> &'static str {
2183 30350 : match self {
2184 21177 : Self::Layer => "layer",
2185 9173 : Self::Index => "index",
2186 : }
2187 30350 : }
2188 : }
2189 :
2190 388 : pub(crate) static REMOTE_OPERATION_TIME: Lazy<HistogramVec> = Lazy::new(|| {
2191 388 : register_histogram_vec!(
2192 388 : "pageserver_remote_operation_seconds",
2193 388 : "Time spent on remote storage operations. \
2194 388 : Grouped by tenant, timeline, operation_kind and status. \
2195 388 : Does not account for time spent waiting in remote timeline client's queues.",
2196 388 : &["file_kind", "op_kind", "status"]
2197 388 : )
2198 388 : .expect("failed to define a metric")
2199 388 : });
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 400 : pub(crate) static PAGESERVER_TIMELINE_WAL_RECORDS_RECEIVED: Lazy<IntCounterVec> = Lazy::new(|| {
2509 400 : register_int_counter_vec!(
2510 400 : "pageserver_timeline_wal_records_received",
2511 400 : "Number of WAL records received per shard",
2512 400 : &["tenant_id", "shard_id", "timeline_id"]
2513 400 : )
2514 400 : .expect("failed to define a metric")
2515 400 : });
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 4316 : fn new(metrics: StorageTimeMetrics) -> Self {
2634 4316 : Self {
2635 4316 : metrics,
2636 4316 : start: Instant::now(),
2637 4316 : }
2638 4316 : }
2639 :
2640 : /// Returns the elapsed duration of the timer.
2641 4316 : pub fn elapsed(&self) -> Duration {
2642 4316 : self.start.elapsed()
2643 4316 : }
2644 :
2645 : /// Record the time from creation to now and return it.
2646 4316 : pub fn stop_and_record(self) -> Duration {
2647 4316 : let duration = self.elapsed();
2648 4316 : let seconds = duration.as_secs_f64();
2649 4316 : self.metrics.timeline_sum.inc_by(seconds);
2650 4316 : self.metrics.timeline_count.inc();
2651 4316 : self.metrics.global_histogram.observe(seconds);
2652 4316 : duration
2653 4316 : }
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 8028 : pub fn new(
2693 8028 : operation: StorageTimeOperation,
2694 8028 : tenant_id: &str,
2695 8028 : shard_id: &str,
2696 8028 : timeline_id: &str,
2697 8028 : ) -> Self {
2698 8028 : let operation: &'static str = operation.into();
2699 8028 :
2700 8028 : let timeline_sum = STORAGE_TIME_SUM_PER_TIMELINE
2701 8028 : .get_metric_with_label_values(&[operation, tenant_id, shard_id, timeline_id])
2702 8028 : .unwrap();
2703 8028 : let timeline_count = STORAGE_TIME_COUNT_PER_TIMELINE
2704 8028 : .get_metric_with_label_values(&[operation, tenant_id, shard_id, timeline_id])
2705 8028 : .unwrap();
2706 8028 : let global_histogram = STORAGE_TIME_GLOBAL
2707 8028 : .get_metric_with_label_values(&[operation])
2708 8028 : .unwrap();
2709 8028 :
2710 8028 : StorageTimeMetrics {
2711 8028 : timeline_sum,
2712 8028 : timeline_count,
2713 8028 : global_histogram,
2714 8028 : }
2715 8028 : }
2716 :
2717 : /// Starts timing a new operation.
2718 : ///
2719 : /// Note: unlike `prometheus::HistogramTimer` the returned timer does not record on drop.
2720 4316 : pub fn start_timer(&self) -> StorageTimeMetricsTimer {
2721 4316 : StorageTimeMetricsTimer::new(self.clone())
2722 4316 : }
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 892 : pub fn new(
2762 892 : tenant_shard_id: &TenantShardId,
2763 892 : timeline_id_raw: &TimelineId,
2764 892 : evictions_with_low_residence_duration_builder: EvictionsWithLowResidenceDurationBuilder,
2765 892 : ) -> Self {
2766 892 : let tenant_id = tenant_shard_id.tenant_id.to_string();
2767 892 : let shard_id = format!("{}", tenant_shard_id.shard_slug());
2768 892 : let timeline_id = timeline_id_raw.to_string();
2769 892 : let flush_time_histo = StorageTimeMetrics::new(
2770 892 : StorageTimeOperation::LayerFlush,
2771 892 : &tenant_id,
2772 892 : &shard_id,
2773 892 : &timeline_id,
2774 892 : );
2775 892 : let flush_delay_histo = StorageTimeMetrics::new(
2776 892 : StorageTimeOperation::LayerFlushDelay,
2777 892 : &tenant_id,
2778 892 : &shard_id,
2779 892 : &timeline_id,
2780 892 : );
2781 892 : let flush_wait_upload_time_gauge = FLUSH_WAIT_UPLOAD_TIME
2782 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2783 892 : .unwrap();
2784 892 : let compact_time_histo = StorageTimeMetrics::new(
2785 892 : StorageTimeOperation::Compact,
2786 892 : &tenant_id,
2787 892 : &shard_id,
2788 892 : &timeline_id,
2789 892 : );
2790 892 : let create_images_time_histo = StorageTimeMetrics::new(
2791 892 : StorageTimeOperation::CreateImages,
2792 892 : &tenant_id,
2793 892 : &shard_id,
2794 892 : &timeline_id,
2795 892 : );
2796 892 : let logical_size_histo = StorageTimeMetrics::new(
2797 892 : StorageTimeOperation::LogicalSize,
2798 892 : &tenant_id,
2799 892 : &shard_id,
2800 892 : &timeline_id,
2801 892 : );
2802 892 : let imitate_logical_size_histo = StorageTimeMetrics::new(
2803 892 : StorageTimeOperation::ImitateLogicalSize,
2804 892 : &tenant_id,
2805 892 : &shard_id,
2806 892 : &timeline_id,
2807 892 : );
2808 892 : let load_layer_map_histo = StorageTimeMetrics::new(
2809 892 : StorageTimeOperation::LoadLayerMap,
2810 892 : &tenant_id,
2811 892 : &shard_id,
2812 892 : &timeline_id,
2813 892 : );
2814 892 : let garbage_collect_histo = StorageTimeMetrics::new(
2815 892 : StorageTimeOperation::Gc,
2816 892 : &tenant_id,
2817 892 : &shard_id,
2818 892 : &timeline_id,
2819 892 : );
2820 892 : let find_gc_cutoffs_histo = StorageTimeMetrics::new(
2821 892 : StorageTimeOperation::FindGcCutoffs,
2822 892 : &tenant_id,
2823 892 : &shard_id,
2824 892 : &timeline_id,
2825 892 : );
2826 892 : let last_record_lsn_gauge = LAST_RECORD_LSN
2827 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2828 892 : .unwrap();
2829 892 :
2830 892 : let disk_consistent_lsn_gauge = DISK_CONSISTENT_LSN
2831 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2832 892 : .unwrap();
2833 892 :
2834 892 : let pitr_history_size = PITR_HISTORY_SIZE
2835 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2836 892 : .unwrap();
2837 892 :
2838 892 : let archival_size = TIMELINE_ARCHIVE_SIZE
2839 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2840 892 : .unwrap();
2841 892 :
2842 892 : let layers_per_read = LAYERS_PER_READ
2843 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2844 892 : .unwrap();
2845 892 :
2846 892 : let standby_horizon_gauge = STANDBY_HORIZON
2847 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2848 892 : .unwrap();
2849 892 : let resident_physical_size_gauge = RESIDENT_PHYSICAL_SIZE
2850 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2851 892 : .unwrap();
2852 892 : let visible_physical_size_gauge = VISIBLE_PHYSICAL_SIZE
2853 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2854 892 : .unwrap();
2855 892 : // TODO: we shouldn't expose this metric
2856 892 : let current_logical_size_gauge = CURRENT_LOGICAL_SIZE
2857 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2858 892 : .unwrap();
2859 892 : let aux_file_size_gauge = AUX_FILE_SIZE
2860 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2861 892 : .unwrap();
2862 892 : // TODO use impl Trait syntax here once we have ability to use it: https://github.com/rust-lang/rust/issues/63065
2863 892 : let directory_entries_count_gauge_closure = {
2864 892 : let tenant_shard_id = *tenant_shard_id;
2865 892 : 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 892 : let directory_entries_count_gauge: Lazy<UIntGauge, Box<dyn Send + Fn() -> UIntGauge>> =
2877 892 : Lazy::new(Box::new(directory_entries_count_gauge_closure));
2878 892 : let evictions = EVICTIONS
2879 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2880 892 : .unwrap();
2881 892 : let evictions_with_low_residence_duration = evictions_with_low_residence_duration_builder
2882 892 : .build(&tenant_id, &shard_id, &timeline_id);
2883 892 :
2884 892 : let valid_lsn_lease_count_gauge = VALID_LSN_LEASE_COUNT
2885 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2886 892 : .unwrap();
2887 892 :
2888 892 : let wal_records_received = PAGESERVER_TIMELINE_WAL_RECORDS_RECEIVED
2889 892 : .get_metric_with_label_values(&[&tenant_id, &shard_id, &timeline_id])
2890 892 : .unwrap();
2891 892 :
2892 892 : TimelineMetrics {
2893 892 : tenant_id,
2894 892 : shard_id,
2895 892 : timeline_id,
2896 892 : flush_time_histo,
2897 892 : flush_delay_histo,
2898 892 : flush_wait_upload_time_gauge,
2899 892 : compact_time_histo,
2900 892 : create_images_time_histo,
2901 892 : logical_size_histo,
2902 892 : imitate_logical_size_histo,
2903 892 : garbage_collect_histo,
2904 892 : find_gc_cutoffs_histo,
2905 892 : load_layer_map_histo,
2906 892 : last_record_lsn_gauge,
2907 892 : disk_consistent_lsn_gauge,
2908 892 : pitr_history_size,
2909 892 : archival_size,
2910 892 : layers_per_read,
2911 892 : standby_horizon_gauge,
2912 892 : resident_physical_size_gauge,
2913 892 : visible_physical_size_gauge,
2914 892 : current_logical_size_gauge,
2915 892 : aux_file_size_gauge,
2916 892 : directory_entries_count_gauge,
2917 892 : evictions,
2918 892 : evictions_with_low_residence_duration: std::sync::RwLock::new(
2919 892 : evictions_with_low_residence_duration,
2920 892 : ),
2921 892 : valid_lsn_lease_count_gauge,
2922 892 : wal_records_received,
2923 892 : shutdown: std::sync::atomic::AtomicBool::default(),
2924 892 : }
2925 892 : }
2926 :
2927 3168 : pub(crate) fn record_new_file_metrics(&self, sz: u64) {
2928 3168 : self.resident_physical_size_add(sz);
2929 3168 : }
2930 :
2931 1049 : pub(crate) fn resident_physical_size_sub(&self, sz: u64) {
2932 1049 : self.resident_physical_size_gauge.sub(sz);
2933 1049 : crate::metrics::RESIDENT_PHYSICAL_SIZE_GLOBAL.sub(sz);
2934 1049 : }
2935 :
2936 3424 : pub(crate) fn resident_physical_size_add(&self, sz: u64) {
2937 3424 : self.resident_physical_size_gauge.add(sz);
2938 3424 : crate::metrics::RESIDENT_PHYSICAL_SIZE_GLOBAL.add(sz);
2939 3424 : }
2940 :
2941 20 : pub(crate) fn resident_physical_size_get(&self) -> u64 {
2942 20 : self.resident_physical_size_gauge.get()
2943 20 : }
2944 :
2945 2344 : pub(crate) fn flush_wait_upload_time_gauge_add(&self, duration: f64) {
2946 2344 : self.flush_wait_upload_time_gauge.add(duration);
2947 2344 : crate::metrics::FLUSH_WAIT_UPLOAD_TIME
2948 2344 : .get_metric_with_label_values(&[&self.tenant_id, &self.shard_id, &self.timeline_id])
2949 2344 : .unwrap()
2950 2344 : .add(duration);
2951 2344 : }
2952 :
2953 : /// Generates TIMELINE_LAYER labels for a persistent layer.
2954 5234 : fn make_layer_labels(&self, layer_desc: &PersistentLayerDesc) -> [&str; 5] {
2955 5234 : let level = match LayerMap::is_l0(&layer_desc.key_range, layer_desc.is_delta()) {
2956 2844 : true => LayerLevel::L0,
2957 2390 : false => LayerLevel::L1,
2958 : };
2959 5234 : let kind = match layer_desc.is_delta() {
2960 4391 : true => LayerKind::Delta,
2961 843 : false => LayerKind::Image,
2962 : };
2963 5234 : [
2964 5234 : &self.tenant_id,
2965 5234 : &self.shard_id,
2966 5234 : &self.timeline_id,
2967 5234 : level.into(),
2968 5234 : kind.into(),
2969 5234 : ]
2970 5234 : }
2971 :
2972 : /// Generates TIMELINE_LAYER labels for a frozen ephemeral layer.
2973 4688 : fn make_frozen_layer_labels(&self, _layer: &InMemoryLayer) -> [&str; 5] {
2974 4688 : [
2975 4688 : &self.tenant_id,
2976 4688 : &self.shard_id,
2977 4688 : &self.timeline_id,
2978 4688 : LayerLevel::Frozen.into(),
2979 4688 : LayerKind::Delta.into(), // by definition
2980 4688 : ]
2981 4688 : }
2982 :
2983 : /// Removes a frozen ephemeral layer to TIMELINE_LAYER metrics.
2984 2344 : pub fn dec_frozen_layer(&self, layer: &InMemoryLayer) {
2985 2344 : assert!(matches!(layer.info(), InMemoryLayerInfo::Frozen { .. }));
2986 2344 : let labels = self.make_frozen_layer_labels(layer);
2987 2344 : let size = layer.try_len().expect("frozen layer should have no writer");
2988 2344 : TIMELINE_LAYER_COUNT
2989 2344 : .get_metric_with_label_values(&labels)
2990 2344 : .unwrap()
2991 2344 : .dec();
2992 2344 : TIMELINE_LAYER_SIZE
2993 2344 : .get_metric_with_label_values(&labels)
2994 2344 : .unwrap()
2995 2344 : .sub(size);
2996 2344 : }
2997 :
2998 : /// Adds a frozen ephemeral layer to TIMELINE_LAYER metrics.
2999 2344 : pub fn inc_frozen_layer(&self, layer: &InMemoryLayer) {
3000 2344 : assert!(matches!(layer.info(), InMemoryLayerInfo::Frozen { .. }));
3001 2344 : let labels = self.make_frozen_layer_labels(layer);
3002 2344 : let size = layer.try_len().expect("frozen layer should have no writer");
3003 2344 : TIMELINE_LAYER_COUNT
3004 2344 : .get_metric_with_label_values(&labels)
3005 2344 : .unwrap()
3006 2344 : .inc();
3007 2344 : TIMELINE_LAYER_SIZE
3008 2344 : .get_metric_with_label_values(&labels)
3009 2344 : .unwrap()
3010 2344 : .add(size);
3011 2344 : }
3012 :
3013 : /// Removes a persistent layer from TIMELINE_LAYER metrics.
3014 1378 : pub fn dec_layer(&self, layer_desc: &PersistentLayerDesc) {
3015 1378 : let labels = self.make_layer_labels(layer_desc);
3016 1378 : TIMELINE_LAYER_COUNT
3017 1378 : .get_metric_with_label_values(&labels)
3018 1378 : .unwrap()
3019 1378 : .dec();
3020 1378 : TIMELINE_LAYER_SIZE
3021 1378 : .get_metric_with_label_values(&labels)
3022 1378 : .unwrap()
3023 1378 : .sub(layer_desc.file_size);
3024 1378 : }
3025 :
3026 : /// Adds a persistent layer to TIMELINE_LAYER metrics.
3027 3856 : pub fn inc_layer(&self, layer_desc: &PersistentLayerDesc) {
3028 3856 : let labels = self.make_layer_labels(layer_desc);
3029 3856 : TIMELINE_LAYER_COUNT
3030 3856 : .get_metric_with_label_values(&labels)
3031 3856 : .unwrap()
3032 3856 : .inc();
3033 3856 : TIMELINE_LAYER_SIZE
3034 3856 : .get_metric_with_label_values(&labels)
3035 3856 : .unwrap()
3036 3856 : .add(layer_desc.file_size);
3037 3856 : }
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 912 : fn new(per_timeline_gauge: UIntGauge) -> Self {
3170 912 : Self {
3171 912 : last_set: AtomicU64::new(0),
3172 912 : gauge: per_timeline_gauge,
3173 912 : }
3174 912 : }
3175 3881 : pub(crate) fn set(&self, sz: u64) {
3176 3881 : self.gauge.set(sz);
3177 3881 : let prev = self.last_set.swap(sz, std::sync::atomic::Ordering::Relaxed);
3178 3881 : if sz < prev {
3179 66 : REMOTE_PHYSICAL_SIZE_GLOBAL.sub(prev - sz);
3180 3815 : } else {
3181 3815 : REMOTE_PHYSICAL_SIZE_GLOBAL.add(sz - prev);
3182 3815 : };
3183 3881 : }
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 912 : pub fn new(tenant_shard_id: &TenantShardId, timeline_id: &TimelineId) -> Self {
3208 912 : let tenant_id_str = tenant_shard_id.tenant_id.to_string();
3209 912 : let shard_id_str = format!("{}", tenant_shard_id.shard_slug());
3210 912 : let timeline_id_str = timeline_id.to_string();
3211 912 :
3212 912 : let remote_physical_size_gauge = PerTimelineRemotePhysicalSizeGauge::new(
3213 912 : REMOTE_PHYSICAL_SIZE
3214 912 : .get_metric_with_label_values(&[&tenant_id_str, &shard_id_str, &timeline_id_str])
3215 912 : .unwrap(),
3216 912 : );
3217 912 :
3218 912 : let projected_remote_consistent_lsn_gauge = PROJECTED_REMOTE_CONSISTENT_LSN
3219 912 : .get_metric_with_label_values(&[&tenant_id_str, &shard_id_str, &timeline_id_str])
3220 912 : .unwrap();
3221 912 :
3222 912 : RemoteTimelineClientMetrics {
3223 912 : tenant_id: tenant_id_str,
3224 912 : shard_id: shard_id_str,
3225 912 : timeline_id: timeline_id_str,
3226 912 : calls: Mutex::new(HashMap::default()),
3227 912 : bytes_started_counter: Mutex::new(HashMap::default()),
3228 912 : bytes_finished_counter: Mutex::new(HashMap::default()),
3229 912 : remote_physical_size_gauge,
3230 912 : projected_remote_consistent_lsn_gauge,
3231 912 : }
3232 912 : }
3233 :
3234 6073 : pub fn remote_operation_time(
3235 6073 : &self,
3236 6073 : file_kind: &RemoteOpFileKind,
3237 6073 : op_kind: &RemoteOpKind,
3238 6073 : status: &'static str,
3239 6073 : ) -> Histogram {
3240 6073 : let key = (file_kind.as_str(), op_kind.as_str(), status);
3241 6073 : REMOTE_OPERATION_TIME
3242 6073 : .get_metric_with_label_values(&[key.0, key.1, key.2])
3243 6073 : .unwrap()
3244 6073 : }
3245 :
3246 14261 : fn calls_counter_pair(
3247 14261 : &self,
3248 14261 : file_kind: &RemoteOpFileKind,
3249 14261 : op_kind: &RemoteOpKind,
3250 14261 : ) -> IntCounterPair {
3251 14261 : let mut guard = self.calls.lock().unwrap();
3252 14261 : let key = (file_kind.as_str(), op_kind.as_str());
3253 14261 : let metric = guard.entry(key).or_insert_with(move || {
3254 1634 : REMOTE_TIMELINE_CLIENT_CALLS
3255 1634 : .get_metric_with_label_values(&[
3256 1634 : &self.tenant_id,
3257 1634 : &self.shard_id,
3258 1634 : &self.timeline_id,
3259 1634 : key.0,
3260 1634 : key.1,
3261 1634 : ])
3262 1634 : .unwrap()
3263 14261 : });
3264 14261 : metric.clone()
3265 14261 : }
3266 :
3267 3480 : fn bytes_started_counter(
3268 3480 : &self,
3269 3480 : file_kind: &RemoteOpFileKind,
3270 3480 : op_kind: &RemoteOpKind,
3271 3480 : ) -> IntCounter {
3272 3480 : let mut guard = self.bytes_started_counter.lock().unwrap();
3273 3480 : let key = (file_kind.as_str(), op_kind.as_str());
3274 3480 : let metric = guard.entry(key).or_insert_with(move || {
3275 644 : REMOTE_TIMELINE_CLIENT_BYTES_STARTED_COUNTER
3276 644 : .get_metric_with_label_values(&[
3277 644 : &self.tenant_id,
3278 644 : &self.shard_id,
3279 644 : &self.timeline_id,
3280 644 : key.0,
3281 644 : key.1,
3282 644 : ])
3283 644 : .unwrap()
3284 3480 : });
3285 3480 : metric.clone()
3286 3480 : }
3287 :
3288 6512 : fn bytes_finished_counter(
3289 6512 : &self,
3290 6512 : file_kind: &RemoteOpFileKind,
3291 6512 : op_kind: &RemoteOpKind,
3292 6512 : ) -> IntCounter {
3293 6512 : let mut guard = self.bytes_finished_counter.lock().unwrap();
3294 6512 : let key = (file_kind.as_str(), op_kind.as_str());
3295 6512 : let metric = guard.entry(key).or_insert_with(move || {
3296 644 : REMOTE_TIMELINE_CLIENT_BYTES_FINISHED_COUNTER
3297 644 : .get_metric_with_label_values(&[
3298 644 : &self.tenant_id,
3299 644 : &self.shard_id,
3300 644 : &self.timeline_id,
3301 644 : key.0,
3302 644 : key.1,
3303 644 : ])
3304 644 : .unwrap()
3305 6512 : });
3306 6512 : metric.clone()
3307 6512 : }
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 7609 : pub fn will_decrement_manually(mut self) {
3346 7609 : let RemoteTimelineClientCallMetricGuard {
3347 7609 : calls_counter_pair,
3348 7609 : bytes_finished,
3349 7609 : } = &mut self;
3350 7609 : calls_counter_pair.take();
3351 7609 : bytes_finished.take();
3352 7609 : }
3353 : }
3354 :
3355 : impl Drop for RemoteTimelineClientCallMetricGuard {
3356 7661 : fn drop(&mut self) {
3357 7661 : let RemoteTimelineClientCallMetricGuard {
3358 7661 : calls_counter_pair,
3359 7661 : bytes_finished,
3360 7661 : } = self;
3361 7661 : if let Some(guard) = calls_counter_pair.take() {
3362 52 : guard.dec();
3363 7609 : }
3364 7661 : if let Some((bytes_finished_metric, value)) = bytes_finished {
3365 0 : bytes_finished_metric.inc_by(*value);
3366 7661 : }
3367 7661 : }
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 7661 : pub(crate) fn call_begin(
3389 7661 : &self,
3390 7661 : file_kind: &RemoteOpFileKind,
3391 7661 : op_kind: &RemoteOpKind,
3392 7661 : size: RemoteTimelineClientMetricsCallTrackSize,
3393 7661 : ) -> RemoteTimelineClientCallMetricGuard {
3394 7661 : let calls_counter_pair = self.calls_counter_pair(file_kind, op_kind);
3395 7661 : calls_counter_pair.inc();
3396 :
3397 7661 : let bytes_finished = match size {
3398 4181 : RemoteTimelineClientMetricsCallTrackSize::DontTrackSize { reason: _reason } => {
3399 4181 : // nothing to do
3400 4181 : None
3401 : }
3402 3480 : RemoteTimelineClientMetricsCallTrackSize::Bytes(size) => {
3403 3480 : self.bytes_started_counter(file_kind, op_kind).inc_by(size);
3404 3480 : let finished_counter = self.bytes_finished_counter(file_kind, op_kind);
3405 3480 : Some((finished_counter, size))
3406 : }
3407 : };
3408 7661 : RemoteTimelineClientCallMetricGuard {
3409 7661 : calls_counter_pair: Some(calls_counter_pair),
3410 7661 : bytes_finished,
3411 7661 : }
3412 7661 : }
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 6600 : pub(crate) fn call_end(
3418 6600 : &self,
3419 6600 : file_kind: &RemoteOpFileKind,
3420 6600 : op_kind: &RemoteOpKind,
3421 6600 : size: RemoteTimelineClientMetricsCallTrackSize,
3422 6600 : ) {
3423 6600 : let calls_counter_pair = self.calls_counter_pair(file_kind, op_kind);
3424 6600 : calls_counter_pair.dec();
3425 6600 : match size {
3426 3568 : RemoteTimelineClientMetricsCallTrackSize::DontTrackSize { reason: _reason } => {}
3427 3032 : RemoteTimelineClientMetricsCallTrackSize::Bytes(size) => {
3428 3032 : self.bytes_finished_counter(file_kind, op_kind).inc_by(size);
3429 3032 : }
3430 : }
3431 6600 : }
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 6405 : fn measure_remote_op(
3489 6405 : self,
3490 6405 : file_kind: RemoteOpFileKind,
3491 6405 : op: RemoteOpKind,
3492 6405 : metrics: Arc<RemoteTimelineClientMetrics>,
3493 6405 : ) -> MeasuredRemoteOp<Self> {
3494 6405 : let start = Instant::now();
3495 6405 : MeasuredRemoteOp {
3496 6405 : inner: self,
3497 6405 : file_kind,
3498 6405 : op,
3499 6405 : start,
3500 6405 : metrics,
3501 6405 : }
3502 6405 : }
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 97411 : fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3523 97411 : let this = self.project();
3524 97411 : let poll_result = this.inner.poll(cx);
3525 97411 : if let Poll::Ready(ref res) = poll_result {
3526 6073 : let duration = this.start.elapsed();
3527 6073 : let status = if res.is_ok() { &"success" } else { &"failure" };
3528 6073 : this.metrics
3529 6073 : .remote_operation_time(this.file_kind, this.op, status)
3530 6073 : .observe(duration.as_secs_f64());
3531 91338 : }
3532 97411 : poll_result
3533 97411 : }
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 232 : Lazy::new(|| {
3548 232 : let slots_submission_queue_depth = register_histogram!(
3549 232 : "pageserver_tokio_epoll_uring_slots_submission_queue_depth",
3550 232 : "The slots waiters queue depth of each tokio_epoll_uring system",
3551 232 : vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0],
3552 232 : )
3553 232 : .expect("failed to define a metric");
3554 232 : ThreadLocalMetricsStorage {
3555 232 : observers: Mutex::new(HashMap::new()),
3556 232 : slots_submission_queue_depth,
3557 232 : }
3558 232 : });
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 995 : pub fn register_system(&self, id: u64) -> Arc<ThreadLocalMetrics> {
3585 995 : let per_system_metrics = Arc::new(ThreadLocalMetrics::new(
3586 995 : self.slots_submission_queue_depth.local(),
3587 995 : ));
3588 995 : let mut g = self.observers.lock().unwrap();
3589 995 : g.insert(id, Arc::clone(&per_system_metrics));
3590 995 : per_system_metrics
3591 995 : }
3592 :
3593 : /// Removes metrics observer for a thread local system.
3594 : /// This should be called before dropping a thread local system.
3595 232 : pub fn remove_system(&self, id: u64) {
3596 232 : let mut g = self.observers.lock().unwrap();
3597 232 : g.remove(&id);
3598 232 : }
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 995 : pub fn new(slots_submission_queue_depth: LocalHistogram) -> Self {
3611 995 : ThreadLocalMetrics {
3612 995 : slots_submission_queue_depth: Mutex::new(slots_submission_queue_depth),
3613 995 : }
3614 995 : }
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 1833950 : fn observe_slots_submission_queue_depth(&self, queue_depth: u64) {
3627 1833950 : let Self {
3628 1833950 : slots_submission_queue_depth,
3629 1833950 : } = self;
3630 1833950 : slots_submission_queue_depth
3631 1833950 : .lock()
3632 1833950 : .unwrap()
3633 1833950 : .observe(queue_depth as f64);
3634 1833950 : }
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 232 : pub(crate) static THREAD_LOCAL_LAUNCH_SUCCESSES: Lazy<metrics::IntCounter> = Lazy::new(|| {
3711 232 : register_int_counter!(
3712 232 : "pageserver_tokio_epoll_uring_pageserver_thread_local_launch_success_count",
3713 232 : "Number of times where thread_local_system creation spanned multiple executor threads",
3714 232 : )
3715 232 : .unwrap()
3716 232 : });
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 404 : static COUNT_ACCOUNTED_START: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3757 404 : register_int_counter_vec!(
3758 404 : "pageserver_tenant_throttling_count_accounted_start_global",
3759 404 : "Count of tenant throttling starts, by kind of throttle.",
3760 404 : &["kind"]
3761 404 : )
3762 404 : .unwrap()
3763 404 : });
3764 404 : static COUNT_ACCOUNTED_START_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3765 404 : register_int_counter_vec!(
3766 404 : "pageserver_tenant_throttling_count_accounted_start",
3767 404 : "Count of tenant throttling starts, by kind of throttle.",
3768 404 : &["kind", "tenant_id", "shard_id"]
3769 404 : )
3770 404 : .unwrap()
3771 404 : });
3772 404 : static COUNT_ACCOUNTED_FINISH: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3773 404 : register_int_counter_vec!(
3774 404 : "pageserver_tenant_throttling_count_accounted_finish_global",
3775 404 : "Count of tenant throttling finishes, by kind of throttle.",
3776 404 : &["kind"]
3777 404 : )
3778 404 : .unwrap()
3779 404 : });
3780 404 : static COUNT_ACCOUNTED_FINISH_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3781 404 : register_int_counter_vec!(
3782 404 : "pageserver_tenant_throttling_count_accounted_finish",
3783 404 : "Count of tenant throttling finishes, by kind of throttle.",
3784 404 : &["kind", "tenant_id", "shard_id"]
3785 404 : )
3786 404 : .unwrap()
3787 404 : });
3788 404 : static WAIT_USECS: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3789 404 : register_int_counter_vec!(
3790 404 : "pageserver_tenant_throttling_wait_usecs_sum_global",
3791 404 : "Sum of microseconds that spent waiting throttle by kind of throttle.",
3792 404 : &["kind"]
3793 404 : )
3794 404 : .unwrap()
3795 404 : });
3796 404 : static WAIT_USECS_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3797 404 : register_int_counter_vec!(
3798 404 : "pageserver_tenant_throttling_wait_usecs_sum",
3799 404 : "Sum of microseconds that spent waiting throttle by kind of throttle.",
3800 404 : &["kind", "tenant_id", "shard_id"]
3801 404 : )
3802 404 : .unwrap()
3803 404 : });
3804 :
3805 404 : static WAIT_COUNT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3806 404 : register_int_counter_vec!(
3807 404 : "pageserver_tenant_throttling_count_global",
3808 404 : "Count of tenant throttlings, by kind of throttle.",
3809 404 : &["kind"]
3810 404 : )
3811 404 : .unwrap()
3812 404 : });
3813 404 : static WAIT_COUNT_PER_TENANT: Lazy<metrics::IntCounterVec> = Lazy::new(|| {
3814 404 : register_int_counter_vec!(
3815 404 : "pageserver_tenant_throttling_count",
3816 404 : "Count of tenant throttlings, by kind of throttle.",
3817 404 : &["kind", "tenant_id", "shard_id"]
3818 404 : )
3819 404 : .unwrap()
3820 404 : });
3821 :
3822 : const KINDS: &[&str] = &["pagestream"];
3823 : pub type Pagestream = Metrics<0>;
3824 :
3825 : impl<const KIND: usize> Metrics<KIND> {
3826 440 : pub(crate) fn new(tenant_shard_id: &TenantShardId) -> Self {
3827 440 : let per_tenant_label_values = &[
3828 440 : KINDS[KIND],
3829 440 : &tenant_shard_id.tenant_id.to_string(),
3830 440 : &tenant_shard_id.shard_slug().to_string(),
3831 440 : ];
3832 440 : Metrics {
3833 440 : count_accounted_start: {
3834 440 : GlobalAndPerTenantIntCounter {
3835 440 : global: COUNT_ACCOUNTED_START.with_label_values(&[KINDS[KIND]]),
3836 440 : per_tenant: COUNT_ACCOUNTED_START_PER_TENANT
3837 440 : .with_label_values(per_tenant_label_values),
3838 440 : }
3839 440 : },
3840 440 : count_accounted_finish: {
3841 440 : GlobalAndPerTenantIntCounter {
3842 440 : global: COUNT_ACCOUNTED_FINISH.with_label_values(&[KINDS[KIND]]),
3843 440 : per_tenant: COUNT_ACCOUNTED_FINISH_PER_TENANT
3844 440 : .with_label_values(per_tenant_label_values),
3845 440 : }
3846 440 : },
3847 440 : wait_time: {
3848 440 : GlobalAndPerTenantIntCounter {
3849 440 : global: WAIT_USECS.with_label_values(&[KINDS[KIND]]),
3850 440 : per_tenant: WAIT_USECS_PER_TENANT
3851 440 : .with_label_values(per_tenant_label_values),
3852 440 : }
3853 440 : },
3854 440 : count_throttled: {
3855 440 : GlobalAndPerTenantIntCounter {
3856 440 : global: WAIT_COUNT.with_label_values(&[KINDS[KIND]]),
3857 440 : per_tenant: WAIT_COUNT_PER_TENANT
3858 440 : .with_label_values(per_tenant_label_values),
3859 440 : }
3860 440 : },
3861 440 : }
3862 440 : }
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 392 : static TOKIO_EXECUTOR_THREAD_COUNT: Lazy<UIntGaugeVec> = Lazy::new(|| {
3949 392 : register_uint_gauge_vec!(
3950 392 : "pageserver_tokio_executor_thread_configured_count",
3951 392 : "Total number of configued tokio executor threads in the process.
3952 392 : The `setup` label denotes whether we're running with multiple runtimes or a single runtime.",
3953 392 : &["setup"],
3954 392 : )
3955 392 : .unwrap()
3956 392 : });
3957 :
3958 392 : pub(crate) fn set_tokio_runtime_setup(setup: &str, num_threads: NonZeroUsize) {
3959 : static SERIALIZE: std::sync::Mutex<()> = std::sync::Mutex::new(());
3960 392 : let _guard = SERIALIZE.lock().unwrap();
3961 392 : TOKIO_EXECUTOR_THREAD_COUNT.reset();
3962 392 : TOKIO_EXECUTOR_THREAD_COUNT
3963 392 : .get_metric_with_label_values(&[setup])
3964 392 : .unwrap()
3965 392 : .set(u64::try_from(num_threads.get()).unwrap());
3966 392 : }
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 : }
|