diff --git a/pageserver/src/http/routes.rs b/pageserver/src/http/routes.rs index b0b07428f9..7279ce0f63 100644 --- a/pageserver/src/http/routes.rs +++ b/pageserver/src/http/routes.rs @@ -496,7 +496,11 @@ async fn tenant_size_handler(request: Request) -> Result, A .map_err(ApiError::InternalServerError)?; let size = if !inputs_only.unwrap_or(false) { - Some(inputs.calculate().map_err(ApiError::InternalServerError)?) + Some( + tenant + .calc_and_update_cached_synthetic_size(&inputs) + .map_err(ApiError::InternalServerError)?, + ) } else { None }; diff --git a/pageserver/src/metrics.rs b/pageserver/src/metrics.rs index 6bd0eddbb5..9d3d11eba8 100644 --- a/pageserver/src/metrics.rs +++ b/pageserver/src/metrics.rs @@ -150,6 +150,15 @@ pub static TENANT_STATE_METRIC: Lazy = Lazy::new(|| { .expect("Failed to register pageserver_tenant_states_count metric") }); +pub static TENANT_SYNTHETIC_SIZE_METRIC: Lazy = Lazy::new(|| { + register_uint_gauge_vec!( + "pageserver_tenant_synthetic_size", + "Synthetic size of each tenant", + &["tenant_id"] + ) + .expect("Failed to register pageserver_tenant_synthetic_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 = Lazy::new(|| { @@ -593,6 +602,7 @@ impl Drop for TimelineMetrics { pub fn remove_tenant_metrics(tenant_id: &TenantId) { let tid = tenant_id.to_string(); + let _ = TENANT_SYNTHETIC_SIZE_METRIC.remove_label_values(&[&tid]); for state in TENANT_STATE_OPTIONS { let _ = TENANT_STATE_METRIC.remove_label_values(&[&tid, state]); } diff --git a/pageserver/src/tenant.rs b/pageserver/src/tenant.rs index 4bcf9bb06a..bc943372f8 100644 --- a/pageserver/src/tenant.rs +++ b/pageserver/src/tenant.rs @@ -52,7 +52,7 @@ use crate::config::PageServerConf; use crate::context::{DownloadBehavior, RequestContext}; use crate::import_datadir; use crate::is_uninit_mark; -use crate::metrics::{remove_tenant_metrics, TENANT_STATE_METRIC}; +use crate::metrics::{remove_tenant_metrics, TENANT_STATE_METRIC, TENANT_SYNTHETIC_SIZE_METRIC}; use crate::repository::GcResult; use crate::task_mgr; use crate::task_mgr::TaskKind; @@ -2441,13 +2441,27 @@ impl Tenant { pub async fn calculate_synthetic_size(&self, ctx: &RequestContext) -> anyhow::Result { let inputs = self.gather_size_inputs(ctx).await?; + self.calc_and_update_cached_synthetic_size(&inputs) + } + + /// Calculate synthetic size , cache it and set metric value + pub fn calc_and_update_cached_synthetic_size( + &self, + inputs: &size::ModelInputs, + ) -> anyhow::Result { let size = inputs.calculate()?; self.cached_synthetic_tenant_size .store(size, Ordering::Relaxed); + TENANT_SYNTHETIC_SIZE_METRIC + .get_metric_with_label_values(&[&self.tenant_id.to_string()]) + .unwrap() + .set(size); + Ok(size) } + pub fn get_cached_synthetic_size(&self) -> u64 { self.cached_synthetic_tenant_size.load(Ordering::Relaxed) } diff --git a/test_runner/regress/test_tenant_size.py b/test_runner/regress/test_tenant_size.py index 72cfbc9dda..bb3bca8782 100644 --- a/test_runner/regress/test_tenant_size.py +++ b/test_runner/regress/test_tenant_size.py @@ -2,6 +2,7 @@ from typing import Any, List, Tuple import pytest from fixtures.log_helper import log +from fixtures.metrics import parse_metrics from fixtures.neon_fixtures import NeonEnv, NeonEnvBuilder, wait_for_last_flush_lsn from fixtures.types import Lsn @@ -368,6 +369,17 @@ def test_single_branch_get_tenant_size_grows(neon_env_builder: NeonEnvBuilder): assert size_after == prev, "size after restarting pageserver should not have changed" + ps_metrics = parse_metrics(http_client.get_metrics(), "pageserver") + tenant_metric_filter = { + "tenant_id": str(tenant_id), + } + + tenant_size_metric = int( + ps_metrics.query_one("pageserver_tenant_synthetic_size", filter=tenant_metric_filter).value + ) + + assert tenant_size_metric == size_after, "API size value should be equal to metric size value" + def test_get_tenant_size_with_multiple_branches(neon_env_builder: NeonEnvBuilder): """