From 5df8284961ef9f7feda558f9830a014f351b731e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arpad=20M=C3=BCller?= Date: Tue, 20 May 2025 17:34:38 +0200 Subject: [PATCH] Add http API for templates on timeline creation --- libs/pageserver_api/src/models.rs | 5 +++++ pageserver/src/http/openapi_spec.yml | 6 ++++++ pageserver/src/http/routes.rs | 8 ++++++++ pageserver/src/tenant.rs | 27 +++++++++++++++++++++++++-- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/libs/pageserver_api/src/models.rs b/libs/pageserver_api/src/models.rs index e9b37c8ca6..5c86b37df4 100644 --- a/libs/pageserver_api/src/models.rs +++ b/libs/pageserver_api/src/models.rs @@ -325,6 +325,7 @@ impl TimelineCreateRequest { match &self.mode { TimelineCreateRequestMode::Branch { .. } => "branch", TimelineCreateRequestMode::ImportPgdata { .. } => "import", + TimelineCreateRequestMode::Template { .. } => "template", TimelineCreateRequestMode::Bootstrap { .. } => "bootstrap", } } @@ -406,6 +407,10 @@ pub enum TimelineCreateRequestMode { ImportPgdata { import_pgdata: TimelineCreateRequestModeImportPgdata, }, + Template { + template_tenant_id: TenantId, + template_timeline_id: TimelineId, + }, // NB: Bootstrap is all-optional, and thus the serde(untagged) will cause serde to stop at Bootstrap. // (serde picks the first matching enum variant, in declaration order). Bootstrap { diff --git a/pageserver/src/http/openapi_spec.yml b/pageserver/src/http/openapi_spec.yml index 7ea148971f..3c21594a87 100644 --- a/pageserver/src/http/openapi_spec.yml +++ b/pageserver/src/http/openapi_spec.yml @@ -629,6 +629,12 @@ paths: existing_initdb_timeline_id: type: string format: hex + template_tenant_id: + type: string + format: hex + template_timeline_id: + type: string + format: hex import_pgdata: $ref: "#/components/schemas/TimelineCreateRequestImportPgdata" responses: diff --git a/pageserver/src/http/routes.rs b/pageserver/src/http/routes.rs index 0b36eb5df7..efd148e946 100644 --- a/pageserver/src/http/routes.rs +++ b/pageserver/src/http/routes.rs @@ -607,6 +607,14 @@ async fn timeline_create_handler( } }, }), + TimelineCreateRequestMode::Template { + template_tenant_id, + template_timeline_id, + } => tenant::CreateTimelineParams::Template(tenant::CreateTimelineParamsTemplate { + new_timeline_id, + template_tenant_id, + template_timeline_id, + }), }; let ctx = RequestContext::new(TaskKind::MgmtRequest, DownloadBehavior::Error); diff --git a/pageserver/src/tenant.rs b/pageserver/src/tenant.rs index fffd1f4090..b2a509ed60 100644 --- a/pageserver/src/tenant.rs +++ b/pageserver/src/tenant.rs @@ -107,7 +107,7 @@ use crate::{InitializationOrder, TEMP_FILE_SUFFIX, import_datadir, span, task_mg static INIT_DB_SEMAPHORE: Lazy = Lazy::new(|| Semaphore::new(8)); use utils::crashsafe; use utils::generation::Generation; -use utils::id::TimelineId; +use utils::id::{TenantId, TimelineId}; use utils::lsn::{Lsn, RecordLsn}; pub mod blob_io; @@ -864,6 +864,7 @@ impl Debug for SetStoppingError { #[derive(Debug)] pub(crate) enum CreateTimelineParams { Bootstrap(CreateTimelineParamsBootstrap), + Template(CreateTimelineParamsTemplate), Branch(CreateTimelineParamsBranch), ImportPgdata(CreateTimelineParamsImportPgdata), } @@ -875,6 +876,13 @@ pub(crate) struct CreateTimelineParamsBootstrap { pub(crate) pg_version: u32, } +#[derive(Debug)] +pub(crate) struct CreateTimelineParamsTemplate { + pub(crate) new_timeline_id: TimelineId, + pub(crate) template_tenant_id: TenantId, + pub(crate) template_timeline_id: TimelineId, +} + /// NB: See comment on [`CreateTimelineIdempotency::Branch`] for why there's no `pg_version` here. #[derive(Debug)] pub(crate) struct CreateTimelineParamsBranch { @@ -2665,6 +2673,9 @@ impl TenantShard { CreateTimelineParams::ImportPgdata(params) => { self.create_timeline_import_pgdata(params, ctx).await? } + CreateTimelineParams::Template(params) => { + self.create_timeline_from_template(params, ctx).await? + } }; // At this point we have dropped our guard on [`Self::timelines_creating`], and @@ -2729,6 +2740,19 @@ impl TenantShard { Ok(activated_timeline) } + async fn create_timeline_from_template( + self: &Arc, + params: CreateTimelineParamsTemplate, + ctx: &RequestContext, + ) -> Result { + let CreateTimelineParamsTemplate { + new_timeline_id, + template_tenant_id, + template_timeline_id, + } = params; + todo!() + } + /// The returned [`Arc`] is NOT in the [`TenantShard::timelines`] map until the import /// completes in the background. A DIFFERENT [`Arc`] will be inserted into the /// [`TenantShard::timelines`] map when the import completes. @@ -5683,7 +5707,6 @@ pub(crate) mod harness { use pageserver_api::models::ShardParameters; use pageserver_api::record::NeonWalRecord; use pageserver_api::shard::ShardIndex; - use utils::id::TenantId; use utils::logging; use super::*;