From 24d5d8edbe5b3dc37c19788ce4ed52008f5aa343 Mon Sep 17 00:00:00 2001 From: Alex Chi Z Date: Mon, 6 May 2024 12:02:06 -0400 Subject: [PATCH] feat(pageserver): add metrics for aux file size Signed-off-by: Alex Chi Z --- pageserver/src/aux_file.rs | 47 ++++++++++++++++++++++++++ pageserver/src/context.rs | 6 ++++ pageserver/src/pgdatadir_mapping.rs | 52 +++++++++++++++++++++-------- pageserver/src/tenant/timeline.rs | 13 ++++++-- 4 files changed, 102 insertions(+), 16 deletions(-) diff --git a/pageserver/src/aux_file.rs b/pageserver/src/aux_file.rs index b5daf9c65ea2d..3b6076d09ff13 100644 --- a/pageserver/src/aux_file.rs +++ b/pageserver/src/aux_file.rs @@ -1,4 +1,10 @@ +use std::sync::{ + atomic::{AtomicIsize, AtomicUsize}, + Arc, +}; + use bytes::{Buf, BufMut}; +use futures::lock::Mutex; use pageserver_api::key::{Key, AUX_KEY_PREFIX, METADATA_KEY_SIZE}; use tracing::warn; @@ -112,6 +118,47 @@ pub fn encode_file_value(files: &[(&str, &[u8])]) -> anyhow::Result> { Ok(encoded) } +/// An estimation of the size of aux files. +pub struct AuxFileSizeEstimator { + size: Arc>>, +} + +impl AuxFileSizeEstimator { + pub fn new() -> Self { + Self { + size: Arc::new(std::sync::Mutex::new(None)), + } + } + + pub fn on_base_backup(&self, new_size: usize) { + let mut guard = self.size.lock().unwrap(); + *guard = Some(new_size as isize); + } + + pub fn on_add(&self, file_size: usize) { + let mut guard = self.size.lock().unwrap(); + if let Some(size) = &mut *guard { + *size += file_size as isize; + } + } + + pub fn on_remove(&self, file_size: usize) { + let mut guard = self.size.lock().unwrap(); + if let Some(size) = &mut *guard { + *size -= file_size as isize; + } + } + + pub fn on_update(&self, old_size: usize, new_size: usize) { + let mut guard = self.size.lock().unwrap(); + if let Some(size) = &mut *guard { + *size += new_size as isize - old_size as isize; + } + } + + pub fn report(&self) {} +} + #[cfg(test)] mod tests { use super::*; diff --git a/pageserver/src/context.rs b/pageserver/src/context.rs index 86d0390c30b17..a76d720cfdfa5 100644 --- a/pageserver/src/context.rs +++ b/pageserver/src/context.rs @@ -86,6 +86,8 @@ //! [`RequestContext`] argument. Functions in the middle of the call chain //! only need to pass it on. +use std::sync::atomic::AtomicUsize; + use crate::task_mgr::TaskKind; pub(crate) mod optional_counter; @@ -98,6 +100,8 @@ pub struct RequestContext { access_stats_behavior: AccessStatsBehavior, page_content_kind: PageContentKind, pub micros_spent_throttled: optional_counter::MicroSecondsCounterU32, + /// Total number of kilobytes of layer files processed in this request. + pub vectored_access_delta_file_size_kb: AtomicUsize, } /// The kind of access to the page cache. @@ -154,6 +158,7 @@ impl RequestContextBuilder { access_stats_behavior: AccessStatsBehavior::Update, page_content_kind: PageContentKind::Unknown, micros_spent_throttled: Default::default(), + vectored_access_delta_file_size_kb: AtomicUsize::new(0), }, } } @@ -168,6 +173,7 @@ impl RequestContextBuilder { access_stats_behavior: original.access_stats_behavior, page_content_kind: original.page_content_kind, micros_spent_throttled: Default::default(), + vectored_access_delta_file_size_kb: AtomicUsize::new(0), }, } } diff --git a/pageserver/src/pgdatadir_mapping.rs b/pageserver/src/pgdatadir_mapping.rs index e8baa8e6d2fcc..c84306b62ba58 100644 --- a/pageserver/src/pgdatadir_mapping.rs +++ b/pageserver/src/pgdatadir_mapping.rs @@ -699,13 +699,17 @@ impl Timeline { .await .context("scan")?; let mut result = HashMap::new(); + let mut sz = 0; for (_, v) in kv { let v = v.context("get value")?; let v = aux_file::decode_file_value(&v).context("value decode")?; for (fname, content) in v { result.insert(fname.to_string(), content.to_vec().into()); + sz += fname.len(); + sz += content.len(); } } + self.aux_file_size_estimator.on_base_backup(sz); Ok(result) } @@ -1471,23 +1475,45 @@ impl<'a> DatadirModification<'a> { Err(PageReconstructError::MissingKey(_)) => None, Err(e) => return Err(e.into()), }; - let files = if let Some(ref old_val) = old_val { + let files: Vec<(&str, &[u8])> = if let Some(ref old_val) = old_val { aux_file::decode_file_value(old_val)? } else { Vec::new() }; - let new_files = if content.is_empty() { - files - .into_iter() - .filter(|(p, _)| &path != p) - .collect::>() - } else { - files - .into_iter() - .filter(|(p, _)| &path != p) - .chain(std::iter::once((path, content))) - .collect::>() - }; + let mut other_files = Vec::with_capacity(files.len()); + let mut modifying_file = None; + for file @ (p, content) in files { + if path == p { + assert!( + modifying_file.is_none(), + "duplicated entries found for {}", + path + ); + modifying_file = Some(content); + } else { + other_files.push(file); + } + } + let mut new_files = other_files; + match (modifying_file, content.is_empty()) { + (Some(old_content), false) => { + self.tline + .aux_file_size_estimator + .on_update(old_content.len(), content.len()); + new_files.push((path, content)); + } + (Some(old_content), true) => { + self.tline + .aux_file_size_estimator + .on_remove(old_content.len()); + // not adding the file key to the final `new_files` vec. + } + (None, false) => { + self.tline.aux_file_size_estimator.on_add(content.len()); + new_files.push((path, content)); + } + (None, true) => anyhow::bail!("removing non-existing aux file: {}", path), + } let new_val = aux_file::encode_file_value(&new_files)?; self.put(key, Value::Image(new_val.into())); } diff --git a/pageserver/src/tenant/timeline.rs b/pageserver/src/tenant/timeline.rs index 7b5533a89c57c..7cdd6d08f2861 100644 --- a/pageserver/src/tenant/timeline.rs +++ b/pageserver/src/tenant/timeline.rs @@ -59,9 +59,12 @@ use std::{ ops::ControlFlow, }; -use crate::tenant::{ - layer_map::{LayerMap, SearchResult}, - metadata::TimelineMetadata, +use crate::{ + aux_file::AuxFileSizeEstimator, + tenant::{ + layer_map::{LayerMap, SearchResult}, + metadata::TimelineMetadata, + }, }; use crate::{ context::{DownloadBehavior, RequestContext}, @@ -407,6 +410,8 @@ pub struct Timeline { /// Keep aux directory cache to avoid it's reconstruction on each update pub(crate) aux_files: tokio::sync::Mutex, + + pub(crate) aux_file_size_estimator: AuxFileSizeEstimator, } pub struct WalReceiverInfo { @@ -2253,6 +2258,8 @@ impl Timeline { dir: None, n_deltas: 0, }), + + aux_file_size_estimator: AuxFileSizeEstimator::new(), }; result.repartition_threshold = result.get_checkpoint_distance() / REPARTITION_FREQ_IN_CHECKPOINT_DISTANCE;