diff --git a/pageserver/src/feature_resolver.rs b/pageserver/src/feature_resolver.rs
index 65cac8eea1..f0178fd9b3 100644
--- a/pageserver/src/feature_resolver.rs
+++ b/pageserver/src/feature_resolver.rs
@@ -409,11 +409,12 @@ impl TenantFeatureResolver {
/// Refresh the cached properties and flags on the critical path.
pub fn refresh_properties_and_flags(&self, tenant_shard: &TenantShard) {
- let mut remote_size_mb = None;
+ let mut remote_size_mb = Some(0.0);
for timeline in tenant_shard.list_timelines() {
let size = timeline.metrics.resident_physical_size_get();
if size == 0 {
remote_size_mb = None;
+ break;
}
if let Some(ref mut remote_size_mb) = remote_size_mb {
*remote_size_mb += size as f64 / 1024.0 / 1024.0;
diff --git a/pageserver/src/http/routes.rs b/pageserver/src/http/routes.rs
index 2995a37089..3612686b5d 100644
--- a/pageserver/src/http/routes.rs
+++ b/pageserver/src/http/routes.rs
@@ -3691,6 +3691,23 @@ async fn read_tar_eof(mut reader: (impl tokio::io::AsyncRead + Unpin)) -> anyhow
Ok(())
}
+async fn force_refresh_feature_flag(
+ request: Request
,
+ _cancel: CancellationToken,
+) -> Result, ApiError> {
+ let tenant_shard_id: TenantShardId = parse_request_param(&request, "tenant_shard_id")?;
+ check_permission(&request, Some(tenant_shard_id.tenant_id))?;
+
+ let state = get_state(&request);
+ let tenant = state
+ .tenant_manager
+ .get_attached_tenant_shard(tenant_shard_id)?;
+ tenant
+ .feature_resolver
+ .refresh_properties_and_flags(&tenant);
+ json_response(StatusCode::OK, ())
+}
+
async fn tenant_evaluate_feature_flag(
request: Request,
_cancel: CancellationToken,
@@ -4156,6 +4173,9 @@ pub fn make_router(
.get("/v1/tenant/:tenant_shard_id/feature_flag/:flag_key", |r| {
api_handler(r, tenant_evaluate_feature_flag)
})
+ .post("/v1/tenant/:tenant_shard_id/force_refresh_feature_flag", |r| {
+ api_handler(r, force_refresh_feature_flag)
+ })
.put("/v1/feature_flag/:flag_key", |r| {
testing_api_handler("force override feature flag - put", r, force_override_feature_flag_for_testing_put)
})
diff --git a/test_runner/fixtures/pageserver/http.py b/test_runner/fixtures/pageserver/http.py
index d9037f2d08..79cfba8da6 100644
--- a/test_runner/fixtures/pageserver/http.py
+++ b/test_runner/fixtures/pageserver/http.py
@@ -1247,3 +1247,10 @@ class PageserverHttpClient(requests.Session, MetricsGetter):
)
self.verbose_error(res)
return res.json()
+
+ def force_refresh_feature_flag(self, tenant_id: TenantId | TenantShardId):
+ res = self.post(
+ f"http://localhost:{self.port}/v1/tenant/{tenant_id}/force_refresh_feature_flag",
+ )
+ self.verbose_error(res)
+ return res.json()
diff --git a/test_runner/regress/test_feature_flag.py b/test_runner/regress/test_feature_flag.py
index 2712d13dcc..c6c192b6f1 100644
--- a/test_runner/regress/test_feature_flag.py
+++ b/test_runner/regress/test_feature_flag.py
@@ -49,3 +49,12 @@ def test_feature_flag(neon_env_builder: NeonEnvBuilder):
env.initial_tenant, "test-feature-flag"
)["result"]
)
+
+ env.pageserver.http_client().force_refresh_feature_flag(env.initial_tenant)
+
+ # Check if the properties exist
+ result = env.pageserver.http_client().evaluate_feature_flag_multivariate(
+ env.initial_tenant, "test-feature-flag"
+ )
+ assert "tenant_remote_size_mb" in result["properties"]
+ assert "tenant_id" in result["properties"]