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):
"""