mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-10 15:02:56 +00:00
Cleanup tenant specific metrics once a tenant is detached. (#2328)
* Add test for pageserver metric cleanup once a tenant is detached. * Remove tenant specific timeline metrics on detach. * Use definitions from timeline_metrics in page service. * Move metrics to own file from layered_repository/timeline.rs * TIMELINE_METRICS: define smgr metrics * REMOVE SMGR cleanup from timeline_metrics. Doesn't seem to work as expected. * Vritual file centralized metrics, except for evicted file as there's no tenat id or timeline id. * Use STORAGE_TIME from timeline_metrics in layered_repository. * Remove timelineless gc metrics for tenant on detach. * Rename timeline metrics -> metrics as it's more generic. * Don't create a TimelineMetrics instance for VirtualFile * Move the rest of the metric definitions to metrics.rs too. * UUID -> ZTenantId * Use consistent style for dict. * Use Repository's Drop trait for dropping STORAGE_TIME metrics. * No need for Arc, TimelineMetrics is used in just one place. Due to that, we can fall back using ZTenantId and ZTimelineId too to avoid additional string allocation.
This commit is contained in:
@@ -32,9 +32,11 @@ use std::time::{Duration, Instant};
|
|||||||
|
|
||||||
use self::metadata::{metadata_path, TimelineMetadata};
|
use self::metadata::{metadata_path, TimelineMetadata};
|
||||||
use crate::config::PageServerConf;
|
use crate::config::PageServerConf;
|
||||||
|
use crate::metrics::remove_tenant_metrics;
|
||||||
use crate::storage_sync::index::RemoteIndex;
|
use crate::storage_sync::index::RemoteIndex;
|
||||||
use crate::tenant_config::{TenantConf, TenantConfOpt};
|
use crate::tenant_config::{TenantConf, TenantConfOpt};
|
||||||
|
|
||||||
|
use crate::metrics::STORAGE_TIME;
|
||||||
use crate::repository::GcResult;
|
use crate::repository::GcResult;
|
||||||
use crate::tenant_mgr::LocalTimelineUpdate;
|
use crate::tenant_mgr::LocalTimelineUpdate;
|
||||||
use crate::thread_mgr;
|
use crate::thread_mgr;
|
||||||
@@ -301,7 +303,7 @@ impl Repository {
|
|||||||
.map(|x| x.to_string())
|
.map(|x| x.to_string())
|
||||||
.unwrap_or_else(|| "-".to_string());
|
.unwrap_or_else(|| "-".to_string());
|
||||||
|
|
||||||
timeline::STORAGE_TIME
|
STORAGE_TIME
|
||||||
.with_label_values(&["gc", &self.tenant_id.to_string(), &timeline_str])
|
.with_label_values(&["gc", &self.tenant_id.to_string(), &timeline_str])
|
||||||
.observe_closure_duration(|| {
|
.observe_closure_duration(|| {
|
||||||
self.gc_iteration_internal(target_timeline_id, horizon, pitr, checkpoint_before_gc)
|
self.gc_iteration_internal(target_timeline_id, horizon, pitr, checkpoint_before_gc)
|
||||||
@@ -858,6 +860,11 @@ impl Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Repository {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
remove_tenant_metrics(&self.tenant_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Dump contents of a layer file to stdout.
|
/// Dump contents of a layer file to stdout.
|
||||||
pub fn dump_layerfile_from_path(path: &Path, verbose: bool) -> Result<()> {
|
pub fn dump_layerfile_from_path(path: &Path, verbose: bool) -> Result<()> {
|
||||||
use std::os::unix::fs::FileExt;
|
use std::os::unix::fs::FileExt;
|
||||||
|
|||||||
@@ -13,21 +13,15 @@
|
|||||||
use crate::layered_repository::inmemory_layer::InMemoryLayer;
|
use crate::layered_repository::inmemory_layer::InMemoryLayer;
|
||||||
use crate::layered_repository::storage_layer::Layer;
|
use crate::layered_repository::storage_layer::Layer;
|
||||||
use crate::layered_repository::storage_layer::{range_eq, range_overlaps};
|
use crate::layered_repository::storage_layer::{range_eq, range_overlaps};
|
||||||
|
use crate::metrics::NUM_ONDISK_LAYERS;
|
||||||
use crate::repository::Key;
|
use crate::repository::Key;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use metrics::{register_int_gauge, IntGauge};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
use utils::lsn::Lsn;
|
use utils::lsn::Lsn;
|
||||||
|
|
||||||
static NUM_ONDISK_LAYERS: Lazy<IntGauge> = Lazy::new(|| {
|
|
||||||
register_int_gauge!("pageserver_ondisk_layers", "Number of layers on-disk")
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// LayerMap tracks what layers exist on a timeline.
|
/// LayerMap tracks what layers exist on a timeline.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ use anyhow::{anyhow, bail, ensure, Context, Result};
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use fail::fail_point;
|
use fail::fail_point;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use metrics::core::{AtomicU64, GenericCounter};
|
use once_cell::sync::OnceCell;
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
|
|
||||||
use std::cmp::{max, min, Ordering};
|
use std::cmp::{max, min, Ordering};
|
||||||
@@ -17,12 +16,6 @@ use std::sync::{mpsc, Arc, Mutex, MutexGuard, RwLock, TryLockError};
|
|||||||
use std::time::{Duration, Instant, SystemTime};
|
use std::time::{Duration, Instant, SystemTime};
|
||||||
use std::{fs, thread};
|
use std::{fs, thread};
|
||||||
|
|
||||||
use metrics::{
|
|
||||||
register_histogram_vec, register_int_counter, register_int_counter_vec, register_int_gauge_vec,
|
|
||||||
register_uint_gauge_vec, Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge,
|
|
||||||
IntGaugeVec, UIntGauge, UIntGaugeVec,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::layered_repository::{
|
use crate::layered_repository::{
|
||||||
delta_layer::{DeltaLayer, DeltaLayerWriter},
|
delta_layer::{DeltaLayer, DeltaLayerWriter},
|
||||||
ephemeral_file::is_ephemeral_file,
|
ephemeral_file::is_ephemeral_file,
|
||||||
@@ -37,6 +30,7 @@ use crate::layered_repository::{
|
|||||||
|
|
||||||
use crate::config::PageServerConf;
|
use crate::config::PageServerConf;
|
||||||
use crate::keyspace::{KeyPartitioning, KeySpace};
|
use crate::keyspace::{KeyPartitioning, KeySpace};
|
||||||
|
use crate::metrics::TimelineMetrics;
|
||||||
use crate::pgdatadir_mapping::BlockNumber;
|
use crate::pgdatadir_mapping::BlockNumber;
|
||||||
use crate::pgdatadir_mapping::LsnForTimestamp;
|
use crate::pgdatadir_mapping::LsnForTimestamp;
|
||||||
use crate::reltag::RelTag;
|
use crate::reltag::RelTag;
|
||||||
@@ -58,182 +52,6 @@ use crate::walredo::WalRedoManager;
|
|||||||
use crate::CheckpointConfig;
|
use crate::CheckpointConfig;
|
||||||
use crate::{page_cache, storage_sync};
|
use crate::{page_cache, storage_sync};
|
||||||
|
|
||||||
/// Prometheus histogram buckets (in seconds) that capture the majority of
|
|
||||||
/// latencies in the microsecond range but also extend far enough up to distinguish
|
|
||||||
/// "bad" from "really bad".
|
|
||||||
fn get_buckets_for_critical_operations() -> Vec<f64> {
|
|
||||||
let buckets_per_digit = 5;
|
|
||||||
let min_exponent = -6;
|
|
||||||
let max_exponent = 2;
|
|
||||||
|
|
||||||
let mut buckets = vec![];
|
|
||||||
// Compute 10^(exp / buckets_per_digit) instead of 10^(1/buckets_per_digit)^exp
|
|
||||||
// because it's more numerically stable and doesn't result in numbers like 9.999999
|
|
||||||
for exp in (min_exponent * buckets_per_digit)..=(max_exponent * buckets_per_digit) {
|
|
||||||
buckets.push(10_f64.powf(exp as f64 / buckets_per_digit as f64))
|
|
||||||
}
|
|
||||||
buckets
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metrics collected on operations on the storage repository.
|
|
||||||
pub static STORAGE_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
|
||||||
register_histogram_vec!(
|
|
||||||
"pageserver_storage_operations_seconds",
|
|
||||||
"Time spent on storage operations",
|
|
||||||
&["operation", "tenant_id", "timeline_id"],
|
|
||||||
get_buckets_for_critical_operations(),
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
// Metrics collected on operations on the storage repository.
|
|
||||||
static RECONSTRUCT_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
|
||||||
register_histogram_vec!(
|
|
||||||
"pageserver_getpage_reconstruct_seconds",
|
|
||||||
"Time spent in reconstruct_value",
|
|
||||||
&["tenant_id", "timeline_id"],
|
|
||||||
get_buckets_for_critical_operations(),
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static MATERIALIZED_PAGE_CACHE_HIT: Lazy<IntCounterVec> = Lazy::new(|| {
|
|
||||||
register_int_counter_vec!(
|
|
||||||
"pageserver_materialized_cache_hits_total",
|
|
||||||
"Number of cache hits from materialized page cache",
|
|
||||||
&["tenant_id", "timeline_id"]
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static WAIT_LSN_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
|
||||||
register_histogram_vec!(
|
|
||||||
"pageserver_wait_lsn_seconds",
|
|
||||||
"Time spent waiting for WAL to arrive",
|
|
||||||
&["tenant_id", "timeline_id"],
|
|
||||||
get_buckets_for_critical_operations(),
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static LAST_RECORD_LSN: Lazy<IntGaugeVec> = Lazy::new(|| {
|
|
||||||
register_int_gauge_vec!(
|
|
||||||
"pageserver_last_record_lsn",
|
|
||||||
"Last record LSN grouped by timeline",
|
|
||||||
&["tenant_id", "timeline_id"]
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
// Metrics for determining timeline's physical size.
|
|
||||||
// A layered timeline's physical is defined as the total size of
|
|
||||||
// (delta/image) layer files on disk.
|
|
||||||
static CURRENT_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
|
|
||||||
register_uint_gauge_vec!(
|
|
||||||
"pageserver_current_physical_size",
|
|
||||||
"Current physical size grouped by timeline",
|
|
||||||
&["tenant_id", "timeline_id"]
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static CURRENT_LOGICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
|
|
||||||
register_uint_gauge_vec!(
|
|
||||||
"pageserver_current_logical_size",
|
|
||||||
"Current logical size grouped by timeline",
|
|
||||||
&["tenant_id", "timeline_id"]
|
|
||||||
)
|
|
||||||
.expect("failed to define current logical size metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
// Metrics for cloud upload. These metrics reflect data uploaded to cloud storage,
|
|
||||||
// or in testing they estimate how much we would upload if we did.
|
|
||||||
static NUM_PERSISTENT_FILES_CREATED: Lazy<IntCounter> = Lazy::new(|| {
|
|
||||||
register_int_counter!(
|
|
||||||
"pageserver_created_persistent_files_total",
|
|
||||||
"Number of files created that are meant to be uploaded to cloud storage",
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static PERSISTENT_BYTES_WRITTEN: Lazy<IntCounter> = Lazy::new(|| {
|
|
||||||
register_int_counter!(
|
|
||||||
"pageserver_written_persistent_bytes_total",
|
|
||||||
"Total bytes written that are meant to be uploaded to cloud storage",
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
struct TimelineMetrics {
|
|
||||||
pub reconstruct_time_histo: Histogram,
|
|
||||||
pub materialized_page_cache_hit_counter: GenericCounter<AtomicU64>,
|
|
||||||
pub flush_time_histo: Histogram,
|
|
||||||
pub compact_time_histo: Histogram,
|
|
||||||
pub create_images_time_histo: Histogram,
|
|
||||||
pub init_logical_size_histo: Histogram,
|
|
||||||
pub load_layer_map_histo: Histogram,
|
|
||||||
pub last_record_gauge: IntGauge,
|
|
||||||
pub wait_lsn_time_histo: Histogram,
|
|
||||||
pub current_physical_size_gauge: UIntGauge,
|
|
||||||
/// copy of LayeredTimeline.current_logical_size
|
|
||||||
pub current_logical_size_gauge: UIntGauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TimelineMetrics {
|
|
||||||
fn new(tenant_id: &ZTenantId, timeline_id: &ZTimelineId) -> Self {
|
|
||||||
let tenant_id = tenant_id.to_string();
|
|
||||||
let timeline_id = timeline_id.to_string();
|
|
||||||
|
|
||||||
let reconstruct_time_histo = RECONSTRUCT_TIME
|
|
||||||
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let materialized_page_cache_hit_counter = MATERIALIZED_PAGE_CACHE_HIT
|
|
||||||
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let flush_time_histo = STORAGE_TIME
|
|
||||||
.get_metric_with_label_values(&["layer flush", &tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let compact_time_histo = STORAGE_TIME
|
|
||||||
.get_metric_with_label_values(&["compact", &tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let create_images_time_histo = STORAGE_TIME
|
|
||||||
.get_metric_with_label_values(&["create images", &tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let init_logical_size_histo = STORAGE_TIME
|
|
||||||
.get_metric_with_label_values(&["init logical size", &tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let load_layer_map_histo = STORAGE_TIME
|
|
||||||
.get_metric_with_label_values(&["load layer map", &tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let last_record_gauge = LAST_RECORD_LSN
|
|
||||||
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let wait_lsn_time_histo = WAIT_LSN_TIME
|
|
||||||
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let current_physical_size_gauge = CURRENT_PHYSICAL_SIZE
|
|
||||||
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
let current_logical_size_gauge = CURRENT_LOGICAL_SIZE
|
|
||||||
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
TimelineMetrics {
|
|
||||||
reconstruct_time_histo,
|
|
||||||
materialized_page_cache_hit_counter,
|
|
||||||
flush_time_histo,
|
|
||||||
compact_time_histo,
|
|
||||||
create_images_time_histo,
|
|
||||||
init_logical_size_histo,
|
|
||||||
load_layer_map_histo,
|
|
||||||
last_record_gauge,
|
|
||||||
wait_lsn_time_histo,
|
|
||||||
current_physical_size_gauge,
|
|
||||||
current_logical_size_gauge,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Timeline {
|
pub struct Timeline {
|
||||||
conf: &'static PageServerConf,
|
conf: &'static PageServerConf,
|
||||||
tenant_conf: Arc<RwLock<TenantConfOpt>>,
|
tenant_conf: Arc<RwLock<TenantConfOpt>>,
|
||||||
@@ -1494,8 +1312,8 @@ impl Timeline {
|
|||||||
let sz = new_delta_path.metadata()?.len();
|
let sz = new_delta_path.metadata()?.len();
|
||||||
self.metrics.current_physical_size_gauge.add(sz);
|
self.metrics.current_physical_size_gauge.add(sz);
|
||||||
// update metrics
|
// update metrics
|
||||||
NUM_PERSISTENT_FILES_CREATED.inc_by(1);
|
self.metrics.num_persistent_files_created.inc_by(1);
|
||||||
PERSISTENT_BYTES_WRITTEN.inc_by(sz);
|
self.metrics.persistent_bytes_written.inc_by(sz);
|
||||||
|
|
||||||
Ok(new_delta_path)
|
Ok(new_delta_path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pub mod http;
|
|||||||
pub mod import_datadir;
|
pub mod import_datadir;
|
||||||
pub mod keyspace;
|
pub mod keyspace;
|
||||||
pub mod layered_repository;
|
pub mod layered_repository;
|
||||||
|
pub mod metrics;
|
||||||
pub mod page_cache;
|
pub mod page_cache;
|
||||||
pub mod page_service;
|
pub mod page_service;
|
||||||
pub mod pgdatadir_mapping;
|
pub mod pgdatadir_mapping;
|
||||||
@@ -22,11 +23,9 @@ pub mod walreceiver;
|
|||||||
pub mod walrecord;
|
pub mod walrecord;
|
||||||
pub mod walredo;
|
pub mod walredo;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::thread_mgr::ThreadKind;
|
use crate::thread_mgr::ThreadKind;
|
||||||
use metrics::{register_int_gauge_vec, IntGaugeVec};
|
|
||||||
|
|
||||||
/// Current storage format version
|
/// Current storage format version
|
||||||
///
|
///
|
||||||
@@ -39,15 +38,6 @@ pub const STORAGE_FORMAT_VERSION: u16 = 3;
|
|||||||
pub const IMAGE_FILE_MAGIC: u16 = 0x5A60;
|
pub const IMAGE_FILE_MAGIC: u16 = 0x5A60;
|
||||||
pub const DELTA_FILE_MAGIC: u16 = 0x5A61;
|
pub const DELTA_FILE_MAGIC: u16 = 0x5A61;
|
||||||
|
|
||||||
static LIVE_CONNECTIONS_COUNT: Lazy<IntGaugeVec> = Lazy::new(|| {
|
|
||||||
register_int_gauge_vec!(
|
|
||||||
"pageserver_live_connections",
|
|
||||||
"Number of live network connections",
|
|
||||||
&["pageserver_connection_kind"]
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const LOG_FILE_NAME: &str = "pageserver.log";
|
pub const LOG_FILE_NAME: &str = "pageserver.log";
|
||||||
|
|
||||||
/// Config for the Repository checkpointer
|
/// Config for the Repository checkpointer
|
||||||
|
|||||||
419
pageserver/src/metrics.rs
Normal file
419
pageserver/src/metrics.rs
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
use metrics::core::{AtomicU64, GenericCounter};
|
||||||
|
use metrics::{
|
||||||
|
register_histogram, register_histogram_vec, register_int_counter, register_int_counter_vec,
|
||||||
|
register_int_gauge, register_int_gauge_vec, register_uint_gauge_vec, Histogram, HistogramVec,
|
||||||
|
IntCounter, IntCounterVec, IntGauge, IntGaugeVec, UIntGauge, UIntGaugeVec,
|
||||||
|
};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use utils::zid::{ZTenantId, ZTimelineId};
|
||||||
|
|
||||||
|
/// Prometheus histogram buckets (in seconds) that capture the majority of
|
||||||
|
/// latencies in the microsecond range but also extend far enough up to distinguish
|
||||||
|
/// "bad" from "really bad".
|
||||||
|
fn get_buckets_for_critical_operations() -> Vec<f64> {
|
||||||
|
let buckets_per_digit = 5;
|
||||||
|
let min_exponent = -6;
|
||||||
|
let max_exponent = 2;
|
||||||
|
|
||||||
|
let mut buckets = vec![];
|
||||||
|
// Compute 10^(exp / buckets_per_digit) instead of 10^(1/buckets_per_digit)^exp
|
||||||
|
// because it's more numerically stable and doesn't result in numbers like 9.999999
|
||||||
|
for exp in (min_exponent * buckets_per_digit)..=(max_exponent * buckets_per_digit) {
|
||||||
|
buckets.push(10_f64.powf(exp as f64 / buckets_per_digit as f64))
|
||||||
|
}
|
||||||
|
buckets
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metrics collected on operations on the storage repository.
|
||||||
|
const STORAGE_TIME_OPERATIONS: &[&str] = &[
|
||||||
|
"layer flush",
|
||||||
|
"compact",
|
||||||
|
"create images",
|
||||||
|
"init logical size",
|
||||||
|
"load layer map",
|
||||||
|
"gc",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub static STORAGE_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
||||||
|
register_histogram_vec!(
|
||||||
|
"pageserver_storage_operations_seconds",
|
||||||
|
"Time spent on storage operations",
|
||||||
|
&["operation", "tenant_id", "timeline_id"],
|
||||||
|
get_buckets_for_critical_operations(),
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Metrics collected on operations on the storage repository.
|
||||||
|
static RECONSTRUCT_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
||||||
|
register_histogram_vec!(
|
||||||
|
"pageserver_getpage_reconstruct_seconds",
|
||||||
|
"Time spent in reconstruct_value",
|
||||||
|
&["tenant_id", "timeline_id"],
|
||||||
|
get_buckets_for_critical_operations(),
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
static MATERIALIZED_PAGE_CACHE_HIT: Lazy<IntCounterVec> = Lazy::new(|| {
|
||||||
|
register_int_counter_vec!(
|
||||||
|
"pageserver_materialized_cache_hits_total",
|
||||||
|
"Number of cache hits from materialized page cache",
|
||||||
|
&["tenant_id", "timeline_id"]
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
static WAIT_LSN_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
||||||
|
register_histogram_vec!(
|
||||||
|
"pageserver_wait_lsn_seconds",
|
||||||
|
"Time spent waiting for WAL to arrive",
|
||||||
|
&["tenant_id", "timeline_id"],
|
||||||
|
get_buckets_for_critical_operations(),
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
static LAST_RECORD_LSN: Lazy<IntGaugeVec> = Lazy::new(|| {
|
||||||
|
register_int_gauge_vec!(
|
||||||
|
"pageserver_last_record_lsn",
|
||||||
|
"Last record LSN grouped by timeline",
|
||||||
|
&["tenant_id", "timeline_id"]
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Metrics for determining timeline's physical size.
|
||||||
|
// A layered timeline's physical is defined as the total size of
|
||||||
|
// (delta/image) layer files on disk.
|
||||||
|
static CURRENT_PHYSICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
|
||||||
|
register_uint_gauge_vec!(
|
||||||
|
"pageserver_current_physical_size",
|
||||||
|
"Current physical size grouped by timeline",
|
||||||
|
&["tenant_id", "timeline_id"]
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
static CURRENT_LOGICAL_SIZE: Lazy<UIntGaugeVec> = Lazy::new(|| {
|
||||||
|
register_uint_gauge_vec!(
|
||||||
|
"pageserver_current_logical_size",
|
||||||
|
"Current logical size grouped by timeline",
|
||||||
|
&["tenant_id", "timeline_id"]
|
||||||
|
)
|
||||||
|
.expect("failed to define current logical size metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Metrics for cloud upload. These metrics reflect data uploaded to cloud storage,
|
||||||
|
// or in testing they estimate how much we would upload if we did.
|
||||||
|
static NUM_PERSISTENT_FILES_CREATED: Lazy<IntCounter> = Lazy::new(|| {
|
||||||
|
IntCounter::new(
|
||||||
|
"pageserver_created_persistent_files_total",
|
||||||
|
"Number of files created that are meant to be uploaded to cloud storage",
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
static PERSISTENT_BYTES_WRITTEN: Lazy<IntCounter> = Lazy::new(|| {
|
||||||
|
IntCounter::new(
|
||||||
|
"pageserver_written_persistent_bytes_total",
|
||||||
|
"Total bytes written that are meant to be uploaded to cloud storage",
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Metrics collected on disk IO operations
|
||||||
|
const STORAGE_IO_TIME_BUCKETS: &[f64] = &[
|
||||||
|
0.000001, // 1 usec
|
||||||
|
0.00001, // 10 usec
|
||||||
|
0.0001, // 100 usec
|
||||||
|
0.001, // 1 msec
|
||||||
|
0.01, // 10 msec
|
||||||
|
0.1, // 100 msec
|
||||||
|
1.0, // 1 sec
|
||||||
|
];
|
||||||
|
|
||||||
|
const STORAGE_IO_TIME_OPERATIONS: &[&str] =
|
||||||
|
&["open", "close", "read", "write", "seek", "fsync", "gc"];
|
||||||
|
|
||||||
|
const STORAGE_IO_SIZE_OPERATIONS: &[&str] = &["read", "write"];
|
||||||
|
|
||||||
|
pub static STORAGE_IO_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
||||||
|
register_histogram_vec!(
|
||||||
|
"pageserver_io_operations_seconds",
|
||||||
|
"Time spent in IO operations",
|
||||||
|
&["operation", "tenant_id", "timeline_id"],
|
||||||
|
STORAGE_IO_TIME_BUCKETS.into()
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static STORAGE_IO_SIZE: Lazy<IntGaugeVec> = Lazy::new(|| {
|
||||||
|
register_int_gauge_vec!(
|
||||||
|
"pageserver_io_operations_bytes_total",
|
||||||
|
"Total amount of bytes read/written in IO operations",
|
||||||
|
&["operation", "tenant_id", "timeline_id"]
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
const SMGR_QUERY_TIME_OPERATIONS: &[&str] = &[
|
||||||
|
"get_rel_exists",
|
||||||
|
"get_rel_size",
|
||||||
|
"get_page_at_lsn",
|
||||||
|
"get_db_size",
|
||||||
|
];
|
||||||
|
|
||||||
|
const SMGR_QUERY_TIME_BUCKETS: &[f64] = &[
|
||||||
|
0.00001, // 1/100000 s
|
||||||
|
0.0001, 0.00015, 0.0002, 0.00025, 0.0003, 0.00035, 0.0005, 0.00075, // 1/10000 s
|
||||||
|
0.001, 0.0025, 0.005, 0.0075, // 1/1000 s
|
||||||
|
0.01, 0.0125, 0.015, 0.025, 0.05, // 1/100 s
|
||||||
|
0.1, // 1/10 s
|
||||||
|
];
|
||||||
|
|
||||||
|
pub static SMGR_QUERY_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
||||||
|
register_histogram_vec!(
|
||||||
|
"pageserver_smgr_query_seconds",
|
||||||
|
"Time spent on smgr query handling",
|
||||||
|
&["smgr_query_type", "tenant_id", "timeline_id"],
|
||||||
|
SMGR_QUERY_TIME_BUCKETS.into()
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static LIVE_CONNECTIONS_COUNT: Lazy<IntGaugeVec> = Lazy::new(|| {
|
||||||
|
register_int_gauge_vec!(
|
||||||
|
"pageserver_live_connections",
|
||||||
|
"Number of live network connections",
|
||||||
|
&["pageserver_connection_kind"]
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static NUM_ONDISK_LAYERS: Lazy<IntGauge> = Lazy::new(|| {
|
||||||
|
register_int_gauge!("pageserver_ondisk_layers", "Number of layers on-disk")
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static REMAINING_SYNC_ITEMS: Lazy<IntGauge> = Lazy::new(|| {
|
||||||
|
register_int_gauge!(
|
||||||
|
"pageserver_remote_storage_remaining_sync_items",
|
||||||
|
"Number of storage sync items left in the queue"
|
||||||
|
)
|
||||||
|
.expect("failed to register pageserver remote storage remaining sync items int gauge")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static IMAGE_SYNC_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
||||||
|
register_histogram_vec!(
|
||||||
|
"pageserver_remote_storage_image_sync_seconds",
|
||||||
|
"Time took to synchronize (download or upload) a whole pageserver image. \
|
||||||
|
Grouped by tenant and timeline ids, `operation_kind` (upload|download) and `status` (success|failure)",
|
||||||
|
&["tenant_id", "timeline_id", "operation_kind", "status"],
|
||||||
|
vec![0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 3.0, 10.0, 20.0]
|
||||||
|
)
|
||||||
|
.expect("failed to register pageserver image sync time histogram vec")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static REMOTE_INDEX_UPLOAD: Lazy<IntCounterVec> = Lazy::new(|| {
|
||||||
|
register_int_counter_vec!(
|
||||||
|
"pageserver_remote_storage_remote_index_uploads_total",
|
||||||
|
"Number of remote index uploads",
|
||||||
|
&["tenant_id", "timeline_id"],
|
||||||
|
)
|
||||||
|
.expect("failed to register pageserver remote index upload vec")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static NO_LAYERS_UPLOAD: Lazy<IntCounterVec> = Lazy::new(|| {
|
||||||
|
register_int_counter_vec!(
|
||||||
|
"pageserver_remote_storage_no_layers_uploads_total",
|
||||||
|
"Number of skipped uploads due to no layers",
|
||||||
|
&["tenant_id", "timeline_id"],
|
||||||
|
)
|
||||||
|
.expect("failed to register pageserver no layers upload vec")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static TENANT_TASK_EVENTS: Lazy<IntCounterVec> = Lazy::new(|| {
|
||||||
|
register_int_counter_vec!(
|
||||||
|
"pageserver_tenant_task_events",
|
||||||
|
"Number of task start/stop/fail events.",
|
||||||
|
&["event"],
|
||||||
|
)
|
||||||
|
.expect("Failed to register tenant_task_events metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Metrics collected on WAL redo operations
|
||||||
|
//
|
||||||
|
// We collect the time spent in actual WAL redo ('redo'), and time waiting
|
||||||
|
// for access to the postgres process ('wait') since there is only one for
|
||||||
|
// each tenant.
|
||||||
|
|
||||||
|
/// Time buckets are small because we want to be able to measure the
|
||||||
|
/// smallest redo processing times. These buckets allow us to measure down
|
||||||
|
/// to 5us, which equates to 200'000 pages/sec, which equates to 1.6GB/sec.
|
||||||
|
/// This is much better than the previous 5ms aka 200 pages/sec aka 1.6MB/sec.
|
||||||
|
macro_rules! redo_histogram_time_buckets {
|
||||||
|
() => {
|
||||||
|
vec![
|
||||||
|
0.000_005, 0.000_010, 0.000_025, 0.000_050, 0.000_100, 0.000_250, 0.000_500, 0.001_000,
|
||||||
|
0.002_500, 0.005_000, 0.010_000, 0.025_000, 0.050_000,
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// While we're at it, also measure the amount of records replayed in each
|
||||||
|
/// operation. We have a global 'total replayed' counter, but that's not
|
||||||
|
/// as useful as 'what is the skew for how many records we replay in one
|
||||||
|
/// operation'.
|
||||||
|
macro_rules! redo_histogram_count_buckets {
|
||||||
|
() => {
|
||||||
|
vec![0.0, 1.0, 2.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static WAL_REDO_TIME: Lazy<Histogram> = Lazy::new(|| {
|
||||||
|
register_histogram!(
|
||||||
|
"pageserver_wal_redo_seconds",
|
||||||
|
"Time spent on WAL redo",
|
||||||
|
redo_histogram_time_buckets!()
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static WAL_REDO_WAIT_TIME: Lazy<Histogram> = Lazy::new(|| {
|
||||||
|
register_histogram!(
|
||||||
|
"pageserver_wal_redo_wait_seconds",
|
||||||
|
"Time spent waiting for access to the WAL redo process",
|
||||||
|
redo_histogram_time_buckets!(),
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static WAL_REDO_RECORDS_HISTOGRAM: Lazy<Histogram> = Lazy::new(|| {
|
||||||
|
register_histogram!(
|
||||||
|
"pageserver_wal_redo_records_histogram",
|
||||||
|
"Histogram of number of records replayed per redo",
|
||||||
|
redo_histogram_count_buckets!(),
|
||||||
|
)
|
||||||
|
.expect("failed to define a metric")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static WAL_REDO_RECORD_COUNTER: Lazy<IntCounter> = Lazy::new(|| {
|
||||||
|
register_int_counter!(
|
||||||
|
"pageserver_replayed_wal_records_total",
|
||||||
|
"Number of WAL records replayed in WAL redo process"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TimelineMetrics {
|
||||||
|
tenant_id: String,
|
||||||
|
timeline_id: String,
|
||||||
|
pub reconstruct_time_histo: Histogram,
|
||||||
|
pub materialized_page_cache_hit_counter: GenericCounter<AtomicU64>,
|
||||||
|
pub flush_time_histo: Histogram,
|
||||||
|
pub compact_time_histo: Histogram,
|
||||||
|
pub create_images_time_histo: Histogram,
|
||||||
|
pub init_logical_size_histo: Histogram,
|
||||||
|
pub load_layer_map_histo: Histogram,
|
||||||
|
pub last_record_gauge: IntGauge,
|
||||||
|
pub wait_lsn_time_histo: Histogram,
|
||||||
|
pub current_physical_size_gauge: UIntGauge,
|
||||||
|
/// copy of LayeredTimeline.current_logical_size
|
||||||
|
pub current_logical_size_gauge: UIntGauge,
|
||||||
|
pub num_persistent_files_created: IntCounter,
|
||||||
|
pub persistent_bytes_written: IntCounter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimelineMetrics {
|
||||||
|
pub fn new(tenant_id: &ZTenantId, timeline_id: &ZTimelineId) -> Self {
|
||||||
|
let tenant_id = tenant_id.to_string();
|
||||||
|
let timeline_id = timeline_id.to_string();
|
||||||
|
let reconstruct_time_histo = RECONSTRUCT_TIME
|
||||||
|
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let materialized_page_cache_hit_counter = MATERIALIZED_PAGE_CACHE_HIT
|
||||||
|
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let flush_time_histo = STORAGE_TIME
|
||||||
|
.get_metric_with_label_values(&["layer flush", &tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let compact_time_histo = STORAGE_TIME
|
||||||
|
.get_metric_with_label_values(&["compact", &tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let create_images_time_histo = STORAGE_TIME
|
||||||
|
.get_metric_with_label_values(&["create images", &tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let init_logical_size_histo = STORAGE_TIME
|
||||||
|
.get_metric_with_label_values(&["init logical size", &tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let load_layer_map_histo = STORAGE_TIME
|
||||||
|
.get_metric_with_label_values(&["load layer map", &tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let last_record_gauge = LAST_RECORD_LSN
|
||||||
|
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let wait_lsn_time_histo = WAIT_LSN_TIME
|
||||||
|
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let current_physical_size_gauge = CURRENT_PHYSICAL_SIZE
|
||||||
|
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let current_logical_size_gauge = CURRENT_LOGICAL_SIZE
|
||||||
|
.get_metric_with_label_values(&[&tenant_id, &timeline_id])
|
||||||
|
.unwrap();
|
||||||
|
let num_persistent_files_created = NUM_PERSISTENT_FILES_CREATED.clone();
|
||||||
|
let persistent_bytes_written = PERSISTENT_BYTES_WRITTEN.clone();
|
||||||
|
|
||||||
|
TimelineMetrics {
|
||||||
|
tenant_id,
|
||||||
|
timeline_id,
|
||||||
|
reconstruct_time_histo,
|
||||||
|
materialized_page_cache_hit_counter,
|
||||||
|
flush_time_histo,
|
||||||
|
compact_time_histo,
|
||||||
|
create_images_time_histo,
|
||||||
|
init_logical_size_histo,
|
||||||
|
load_layer_map_histo,
|
||||||
|
last_record_gauge,
|
||||||
|
wait_lsn_time_histo,
|
||||||
|
current_physical_size_gauge,
|
||||||
|
current_logical_size_gauge,
|
||||||
|
num_persistent_files_created,
|
||||||
|
persistent_bytes_written,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TimelineMetrics {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let tenant_id = &self.tenant_id;
|
||||||
|
let timeline_id = &self.timeline_id;
|
||||||
|
let _ = RECONSTRUCT_TIME.remove_label_values(&[tenant_id, timeline_id]);
|
||||||
|
let _ = MATERIALIZED_PAGE_CACHE_HIT.remove_label_values(&[tenant_id, timeline_id]);
|
||||||
|
let _ = LAST_RECORD_LSN.remove_label_values(&[tenant_id, timeline_id]);
|
||||||
|
let _ = WAIT_LSN_TIME.remove_label_values(&[tenant_id, timeline_id]);
|
||||||
|
let _ = CURRENT_PHYSICAL_SIZE.remove_label_values(&[tenant_id, timeline_id]);
|
||||||
|
let _ = CURRENT_LOGICAL_SIZE.remove_label_values(&[tenant_id, timeline_id]);
|
||||||
|
|
||||||
|
for op in STORAGE_TIME_OPERATIONS {
|
||||||
|
let _ = STORAGE_TIME.remove_label_values(&[op, tenant_id, timeline_id]);
|
||||||
|
}
|
||||||
|
for op in STORAGE_IO_TIME_OPERATIONS {
|
||||||
|
let _ = STORAGE_IO_TIME.remove_label_values(&[op, tenant_id, timeline_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for op in STORAGE_IO_SIZE_OPERATIONS {
|
||||||
|
let _ = STORAGE_IO_SIZE.remove_label_values(&[op, tenant_id, timeline_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for op in SMGR_QUERY_TIME_OPERATIONS {
|
||||||
|
let _ = SMGR_QUERY_TIME.remove_label_values(&[op, tenant_id, timeline_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_tenant_metrics(tenant_id: &ZTenantId) {
|
||||||
|
let _ = STORAGE_TIME.remove_label_values(&["gc", &tenant_id.to_string(), "-"]);
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
use anyhow::{bail, ensure, Context, Result};
|
use anyhow::{bail, ensure, Context, Result};
|
||||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::net::TcpListener;
|
use std::net::TcpListener;
|
||||||
@@ -32,6 +31,7 @@ use crate::basebackup;
|
|||||||
use crate::config::{PageServerConf, ProfilingConfig};
|
use crate::config::{PageServerConf, ProfilingConfig};
|
||||||
use crate::import_datadir::{import_basebackup_from_tar, import_wal_from_tar};
|
use crate::import_datadir::{import_basebackup_from_tar, import_wal_from_tar};
|
||||||
use crate::layered_repository::Timeline;
|
use crate::layered_repository::Timeline;
|
||||||
|
use crate::metrics::{LIVE_CONNECTIONS_COUNT, SMGR_QUERY_TIME};
|
||||||
use crate::pgdatadir_mapping::LsnForTimestamp;
|
use crate::pgdatadir_mapping::LsnForTimestamp;
|
||||||
use crate::profiling::profpoint_start;
|
use crate::profiling::profpoint_start;
|
||||||
use crate::reltag::RelTag;
|
use crate::reltag::RelTag;
|
||||||
@@ -39,7 +39,6 @@ use crate::tenant_mgr;
|
|||||||
use crate::thread_mgr;
|
use crate::thread_mgr;
|
||||||
use crate::thread_mgr::ThreadKind;
|
use crate::thread_mgr::ThreadKind;
|
||||||
use crate::CheckpointConfig;
|
use crate::CheckpointConfig;
|
||||||
use metrics::{register_histogram_vec, HistogramVec};
|
|
||||||
use postgres_ffi::v14::xlog_utils::to_pg_timestamp;
|
use postgres_ffi::v14::xlog_utils::to_pg_timestamp;
|
||||||
|
|
||||||
use postgres_ffi::v14::pg_constants::DEFAULTTABLESPACE_OID;
|
use postgres_ffi::v14::pg_constants::DEFAULTTABLESPACE_OID;
|
||||||
@@ -374,7 +373,7 @@ fn page_service_conn_main(
|
|||||||
// Immediately increment the gauge, then create a job to decrement it on thread exit.
|
// Immediately increment the gauge, then create a job to decrement it on thread exit.
|
||||||
// One of the pros of `defer!` is that this will *most probably*
|
// One of the pros of `defer!` is that this will *most probably*
|
||||||
// get called, even in presence of panics.
|
// get called, even in presence of panics.
|
||||||
let gauge = crate::LIVE_CONNECTIONS_COUNT.with_label_values(&["page_service"]);
|
let gauge = LIVE_CONNECTIONS_COUNT.with_label_values(&["page_service"]);
|
||||||
gauge.inc();
|
gauge.inc();
|
||||||
scopeguard::defer! {
|
scopeguard::defer! {
|
||||||
gauge.dec();
|
gauge.dec();
|
||||||
@@ -427,24 +426,6 @@ struct PageServerHandler {
|
|||||||
claims: Option<Claims>,
|
claims: Option<Claims>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TIME_BUCKETS: &[f64] = &[
|
|
||||||
0.00001, // 1/100000 s
|
|
||||||
0.0001, 0.00015, 0.0002, 0.00025, 0.0003, 0.00035, 0.0005, 0.00075, // 1/10000 s
|
|
||||||
0.001, 0.0025, 0.005, 0.0075, // 1/1000 s
|
|
||||||
0.01, 0.0125, 0.015, 0.025, 0.05, // 1/100 s
|
|
||||||
0.1, // 1/10 s
|
|
||||||
];
|
|
||||||
|
|
||||||
static SMGR_QUERY_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
|
||||||
register_histogram_vec!(
|
|
||||||
"pageserver_smgr_query_seconds",
|
|
||||||
"Time spent on smgr query handling",
|
|
||||||
&["smgr_query_type", "tenant_id", "timeline_id"],
|
|
||||||
TIME_BUCKETS.into()
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
impl PageServerHandler {
|
impl PageServerHandler {
|
||||||
pub fn new(conf: &'static PageServerConf, auth: Option<Arc<JwtAuth>>) -> Self {
|
pub fn new(conf: &'static PageServerConf, auth: Option<Arc<JwtAuth>>) -> Self {
|
||||||
PageServerHandler {
|
PageServerHandler {
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ use std::{
|
|||||||
|
|
||||||
use anyhow::{anyhow, bail, Context};
|
use anyhow::{anyhow, bail, Context};
|
||||||
use futures::stream::{FuturesUnordered, StreamExt};
|
use futures::stream::{FuturesUnordered, StreamExt};
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
use once_cell::sync::OnceCell;
|
||||||
use remote_storage::GenericRemoteStorage;
|
use remote_storage::GenericRemoteStorage;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs,
|
fs,
|
||||||
@@ -170,6 +170,7 @@ use self::{
|
|||||||
index::{IndexPart, RemoteTimeline, RemoteTimelineIndex},
|
index::{IndexPart, RemoteTimeline, RemoteTimelineIndex},
|
||||||
upload::{upload_index_part, upload_timeline_layers, UploadedTimeline},
|
upload::{upload_index_part, upload_timeline_layers, UploadedTimeline},
|
||||||
};
|
};
|
||||||
|
use crate::metrics::{IMAGE_SYNC_TIME, REMAINING_SYNC_ITEMS, REMOTE_INDEX_UPLOAD};
|
||||||
use crate::{
|
use crate::{
|
||||||
config::PageServerConf,
|
config::PageServerConf,
|
||||||
exponential_backoff,
|
exponential_backoff,
|
||||||
@@ -183,44 +184,12 @@ use crate::{
|
|||||||
thread_mgr::ThreadKind,
|
thread_mgr::ThreadKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use metrics::{
|
|
||||||
register_histogram_vec, register_int_counter_vec, register_int_gauge, HistogramVec,
|
|
||||||
IntCounterVec, IntGauge,
|
|
||||||
};
|
|
||||||
use utils::zid::{ZTenantId, ZTenantTimelineId, ZTimelineId};
|
use utils::zid::{ZTenantId, ZTenantTimelineId, ZTimelineId};
|
||||||
|
|
||||||
use self::download::download_index_parts;
|
use self::download::download_index_parts;
|
||||||
pub use self::download::gather_tenant_timelines_index_parts;
|
pub use self::download::gather_tenant_timelines_index_parts;
|
||||||
pub use self::download::TEMP_DOWNLOAD_EXTENSION;
|
pub use self::download::TEMP_DOWNLOAD_EXTENSION;
|
||||||
|
|
||||||
static REMAINING_SYNC_ITEMS: Lazy<IntGauge> = Lazy::new(|| {
|
|
||||||
register_int_gauge!(
|
|
||||||
"pageserver_remote_storage_remaining_sync_items",
|
|
||||||
"Number of storage sync items left in the queue"
|
|
||||||
)
|
|
||||||
.expect("failed to register pageserver remote storage remaining sync items int gauge")
|
|
||||||
});
|
|
||||||
|
|
||||||
static IMAGE_SYNC_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
|
||||||
register_histogram_vec!(
|
|
||||||
"pageserver_remote_storage_image_sync_seconds",
|
|
||||||
"Time took to synchronize (download or upload) a whole pageserver image. \
|
|
||||||
Grouped by tenant and timeline ids, `operation_kind` (upload|download) and `status` (success|failure)",
|
|
||||||
&["tenant_id", "timeline_id", "operation_kind", "status"],
|
|
||||||
vec![0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 3.0, 10.0, 20.0]
|
|
||||||
)
|
|
||||||
.expect("failed to register pageserver image sync time histogram vec")
|
|
||||||
});
|
|
||||||
|
|
||||||
static REMOTE_INDEX_UPLOAD: Lazy<IntCounterVec> = Lazy::new(|| {
|
|
||||||
register_int_counter_vec!(
|
|
||||||
"pageserver_remote_storage_remote_index_uploads_total",
|
|
||||||
"Number of remote index uploads",
|
|
||||||
&["tenant_id", "timeline_id"],
|
|
||||||
)
|
|
||||||
.expect("failed to register pageserver remote index upload vec")
|
|
||||||
});
|
|
||||||
|
|
||||||
static SYNC_QUEUE: OnceCell<SyncQueue> = OnceCell::new();
|
static SYNC_QUEUE: OnceCell<SyncQueue> = OnceCell::new();
|
||||||
|
|
||||||
/// A timeline status to share with pageserver's sync counterpart,
|
/// A timeline status to share with pageserver's sync counterpart,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use std::{fmt::Debug, path::PathBuf};
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use futures::stream::{FuturesUnordered, StreamExt};
|
use futures::stream::{FuturesUnordered, StreamExt};
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use remote_storage::GenericRemoteStorage;
|
use remote_storage::GenericRemoteStorage;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
@@ -15,19 +14,10 @@ use super::{
|
|||||||
index::{IndexPart, RemoteTimeline},
|
index::{IndexPart, RemoteTimeline},
|
||||||
LayersUpload, SyncData, SyncQueue,
|
LayersUpload, SyncData, SyncQueue,
|
||||||
};
|
};
|
||||||
|
use crate::metrics::NO_LAYERS_UPLOAD;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::PageServerConf, layered_repository::metadata::metadata_path, storage_sync::SyncTask,
|
config::PageServerConf, layered_repository::metadata::metadata_path, storage_sync::SyncTask,
|
||||||
};
|
};
|
||||||
use metrics::{register_int_counter_vec, IntCounterVec};
|
|
||||||
|
|
||||||
static NO_LAYERS_UPLOAD: Lazy<IntCounterVec> = Lazy::new(|| {
|
|
||||||
register_int_counter_vec!(
|
|
||||||
"pageserver_remote_storage_no_layers_uploads_total",
|
|
||||||
"Number of skipped uploads due to no layers",
|
|
||||||
&["tenant_id", "timeline_id"],
|
|
||||||
)
|
|
||||||
.expect("failed to register pageserver no layers upload vec")
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Serializes and uploads the given index part data to the remote storage.
|
/// Serializes and uploads the given index part data to the remote storage.
|
||||||
pub(super) async fn upload_index_part(
|
pub(super) async fn upload_index_part(
|
||||||
|
|||||||
@@ -5,28 +5,19 @@ use std::collections::HashMap;
|
|||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::metrics::TENANT_TASK_EVENTS;
|
||||||
use crate::tenant_mgr::TenantState;
|
use crate::tenant_mgr::TenantState;
|
||||||
use crate::thread_mgr::ThreadKind;
|
use crate::thread_mgr::ThreadKind;
|
||||||
use crate::{tenant_mgr, thread_mgr};
|
use crate::{tenant_mgr, thread_mgr};
|
||||||
use anyhow::{self, Context};
|
use anyhow::{self, Context};
|
||||||
use futures::stream::FuturesUnordered;
|
use futures::stream::FuturesUnordered;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use metrics::{register_int_counter_vec, IntCounterVec};
|
use once_cell::sync::OnceCell;
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
use utils::zid::ZTenantId;
|
use utils::zid::ZTenantId;
|
||||||
|
|
||||||
static TENANT_TASK_EVENTS: Lazy<IntCounterVec> = Lazy::new(|| {
|
|
||||||
register_int_counter_vec!(
|
|
||||||
"pageserver_tenant_task_events",
|
|
||||||
"Number of task start/stop/fail events.",
|
|
||||||
&["event"],
|
|
||||||
)
|
|
||||||
.expect("Failed to register tenant_task_events metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Compaction task's main loop
|
/// Compaction task's main loop
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
//! This is similar to PostgreSQL's virtual file descriptor facility in
|
//! This is similar to PostgreSQL's virtual file descriptor facility in
|
||||||
//! src/backend/storage/file/fd.c
|
//! src/backend/storage/file/fd.c
|
||||||
//!
|
//!
|
||||||
use once_cell::sync::Lazy;
|
use crate::metrics::{STORAGE_IO_SIZE, STORAGE_IO_TIME};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Error, ErrorKind, Read, Seek, SeekFrom, Write};
|
use std::io::{Error, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||||
@@ -19,38 +19,6 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::sync::{RwLock, RwLockWriteGuard};
|
use std::sync::{RwLock, RwLockWriteGuard};
|
||||||
|
|
||||||
use metrics::{register_histogram_vec, register_int_gauge_vec, HistogramVec, IntGaugeVec};
|
|
||||||
|
|
||||||
// Metrics collected on disk IO operations
|
|
||||||
const STORAGE_IO_TIME_BUCKETS: &[f64] = &[
|
|
||||||
0.000001, // 1 usec
|
|
||||||
0.00001, // 10 usec
|
|
||||||
0.0001, // 100 usec
|
|
||||||
0.001, // 1 msec
|
|
||||||
0.01, // 10 msec
|
|
||||||
0.1, // 100 msec
|
|
||||||
1.0, // 1 sec
|
|
||||||
];
|
|
||||||
|
|
||||||
static STORAGE_IO_TIME: Lazy<HistogramVec> = Lazy::new(|| {
|
|
||||||
register_histogram_vec!(
|
|
||||||
"pageserver_io_operations_seconds",
|
|
||||||
"Time spent in IO operations",
|
|
||||||
&["operation", "tenant_id", "timeline_id"],
|
|
||||||
STORAGE_IO_TIME_BUCKETS.into()
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static STORAGE_IO_SIZE: Lazy<IntGaugeVec> = Lazy::new(|| {
|
|
||||||
register_int_gauge_vec!(
|
|
||||||
"pageserver_io_operations_bytes_total",
|
|
||||||
"Total amount of bytes read/written in IO operations",
|
|
||||||
&["operation", "tenant_id", "timeline_id"]
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// A virtual file descriptor. You can use this just like std::fs::File, but internally
|
/// A virtual file descriptor. You can use this just like std::fs::File, but internally
|
||||||
/// the underlying file is closed if the system is low on file descriptors,
|
/// the underlying file is closed if the system is low on file descriptors,
|
||||||
@@ -85,7 +53,6 @@ pub struct VirtualFile {
|
|||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
open_options: OpenOptions,
|
open_options: OpenOptions,
|
||||||
|
|
||||||
/// For metrics
|
|
||||||
tenantid: String,
|
tenantid: String,
|
||||||
timelineid: String,
|
timelineid: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use tokio_postgres::{replication::ReplicationStream, Client};
|
|||||||
use tracing::{debug, error, info, info_span, trace, warn, Instrument};
|
use tracing::{debug, error, info, info_span, trace, warn, Instrument};
|
||||||
|
|
||||||
use super::TaskEvent;
|
use super::TaskEvent;
|
||||||
|
use crate::metrics::LIVE_CONNECTIONS_COUNT;
|
||||||
use crate::{
|
use crate::{
|
||||||
layered_repository::WalReceiverInfo, tenant_mgr, walingest::WalIngest,
|
layered_repository::WalReceiverInfo, tenant_mgr, walingest::WalIngest,
|
||||||
walrecord::DecodedWALRecord,
|
walrecord::DecodedWALRecord,
|
||||||
@@ -105,7 +106,7 @@ pub async fn handle_walreceiver_connection(
|
|||||||
// Immediately increment the gauge, then create a job to decrement it on task exit.
|
// Immediately increment the gauge, then create a job to decrement it on task exit.
|
||||||
// One of the pros of `defer!` is that this will *most probably*
|
// One of the pros of `defer!` is that this will *most probably*
|
||||||
// get called, even in presence of panics.
|
// get called, even in presence of panics.
|
||||||
let gauge = crate::LIVE_CONNECTIONS_COUNT.with_label_values(&["wal_receiver"]);
|
let gauge = LIVE_CONNECTIONS_COUNT.with_label_values(&["wal_receiver"]);
|
||||||
gauge.inc();
|
gauge.inc();
|
||||||
scopeguard::defer! {
|
scopeguard::defer! {
|
||||||
gauge.dec();
|
gauge.dec();
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use nix::poll::*;
|
use nix::poll::*;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
@@ -39,11 +38,13 @@ use tracing::*;
|
|||||||
use utils::{bin_ser::BeSer, lsn::Lsn, nonblock::set_nonblock, zid::ZTenantId};
|
use utils::{bin_ser::BeSer, lsn::Lsn, nonblock::set_nonblock, zid::ZTenantId};
|
||||||
|
|
||||||
use crate::config::PageServerConf;
|
use crate::config::PageServerConf;
|
||||||
|
use crate::metrics::{
|
||||||
|
WAL_REDO_RECORDS_HISTOGRAM, WAL_REDO_RECORD_COUNTER, WAL_REDO_TIME, WAL_REDO_WAIT_TIME,
|
||||||
|
};
|
||||||
use crate::pgdatadir_mapping::{key_to_rel_block, key_to_slru_block};
|
use crate::pgdatadir_mapping::{key_to_rel_block, key_to_slru_block};
|
||||||
use crate::reltag::{RelTag, SlruKind};
|
use crate::reltag::{RelTag, SlruKind};
|
||||||
use crate::repository::Key;
|
use crate::repository::Key;
|
||||||
use crate::walrecord::ZenithWalRecord;
|
use crate::walrecord::ZenithWalRecord;
|
||||||
use metrics::{register_histogram, register_int_counter, Histogram, IntCounter};
|
|
||||||
use postgres_ffi::v14::nonrelfile_utils::{
|
use postgres_ffi::v14::nonrelfile_utils::{
|
||||||
mx_offset_to_flags_bitshift, mx_offset_to_flags_offset, mx_offset_to_member_offset,
|
mx_offset_to_flags_bitshift, mx_offset_to_flags_offset, mx_offset_to_member_offset,
|
||||||
transaction_id_set_status,
|
transaction_id_set_status,
|
||||||
@@ -83,70 +84,6 @@ pub trait WalRedoManager: Send + Sync {
|
|||||||
) -> Result<Bytes, WalRedoError>;
|
) -> Result<Bytes, WalRedoError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metrics collected on WAL redo operations
|
|
||||||
//
|
|
||||||
// We collect the time spent in actual WAL redo ('redo'), and time waiting
|
|
||||||
// for access to the postgres process ('wait') since there is only one for
|
|
||||||
// each tenant.
|
|
||||||
|
|
||||||
/// Time buckets are small because we want to be able to measure the
|
|
||||||
/// smallest redo processing times. These buckets allow us to measure down
|
|
||||||
/// to 5us, which equates to 200'000 pages/sec, which equates to 1.6GB/sec.
|
|
||||||
/// This is much better than the previous 5ms aka 200 pages/sec aka 1.6MB/sec.
|
|
||||||
macro_rules! redo_histogram_time_buckets {
|
|
||||||
() => {
|
|
||||||
vec![
|
|
||||||
0.000_005, 0.000_010, 0.000_025, 0.000_050, 0.000_100, 0.000_250, 0.000_500, 0.001_000,
|
|
||||||
0.002_500, 0.005_000, 0.010_000, 0.025_000, 0.050_000,
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// While we're at it, also measure the amount of records replayed in each
|
|
||||||
/// operation. We have a global 'total replayed' counter, but that's not
|
|
||||||
/// as useful as 'what is the skew for how many records we replay in one
|
|
||||||
/// operation'.
|
|
||||||
macro_rules! redo_histogram_count_buckets {
|
|
||||||
() => {
|
|
||||||
vec![0.0, 1.0, 2.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static WAL_REDO_TIME: Lazy<Histogram> = Lazy::new(|| {
|
|
||||||
register_histogram!(
|
|
||||||
"pageserver_wal_redo_seconds",
|
|
||||||
"Time spent on WAL redo",
|
|
||||||
redo_histogram_time_buckets!()
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static WAL_REDO_WAIT_TIME: Lazy<Histogram> = Lazy::new(|| {
|
|
||||||
register_histogram!(
|
|
||||||
"pageserver_wal_redo_wait_seconds",
|
|
||||||
"Time spent waiting for access to the WAL redo process",
|
|
||||||
redo_histogram_time_buckets!(),
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static WAL_REDO_RECORDS_HISTOGRAM: Lazy<Histogram> = Lazy::new(|| {
|
|
||||||
register_histogram!(
|
|
||||||
"pageserver_wal_redo_records_histogram",
|
|
||||||
"Histogram of number of records replayed per redo",
|
|
||||||
redo_histogram_count_buckets!(),
|
|
||||||
)
|
|
||||||
.expect("failed to define a metric")
|
|
||||||
});
|
|
||||||
|
|
||||||
static WAL_REDO_RECORD_COUNTER: Lazy<IntCounter> = Lazy::new(|| {
|
|
||||||
register_int_counter!(
|
|
||||||
"pageserver_replayed_wal_records_total",
|
|
||||||
"Number of WAL records replayed in WAL redo process"
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// This is the real implementation that uses a Postgres process to
|
/// This is the real implementation that uses a Postgres process to
|
||||||
/// perform WAL replay. Only one thread can use the process at a time,
|
/// perform WAL replay. Only one thread can use the process at a time,
|
||||||
|
|||||||
@@ -16,8 +16,11 @@ class Metrics:
|
|||||||
def query_all(self, name: str, filter: Dict[str, str]) -> List[Sample]:
|
def query_all(self, name: str, filter: Dict[str, str]) -> List[Sample]:
|
||||||
res = []
|
res = []
|
||||||
for sample in self.metrics[name]:
|
for sample in self.metrics[name]:
|
||||||
if all(sample.labels[k] == v for k, v in filter.items()):
|
try:
|
||||||
res.append(sample)
|
if all(sample.labels[k] == v for k, v in filter.items()):
|
||||||
|
res.append(sample)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def query_one(self, name: str, filter: Dict[str, str] = {}) -> Sample:
|
def query_one(self, name: str, filter: Dict[str, str] = {}) -> Sample:
|
||||||
@@ -34,3 +37,27 @@ def parse_metrics(text: str, name: str = ""):
|
|||||||
metrics.metrics[sample.name].append(sample)
|
metrics.metrics[sample.name].append(sample)
|
||||||
|
|
||||||
return metrics
|
return metrics
|
||||||
|
|
||||||
|
|
||||||
|
PAGESERVER_PER_TENANT_METRICS = [
|
||||||
|
"pageserver_current_logical_size",
|
||||||
|
"pageserver_current_physical_size",
|
||||||
|
"pageserver_getpage_reconstruct_seconds_bucket",
|
||||||
|
"pageserver_getpage_reconstruct_seconds_count",
|
||||||
|
"pageserver_getpage_reconstruct_seconds_sum",
|
||||||
|
"pageserver_io_operations_bytes_total",
|
||||||
|
"pageserver_io_operations_seconds_bucket",
|
||||||
|
"pageserver_io_operations_seconds_count",
|
||||||
|
"pageserver_io_operations_seconds_sum",
|
||||||
|
"pageserver_last_record_lsn",
|
||||||
|
"pageserver_materialized_cache_hits_total",
|
||||||
|
"pageserver_smgr_query_seconds_bucket",
|
||||||
|
"pageserver_smgr_query_seconds_count",
|
||||||
|
"pageserver_smgr_query_seconds_sum",
|
||||||
|
"pageserver_storage_operations_seconds_bucket",
|
||||||
|
"pageserver_storage_operations_seconds_count",
|
||||||
|
"pageserver_storage_operations_seconds_sum",
|
||||||
|
"pageserver_wait_lsn_seconds_bucket",
|
||||||
|
"pageserver_wait_lsn_seconds_count",
|
||||||
|
"pageserver_wait_lsn_seconds_sum",
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import os
|
import os
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from fixtures.log_helper import log
|
from fixtures.log_helper import log
|
||||||
from fixtures.metrics import parse_metrics
|
from fixtures.metrics import PAGESERVER_PER_TENANT_METRICS, parse_metrics
|
||||||
from fixtures.neon_fixtures import NeonEnvBuilder
|
from fixtures.neon_fixtures import NeonEnvBuilder
|
||||||
from fixtures.types import Lsn
|
from fixtures.types import Lsn, ZTenantId
|
||||||
|
from prometheus_client.samples import Sample
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("with_safekeepers", [False, True])
|
@pytest.mark.parametrize("with_safekeepers", [False, True])
|
||||||
@@ -122,3 +124,46 @@ def test_metrics_normal_work(neon_env_builder: NeonEnvBuilder):
|
|||||||
log.info(
|
log.info(
|
||||||
f"process_start_time_seconds (UTC): {datetime.fromtimestamp(metrics.query_one('process_start_time_seconds').value)}"
|
f"process_start_time_seconds (UTC): {datetime.fromtimestamp(metrics.query_one('process_start_time_seconds').value)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pageserver_metrics_removed_after_detach(neon_env_builder: NeonEnvBuilder):
|
||||||
|
"""Tests that when a tenant is detached, the tenant specific metrics are not left behind"""
|
||||||
|
|
||||||
|
neon_env_builder.num_safekeepers = 3
|
||||||
|
|
||||||
|
env = neon_env_builder.init_start()
|
||||||
|
tenant_1, _ = env.neon_cli.create_tenant()
|
||||||
|
tenant_2, _ = env.neon_cli.create_tenant()
|
||||||
|
|
||||||
|
env.neon_cli.create_timeline("test_metrics_removed_after_detach", tenant_id=tenant_1)
|
||||||
|
env.neon_cli.create_timeline("test_metrics_removed_after_detach", tenant_id=tenant_2)
|
||||||
|
|
||||||
|
pg_tenant1 = env.postgres.create_start("test_metrics_removed_after_detach", tenant_id=tenant_1)
|
||||||
|
pg_tenant2 = env.postgres.create_start("test_metrics_removed_after_detach", tenant_id=tenant_2)
|
||||||
|
|
||||||
|
for pg in [pg_tenant1, pg_tenant2]:
|
||||||
|
with closing(pg.connect()) as conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("CREATE TABLE t(key int primary key, value text)")
|
||||||
|
cur.execute("INSERT INTO t SELECT generate_series(1,100000), 'payload'")
|
||||||
|
cur.execute("SELECT sum(key) FROM t")
|
||||||
|
assert cur.fetchone() == (5000050000,)
|
||||||
|
|
||||||
|
def get_ps_metric_samples_for_tenant(tenant_id: ZTenantId) -> List[Sample]:
|
||||||
|
ps_metrics = parse_metrics(env.pageserver.http_client().get_metrics(), "pageserver")
|
||||||
|
samples = []
|
||||||
|
for metric_name in ps_metrics.metrics:
|
||||||
|
for sample in ps_metrics.query_all(
|
||||||
|
name=metric_name, filter={"tenant_id": str(tenant_id)}
|
||||||
|
):
|
||||||
|
samples.append(sample)
|
||||||
|
return samples
|
||||||
|
|
||||||
|
for tenant in [tenant_1, tenant_2]:
|
||||||
|
pre_detach_samples = set([x.name for x in get_ps_metric_samples_for_tenant(tenant)])
|
||||||
|
assert pre_detach_samples == set(PAGESERVER_PER_TENANT_METRICS)
|
||||||
|
|
||||||
|
env.pageserver.http_client().tenant_detach(tenant)
|
||||||
|
|
||||||
|
post_detach_samples = set([x.name for x in get_ps_metric_samples_for_tenant(tenant)])
|
||||||
|
assert post_detach_samples == set()
|
||||||
|
|||||||
Reference in New Issue
Block a user