diff --git a/libs/safekeeper_api/src/models.rs b/libs/safekeeper_api/src/models.rs index 10c703395f..6bdc651668 100644 --- a/libs/safekeeper_api/src/models.rs +++ b/libs/safekeeper_api/src/models.rs @@ -221,6 +221,11 @@ pub struct TimelineMembershipSwitchResponse { pub current_conf: Configuration, } +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct TimelineDeleteResult { + pub dir_existed: bool, +} + fn lsn_invalid() -> Lsn { Lsn::INVALID } diff --git a/safekeeper/client/src/mgmt_api.rs b/safekeeper/client/src/mgmt_api.rs index 3966aa811f..7ae39ef95e 100644 --- a/safekeeper/client/src/mgmt_api.rs +++ b/safekeeper/client/src/mgmt_api.rs @@ -8,7 +8,7 @@ use std::error::Error as _; use http_utils::error::HttpErrorBody; use reqwest::{IntoUrl, Method, StatusCode}; use safekeeper_api::models::{ - PullTimelineRequest, PullTimelineResponse, SafekeeperUtilization, TimelineCreateRequest, + self, PullTimelineRequest, PullTimelineResponse, SafekeeperUtilization, TimelineCreateRequest, TimelineStatus, }; use utils::id::{NodeId, TenantId, TimelineId}; @@ -96,11 +96,25 @@ impl Client { resp.json().await.map_err(Error::ReceiveBody) } + pub async fn exclude_timeline( + &self, + tenant_id: TenantId, + timeline_id: TimelineId, + req: &models::TimelineMembershipSwitchRequest, + ) -> Result { + let uri = format!( + "{}/v1/tenant/{}/timeline/{}/exclude", + self.mgmt_api_endpoint, tenant_id, timeline_id + ); + let resp = self.put(&uri, req).await?; + resp.json().await.map_err(Error::ReceiveBody) + } + pub async fn delete_timeline( &self, tenant_id: TenantId, timeline_id: TimelineId, - ) -> Result { + ) -> Result { let uri = format!( "{}/v1/tenant/{}/timeline/{}", self.mgmt_api_endpoint, tenant_id, timeline_id @@ -109,6 +123,20 @@ impl Client { resp.json().await.map_err(Error::ReceiveBody) } + pub async fn bump_timeline_term( + &self, + tenant_id: TenantId, + timeline_id: TimelineId, + req: &models::TimelineTermBumpRequest, + ) -> Result { + let uri = format!( + "{}/v1/tenant/{}/timeline/{}/term_bump", + self.mgmt_api_endpoint, tenant_id, timeline_id + ); + let resp = self.post(&uri, req).await?; + resp.json().await.map_err(Error::ReceiveBody) + } + pub async fn timeline_status( &self, tenant_id: TenantId, @@ -149,6 +177,14 @@ impl Client { self.request(Method::POST, uri, body).await } + async fn put( + &self, + uri: U, + body: B, + ) -> Result { + self.request(Method::PUT, uri, body).await + } + async fn get(&self, uri: U) -> Result { self.request(Method::GET, uri, ()).await } diff --git a/safekeeper/src/http/routes.rs b/safekeeper/src/http/routes.rs index 4f47331c85..21293671e1 100644 --- a/safekeeper/src/http/routes.rs +++ b/safekeeper/src/http/routes.rs @@ -17,7 +17,8 @@ use hyper::{Body, Request, Response, StatusCode}; use postgres_ffi::WAL_SEGMENT_SIZE; use safekeeper_api::models::{ AcceptorStateStatus, PullTimelineRequest, SafekeeperStatus, SkTimelineInfo, TermSwitchApiEntry, - TimelineCopyRequest, TimelineCreateRequest, TimelineStatus, TimelineTermBumpRequest, + TimelineCopyRequest, TimelineCreateRequest, TimelineDeleteResult, TimelineStatus, + TimelineTermBumpRequest, }; use safekeeper_api::{ServerInfo, membership, models}; use storage_broker::proto::{SafekeeperTimelineInfo, TenantTimelineId as ProtoTenantTimelineId}; @@ -32,7 +33,7 @@ use utils::lsn::Lsn; use crate::debug_dump::TimelineDigestRequest; use crate::safekeeper::TermLsn; -use crate::timelines_global_map::{DeleteOrExclude, TimelineDeleteResult}; +use crate::timelines_global_map::DeleteOrExclude; use crate::{ GlobalTimelines, SafeKeeperConf, copy_timeline, debug_dump, patch_control_file, pull_timeline, }; diff --git a/safekeeper/src/timelines_global_map.rs b/safekeeper/src/timelines_global_map.rs index 858dfce807..41abee369e 100644 --- a/safekeeper/src/timelines_global_map.rs +++ b/safekeeper/src/timelines_global_map.rs @@ -11,9 +11,8 @@ use anyhow::{Context, Result, bail}; use camino::Utf8PathBuf; use camino_tempfile::Utf8TempDir; use safekeeper_api::membership::Configuration; -use safekeeper_api::models::SafekeeperUtilization; +use safekeeper_api::models::{SafekeeperUtilization, TimelineDeleteResult}; use safekeeper_api::{ServerInfo, membership}; -use serde::Serialize; use tokio::fs; use tracing::*; use utils::crashsafe::{durable_rename, fsync_async_opt}; @@ -579,11 +578,6 @@ impl GlobalTimelines { } } -#[derive(Clone, Copy, Serialize)] -pub struct TimelineDeleteResult { - pub dir_existed: bool, -} - /// Action for delete_or_exclude. #[derive(Clone, Debug)] pub enum DeleteOrExclude { diff --git a/storage_controller/src/safekeeper_client.rs b/storage_controller/src/safekeeper_client.rs index 1533b6c086..a44fcc27d2 100644 --- a/storage_controller/src/safekeeper_client.rs +++ b/storage_controller/src/safekeeper_client.rs @@ -1,5 +1,5 @@ use safekeeper_api::models::{ - PullTimelineRequest, PullTimelineResponse, SafekeeperUtilization, TimelineCreateRequest, + self, PullTimelineRequest, PullTimelineResponse, SafekeeperUtilization, TimelineCreateRequest, TimelineStatus, }; use safekeeper_client::mgmt_api::{Client, Result}; @@ -69,11 +69,28 @@ impl SafekeeperClient { ) } + #[allow(unused)] + pub(crate) async fn exclude_timeline( + &self, + tenant_id: TenantId, + timeline_id: TimelineId, + req: &models::TimelineMembershipSwitchRequest, + ) -> Result { + measured_request!( + "exclude_timeline", + crate::metrics::Method::Post, + &self.node_id_label, + self.inner + .exclude_timeline(tenant_id, timeline_id, req) + .await + ) + } + pub(crate) async fn delete_timeline( &self, tenant_id: TenantId, timeline_id: TimelineId, - ) -> Result { + ) -> Result { measured_request!( "delete_timeline", crate::metrics::Method::Delete, @@ -94,6 +111,23 @@ impl SafekeeperClient { ) } + #[allow(unused)] + pub(crate) async fn bump_timeline_term( + &self, + tenant_id: TenantId, + timeline_id: TimelineId, + req: &models::TimelineTermBumpRequest, + ) -> Result { + measured_request!( + "term_bump", + crate::metrics::Method::Post, + &self.node_id_label, + self.inner + .bump_timeline_term(tenant_id, timeline_id, req) + .await + ) + } + pub(crate) async fn get_utilization(&self) -> Result { measured_request!( "utilization",