Skip to content

Commit b0f3409

Browse files
authored
Add safekeeper utilization endpoint (#10429)
Add an endpoint to obtain the utilization of a safekeeper. Future changes to the storage controller can use this endpoint to find the most suitable safekeepers for newly created timelines, analogously to how it's done for pageservers already. Initially we just want to assign by timeline count, then we can iterate from there. Part of #9011
1 parent 6975228 commit b0f3409

File tree

4 files changed

+33
-0
lines changed

4 files changed

+33
-0
lines changed

libs/safekeeper_api/src/models.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,8 @@ pub struct TimelineTermBumpResponse {
277277
pub previous_term: u64,
278278
pub current_term: u64,
279279
}
280+
281+
#[derive(Debug, Clone, Deserialize, Serialize)]
282+
pub struct SafekeeperUtilization {
283+
pub timeline_count: u64,
284+
}

safekeeper/client/src/mgmt_api.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ impl Client {
102102
self.get(&uri).await
103103
}
104104

105+
pub async fn utilization(&self) -> Result<reqwest::Response> {
106+
let uri = format!("{}/v1/utilization/", self.mgmt_api_endpoint);
107+
self.get(&uri).await
108+
}
109+
105110
async fn get<U: IntoUrl>(&self, uri: U) -> Result<reqwest::Response> {
106111
self.request(Method::GET, uri, ()).await
107112
}

safekeeper/src/http/routes.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ async fn timeline_create_handler(mut request: Request<Body>) -> Result<Response<
127127
json_response(StatusCode::OK, ())
128128
}
129129

130+
async fn utilization_handler(request: Request<Body>) -> Result<Response<Body>, ApiError> {
131+
check_permission(&request, None)?;
132+
let global_timelines = get_global_timelines(&request);
133+
let utilization = global_timelines.get_timeline_counts();
134+
json_response(StatusCode::OK, utilization)
135+
}
136+
130137
/// List all (not deleted) timelines.
131138
/// Note: it is possible to do the same with debug_dump.
132139
async fn timeline_list_handler(request: Request<Body>) -> Result<Response<Body>, ApiError> {
@@ -620,6 +627,7 @@ pub fn make_router(
620627
failpoints_handler(r, cancel).await
621628
})
622629
})
630+
.get("/v1/uzilization", |r| request_span(r, utilization_handler))
623631
.delete("/v1/tenant/:tenant_id", |r| {
624632
request_span(r, tenant_delete_handler)
625633
})

safekeeper/src/timelines_global_map.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use anyhow::{bail, Context, Result};
1313
use camino::Utf8PathBuf;
1414
use camino_tempfile::Utf8TempDir;
1515
use safekeeper_api::membership::Configuration;
16+
use safekeeper_api::models::SafekeeperUtilization;
1617
use safekeeper_api::ServerInfo;
1718
use serde::Serialize;
1819
use std::collections::HashMap;
@@ -416,6 +417,20 @@ impl GlobalTimelines {
416417
.collect()
417418
}
418419

420+
/// Returns statistics about timeline counts
421+
pub fn get_timeline_counts(&self) -> SafekeeperUtilization {
422+
let global_lock = self.state.lock().unwrap();
423+
let timeline_count = global_lock
424+
.timelines
425+
.values()
426+
.filter(|t| match t {
427+
GlobalMapTimeline::CreationInProgress => false,
428+
GlobalMapTimeline::Timeline(t) => !t.is_cancelled(),
429+
})
430+
.count() as u64;
431+
SafekeeperUtilization { timeline_count }
432+
}
433+
419434
/// Returns all timelines belonging to a given tenant. Used for deleting all timelines of a tenant,
420435
/// and that's why it can return cancelled timelines, to retry deleting them.
421436
fn get_all_for_tenant(&self, tenant_id: TenantId) -> Vec<Arc<Timeline>> {

0 commit comments

Comments
 (0)