From 2d0930f622329f7ef4695ee1550c269758b5fc3b Mon Sep 17 00:00:00 2001 From: John Spray Date: Sun, 5 Nov 2023 14:17:42 +0000 Subject: [PATCH] neon_local: basic sharding support --- control_plane/src/bin/neon_local.rs | 201 +++++++++++++++++--------- control_plane/src/endpoint.rs | 66 +++++---- control_plane/src/pageserver.rs | 38 ++--- control_plane/src/tenant_migration.rs | 16 +- libs/pageserver_api/src/models.rs | 2 +- 5 files changed, 204 insertions(+), 119 deletions(-) diff --git a/control_plane/src/bin/neon_local.rs b/control_plane/src/bin/neon_local.rs index 8d53a6a658..9aa5dd6b05 100644 --- a/control_plane/src/bin/neon_local.rs +++ b/control_plane/src/bin/neon_local.rs @@ -15,7 +15,8 @@ use control_plane::pageserver::{PageServerNode, PAGESERVER_REMOTE_STORAGE_DIR}; use control_plane::safekeeper::SafekeeperNode; use control_plane::tenant_migration::migrate_tenant; use control_plane::{broker, local_env}; -use pageserver_api::models::TimelineInfo; +use pageserver_api::models::{LocationConfig, LocationConfigMode, TimelineInfo}; +use pageserver_api::shard::{ShardCount, ShardNumber, TenantShardId}; use pageserver_api::{ DEFAULT_HTTP_LISTEN_PORT as DEFAULT_PAGESERVER_HTTP_PORT, DEFAULT_PG_LISTEN_PORT as DEFAULT_PAGESERVER_PG_PORT, @@ -26,6 +27,7 @@ use safekeeper_api::{ DEFAULT_PG_LISTEN_PORT as DEFAULT_SAFEKEEPER_PG_PORT, }; use std::collections::{BTreeSet, HashMap}; +use std::num::ParseIntError; use std::path::PathBuf; use std::process::exit; use std::str::FromStr; @@ -97,6 +99,22 @@ struct TimelineTreeEl { pub children: BTreeSet, } +/// Helper for CLI args that contain a comma-separate list of NodeId +fn parse_ids_arg( + matches: &ArgMatches, + arg: &str, +) -> Result>, std::num::ParseIntError> { + if let Some(id_str) = matches.get_one::(arg) { + let r: Result, ParseIntError> = id_str + .split(',') + .map(|ps_id| u64::from_str(str::trim(ps_id)).map(NodeId)) + .collect(); + r.map(Some) + } else { + Ok(Some(vec![DEFAULT_PAGESERVER_ID])) + } +} + // Main entry point for the 'neon_local' CLI utility // // This utility helps to manage neon installation. That includes following: @@ -374,9 +392,10 @@ fn pageserver_config_overrides(init_match: &ArgMatches) -> Vec<&str> { } fn handle_tenant(tenant_match: &ArgMatches, env: &mut local_env::LocalEnv) -> anyhow::Result<()> { - let pageserver = get_default_pageserver(env); match tenant_match.subcommand() { Some(("list", _)) => { + // TODO: make command aware of multiple pageservers + let pageserver = get_default_pageserver(env); for t in pageserver.tenant_list()? { println!("{} {:?}", t.id, t.state); } @@ -387,38 +406,94 @@ fn handle_tenant(tenant_match: &ArgMatches, env: &mut local_env::LocalEnv) -> an .map(|vals| vals.flat_map(|c| c.split_once(':')).collect()) .unwrap_or_default(); + let shard_count: u8 = create_match + .get_one::("shard-count") + .cloned() + .unwrap_or(1); + // If tenant ID was not specified, generate one let tenant_id = parse_tenant_id(create_match)?.unwrap_or_else(TenantId::generate); - let generation = if env.control_plane_api.is_some() { - // We must register the tenant with the attachment service, so - // that when the pageserver restarts, it will be re-attached. - let attachment_service = AttachmentService::from_env(env); - attachment_service.attach_hook(tenant_id, pageserver.conf.id)? - } else { - None - }; - - pageserver.tenant_create(tenant_id, generation, tenant_conf)?; - println!("tenant {tenant_id} successfully created on the pageserver"); - - // Create an initial timeline for the new tenant - let new_timeline_id = parse_timeline_id(create_match)?; + // We will create an initial timeline for the new tenant + let new_timeline_id = + parse_timeline_id(create_match)?.unwrap_or(TimelineId::generate()); let pg_version = create_match .get_one::("pg-version") .copied() .context("Failed to parse postgres version from the argument string")?; - let timeline_info = pageserver.timeline_create( - tenant_id, - new_timeline_id, - None, - None, - Some(pg_version), - None, - )?; - let new_timeline_id = timeline_info.timeline_id; - let last_record_lsn = timeline_info.last_record_lsn; + // TODO: implement ability for one pageserver to hold multiple + // shards for the same tenant. Until then, we must place each + // shard on a different pageserver. + assert!(env.pageservers.len() >= shard_count as usize); + + let cfg_shard_count = if shard_count > 1 { + shard_count + } else { + // For single-sharded mode, use the legacy unsharded configuration. This avoids + // breaking any existing tests that assume legacy unsharded storage paths + 0 + }; + + for shard_number in 0..shard_count { + let ps_conf = env.pageservers.get(shard_number as usize).unwrap(); + let pageserver = PageServerNode::from_env(env, ps_conf); + + // TODO: per-shard generations + let generation = if env.control_plane_api.is_some() { + // We must register the tenant with the attachment service, so + // that when the pageserver restarts, it will be re-attached. + let attachment_service = AttachmentService::from_env(env); + attachment_service.attach_hook(tenant_id, pageserver.conf.id)? + } else { + None + }; + + // TODO: shard-aware POST /v1/tenant. Currently tenant creation on the + // pageserver is a no-op, but we shouldn't skip the command entirely. + + let tenant_conf = PageServerNode::build_config(tenant_conf.clone())?; + + let tenant_shard_id = TenantShardId { + shard_number: ShardNumber(shard_number), + shard_count: ShardCount(cfg_shard_count), + tenant_id, + }; + + let location_conf = LocationConfig { + shard_count: cfg_shard_count, + shard_number, + shard_stripe_size: 32768, + mode: LocationConfigMode::AttachedSingle, + generation, + secondary_conf: None, + tenant_conf, + }; + pageserver.location_config(tenant_shard_id, location_conf, None)?; + println!( + "tenant {tenant_id} successfully created on pageserver {}", + pageserver.conf.id + ); + } + + for shard_number in 0..shard_count { + let ps_conf = env.pageservers.get(shard_number as usize).unwrap(); + let pageserver = PageServerNode::from_env(env, ps_conf); + let tenant_shard_id = TenantShardId { + shard_number: ShardNumber(shard_number), + shard_count: ShardCount(cfg_shard_count), + tenant_id, + }; + + pageserver.timeline_create( + tenant_shard_id, + Some(new_timeline_id), + None, + None, + Some(pg_version), + None, + )?; + } env.register_branch_mapping( DEFAULT_BRANCH_NAME.to_string(), @@ -426,9 +501,7 @@ fn handle_tenant(tenant_match: &ArgMatches, env: &mut local_env::LocalEnv) -> an new_timeline_id, )?; - println!( - "Created an initial timeline '{new_timeline_id}' at Lsn {last_record_lsn} for tenant: {tenant_id}", - ); + println!("Created an initial timeline '{new_timeline_id}' for tenant: {tenant_id}",); if create_match.get_flag("set-default") { println!("Setting tenant {tenant_id} as a default one"); @@ -448,6 +521,8 @@ fn handle_tenant(tenant_match: &ArgMatches, env: &mut local_env::LocalEnv) -> an .map(|vals| vals.flat_map(|c| c.split_once(':')).collect()) .unwrap_or_default(); + // TODO: make command aware of multiple pageservers + let pageserver = get_default_pageserver(env); pageserver .tenant_config(tenant_id, tenant_conf) .with_context(|| format!("Tenant config failed for tenant with id {tenant_id}"))?; @@ -491,7 +566,7 @@ fn handle_timeline(timeline_match: &ArgMatches, env: &mut local_env::LocalEnv) - let new_timeline_id_opt = parse_timeline_id(create_match)?; let timeline_info = pageserver.timeline_create( - tenant_id, + TenantShardId::unsharded(tenant_id), new_timeline_id_opt, None, None, @@ -554,7 +629,7 @@ fn handle_timeline(timeline_match: &ArgMatches, env: &mut local_env::LocalEnv) - None, pg_version, ComputeMode::Primary, - DEFAULT_PAGESERVER_ID, + vec![DEFAULT_PAGESERVER_ID], )?; println!("Done"); } @@ -579,7 +654,7 @@ fn handle_timeline(timeline_match: &ArgMatches, env: &mut local_env::LocalEnv) - .transpose() .context("Failed to parse ancestor start Lsn from the request")?; let timeline_info = pageserver.timeline_create( - tenant_id, + TenantShardId::unsharded(tenant_id), None, start_lsn, Some(ancestor_timeline_id), @@ -704,13 +779,8 @@ fn handle_endpoint(ep_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<( .copied() .unwrap_or(false); - let pageserver_id = - if let Some(id_str) = sub_args.get_one::("endpoint-pageserver-id") { - NodeId(id_str.parse().context("while parsing pageserver id")?) - } else { - DEFAULT_PAGESERVER_ID - }; - + let pageserver_ids = parse_ids_arg(sub_args, "endpoint-pageserver-id")? + .unwrap_or(vec![DEFAULT_PAGESERVER_ID]); let mode = match (lsn, hot_standby) { (Some(lsn), false) => ComputeMode::Static(lsn), (None, true) => ComputeMode::Replica, @@ -738,7 +808,7 @@ fn handle_endpoint(ep_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<( http_port, pg_version, mode, - pageserver_id, + pageserver_ids, )?; } "start" => { @@ -746,29 +816,14 @@ fn handle_endpoint(ep_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<( .get_one::("endpoint_id") .ok_or_else(|| anyhow!("No endpoint ID was provided to start"))?; - let pageserver_id = - if let Some(id_str) = sub_args.get_one::("endpoint-pageserver-id") { - NodeId(id_str.parse().context("while parsing pageserver id")?) - } else { - DEFAULT_PAGESERVER_ID - }; + let pageservers = parse_ids_arg(sub_args, "endpoint-pageserver-id")? + .unwrap_or(vec![DEFAULT_PAGESERVER_ID]); let remote_ext_config = sub_args.get_one::("remote-ext-config"); // If --safekeepers argument is given, use only the listed safekeeper nodes. - let safekeepers = - if let Some(safekeepers_str) = sub_args.get_one::("safekeepers") { - let mut safekeepers: Vec = Vec::new(); - for sk_id in safekeepers_str.split(',').map(str::trim) { - let sk_id = NodeId(u64::from_str(sk_id).map_err(|_| { - anyhow!("invalid node ID \"{sk_id}\" in --safekeepers list") - })?); - safekeepers.push(sk_id); - } - safekeepers - } else { - env.safekeepers.iter().map(|sk| sk.id).collect() - }; + let safekeepers = parse_ids_arg(sub_args, "safekeepers")? + .unwrap_or_else(|| env.safekeepers.iter().map(|sk| sk.id).collect()); let endpoint = cplane .endpoints @@ -781,7 +836,8 @@ fn handle_endpoint(ep_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<( endpoint.timeline_id, )?; - let ps_conf = env.get_pageserver_conf(pageserver_id)?; + // We assume that all pageservers have the same auth conf + let ps_conf = env.get_pageserver_conf(pageservers[0])?; let auth_token = if matches!(ps_conf.pg_auth_type, AuthType::NeonJWT) { let claims = Claims::new(Some(endpoint.tenant_id), Scope::Tenant); @@ -801,15 +857,21 @@ fn handle_endpoint(ep_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<( .endpoints .get(endpoint_id.as_str()) .with_context(|| format!("postgres endpoint {endpoint_id} is not found"))?; - let pageserver_id = - if let Some(id_str) = sub_args.get_one::("endpoint-pageserver-id") { - Some(NodeId( - id_str.parse().context("while parsing pageserver id")?, - )) - } else { - None - }; - endpoint.reconfigure(pageserver_id)?; + let pageserver_ids: Option, _>> = sub_args + .get_many::("endpoint-pageserver-id") + .map(|ids| { + ids.map(|id_str| id_str.parse().context("while parsing pageserver id")) + .map(|r| r.map(NodeId)) + .collect() + }); + + let pageserver_ids = match pageserver_ids { + Some(Ok(v)) => Ok(Some(v)), + Some(Err(e)) => Err(e), + None => Ok(None), + }?; + + endpoint.reconfigure(pageserver_ids)?; } "stop" => { let endpoint_id = sub_args @@ -1313,6 +1375,7 @@ fn cli() -> Command { .arg(pg_version_arg.clone()) .arg(Arg::new("set-default").long("set-default").action(ArgAction::SetTrue).required(false) .help("Use this tenant in future CLI commands where tenant_id is needed, but not specified")) + .arg(Arg::new("shard-count").value_parser(value_parser!(u8)).long("shard-count").action(ArgAction::Set).help("Number of shards in the new tenant (default 1)")) ) .subcommand(Command::new("set-default").arg(tenant_id_arg.clone().required(true)) .about("Set a particular tenant as default in future CLI commands where tenant_id is needed, but not specified")) diff --git a/control_plane/src/endpoint.rs b/control_plane/src/endpoint.rs index a566f03db9..1607b540a7 100644 --- a/control_plane/src/endpoint.rs +++ b/control_plane/src/endpoint.rs @@ -67,7 +67,7 @@ pub struct EndpointConf { http_port: u16, pg_version: u32, skip_pg_catalog_updates: bool, - pageserver_id: NodeId, + pageservers: Vec, } // @@ -82,6 +82,33 @@ pub struct ComputeControlPlane { env: LocalEnv, } +fn load_pageservers( + env: &LocalEnv, + pageserver_ids: &Vec, +) -> anyhow::Result> { + let mut pageservers = Vec::new(); + for ps_id in pageserver_ids { + let pageserver = env + .get_pageserver_conf(*ps_id) + .map(|conf| PageServerNode::from_env(env, conf))?; + pageservers.push(pageserver); + } + Ok(pageservers) +} + +fn build_pageserver_connstr(pageservers: &[PageServerNode]) -> String { + pageservers + .iter() + .map(|ps| { + let config = ps.pg_connection_config.clone(); + let (host, port) = (config.host(), config.port()); + // NOTE: avoid spaces in connection string, because it is less error prone if we forward it somewhere. + format!("postgresql://no_user@{host}:{port}") + }) + .collect::>() + .join(",") +} + impl ComputeControlPlane { // Load current endpoints from the endpoints/ subdirectories pub fn load(env: LocalEnv) -> Result { @@ -119,19 +146,16 @@ impl ComputeControlPlane { http_port: Option, pg_version: u32, mode: ComputeMode, - pageserver_id: NodeId, + pageservers: Vec, ) -> Result> { let pg_port = pg_port.unwrap_or_else(|| self.get_port()); let http_port = http_port.unwrap_or_else(|| self.get_port() + 1); - let pageserver = - PageServerNode::from_env(&self.env, self.env.get_pageserver_conf(pageserver_id)?); - let ep = Arc::new(Endpoint { endpoint_id: endpoint_id.to_owned(), pg_address: SocketAddr::new("127.0.0.1".parse().unwrap(), pg_port), http_address: SocketAddr::new("127.0.0.1".parse().unwrap(), http_port), env: self.env.clone(), - pageserver, + pageservers: load_pageservers(&self.env, &pageservers)?, timeline_id, mode, tenant_id, @@ -157,7 +181,7 @@ impl ComputeControlPlane { pg_port, pg_version, skip_pg_catalog_updates: true, - pageserver_id, + pageservers, })?, )?; std::fs::write( @@ -216,7 +240,7 @@ pub struct Endpoint { // These are not part of the endpoint as such, but the environment // the endpoint runs in. pub env: LocalEnv, - pageserver: PageServerNode, + pageservers: Vec, // Optimizations skip_pg_catalog_updates: bool, @@ -239,15 +263,14 @@ impl Endpoint { let conf: EndpointConf = serde_json::from_slice(&std::fs::read(entry.path().join("endpoint.json"))?)?; - let pageserver = - PageServerNode::from_env(env, env.get_pageserver_conf(conf.pageserver_id)?); + let pageservers: Vec = load_pageservers(env, &conf.pageservers)?; Ok(Endpoint { pg_address: SocketAddr::new("127.0.0.1".parse().unwrap(), conf.pg_port), http_address: SocketAddr::new("127.0.0.1".parse().unwrap(), conf.http_port), endpoint_id, env: env.clone(), - pageserver, + pageservers, timeline_id: conf.timeline_id, mode: conf.mode, tenant_id: conf.tenant_id, @@ -482,13 +505,7 @@ impl Endpoint { std::fs::remove_dir_all(self.pgdata())?; } - let pageserver_connstring = { - let config = &self.pageserver.pg_connection_config; - let (host, port) = (config.host(), config.port()); - - // NOTE: avoid spaces in connection string, because it is less error prone if we forward it somewhere. - format!("postgresql://no_user@{host}:{port}") - }; + let pageserver_connstring = build_pageserver_connstr(&self.pageservers); let mut safekeeper_connstrings = Vec::new(); if self.mode == ComputeMode::Primary { for sk_id in safekeepers { @@ -658,7 +675,7 @@ impl Endpoint { } } - pub fn reconfigure(&self, pageserver_id: Option) -> Result<()> { + pub fn reconfigure(&self, pageservers: Option>) -> Result<()> { let mut spec: ComputeSpec = { let spec_path = self.endpoint_path().join("spec.json"); let file = std::fs::File::open(spec_path)?; @@ -668,23 +685,20 @@ impl Endpoint { let postgresql_conf = self.read_postgresql_conf()?; spec.cluster.postgresql_conf = Some(postgresql_conf); - if let Some(pageserver_id) = pageserver_id { + if let Some(pageservers) = pageservers { let endpoint_config_path = self.endpoint_path().join("endpoint.json"); let mut endpoint_conf: EndpointConf = { let file = std::fs::File::open(&endpoint_config_path)?; serde_json::from_reader(file)? }; - endpoint_conf.pageserver_id = pageserver_id; + endpoint_conf.pageservers = pageservers.clone(); std::fs::write( endpoint_config_path, serde_json::to_string_pretty(&endpoint_conf)?, )?; - let pageserver = - PageServerNode::from_env(&self.env, self.env.get_pageserver_conf(pageserver_id)?); - let ps_http_conf = &pageserver.pg_connection_config; - let (host, port) = (ps_http_conf.host(), ps_http_conf.port()); - spec.pageserver_connstring = Some(format!("postgresql://no_user@{host}:{port}")); + let pageservers = load_pageservers(&self.env, &pageservers)?; + spec.pageserver_connstring = Some(build_pageserver_connstr(&pageservers)); } let client = reqwest::blocking::Client::new(); diff --git a/control_plane/src/pageserver.rs b/control_plane/src/pageserver.rs index 96a41874fd..1e02aee0bf 100644 --- a/control_plane/src/pageserver.rs +++ b/control_plane/src/pageserver.rs @@ -340,15 +340,8 @@ impl PageServerNode { .json()?) } - pub fn tenant_create( - &self, - new_tenant_id: TenantId, - generation: Option, - settings: HashMap<&str, &str>, - ) -> anyhow::Result { - let mut settings = settings.clone(); - - let config = models::TenantConfig { + pub fn build_config(mut settings: HashMap<&str, &str>) -> anyhow::Result { + Ok(models::TenantConfig { checkpoint_distance: settings .remove("checkpoint_distance") .map(|x| x.parse::()) @@ -407,8 +400,16 @@ impl PageServerNode { .map(|x| x.parse::()) .transpose() .context("Failed to parse 'gc_feedback' as bool")?, - }; + }) + } + pub fn tenant_create( + &self, + new_tenant_id: TenantId, + generation: Option, + settings: HashMap<&str, &str>, + ) -> anyhow::Result { + let config = Self::build_config(settings.clone())?; let request = models::TenantCreateRequest { new_tenant_id: TenantShardId::unsharded(new_tenant_id), generation, @@ -521,15 +522,18 @@ impl PageServerNode { pub fn location_config( &self, - tenant_id: TenantId, + tenant_shard_id: TenantShardId, config: LocationConfig, flush_ms: Option, ) -> anyhow::Result<()> { - let req_body = TenantLocationConfigRequest { tenant_id, config }; + let req_body = TenantLocationConfigRequest { + tenant_shard_id, + config, + }; let path = format!( "{}/tenant/{}/location_config", - self.http_base_url, tenant_id + self.http_base_url, tenant_shard_id ); let path = if let Some(flush_ms) = flush_ms { format!("{}?flush_ms={}", path, flush_ms.as_millis()) @@ -560,7 +564,7 @@ impl PageServerNode { pub fn timeline_create( &self, - tenant_id: TenantId, + tenant_shard_id: TenantShardId, new_timeline_id: Option, ancestor_start_lsn: Option, ancestor_timeline_id: Option, @@ -572,7 +576,7 @@ impl PageServerNode { self.http_request( Method::POST, - format!("{}/tenant/{}/timeline", self.http_base_url, tenant_id), + format!("{}/tenant/{}/timeline", self.http_base_url, tenant_shard_id), )? .json(&models::TimelineCreateRequest { new_timeline_id, @@ -585,11 +589,11 @@ impl PageServerNode { .error_from_body()? .json::>() .with_context(|| { - format!("Failed to parse timeline creation response for tenant id: {tenant_id}") + format!("Failed to parse timeline creation response for tenant id: {tenant_shard_id}") })? .with_context(|| { format!( - "No timeline id was found in the timeline creation response for tenant {tenant_id}" + "No timeline id was found in the timeline creation response for tenant {tenant_shard_id}" ) }) } diff --git a/control_plane/src/tenant_migration.rs b/control_plane/src/tenant_migration.rs index c0c44e279f..9c74d1b106 100644 --- a/control_plane/src/tenant_migration.rs +++ b/control_plane/src/tenant_migration.rs @@ -11,6 +11,7 @@ use crate::{ use pageserver_api::models::{ LocationConfig, LocationConfigMode, LocationConfigSecondary, TenantConfig, }; +use pageserver_api::shard::TenantShardId; use std::collections::HashMap; use std::time::Duration; use utils::{ @@ -108,6 +109,9 @@ pub fn migrate_tenant( } } + // No support for sharding in this function yet + let tenant_shard_id = TenantShardId::unsharded(tenant_id); + let previous = attachment_service.inspect(tenant_id)?; let mut baseline_lsns = None; if let Some((generation, origin_ps_id)) = &previous { @@ -117,7 +121,7 @@ pub fn migrate_tenant( println!("🔁 Already attached to {origin_ps_id}, freshening..."); let gen = attachment_service.attach_hook(tenant_id, dest_ps.conf.id)?; let dest_conf = build_location_config(LocationConfigMode::AttachedSingle, gen, None); - dest_ps.location_config(tenant_id, dest_conf, None)?; + dest_ps.location_config(tenant_shard_id, dest_conf, None)?; println!("✅ Migration complete"); return Ok(()); } @@ -126,7 +130,7 @@ pub fn migrate_tenant( let stale_conf = build_location_config(LocationConfigMode::AttachedStale, Some(*generation), None); - origin_ps.location_config(tenant_id, stale_conf, Some(Duration::from_secs(10)))?; + origin_ps.location_config(tenant_shard_id, stale_conf, Some(Duration::from_secs(10)))?; baseline_lsns = Some(get_lsns(tenant_id, &origin_ps)?); } @@ -135,7 +139,7 @@ pub fn migrate_tenant( let dest_conf = build_location_config(LocationConfigMode::AttachedMulti, gen, None); println!("🔁 Attaching to pageserver {}", dest_ps.conf.id); - dest_ps.location_config(tenant_id, dest_conf, None)?; + dest_ps.location_config(tenant_shard_id, dest_conf, None)?; if let Some(baseline) = baseline_lsns { println!("🕑 Waiting for LSN to catch up..."); @@ -149,7 +153,7 @@ pub fn migrate_tenant( "🔁 Reconfiguring endpoint {} to use pageserver {}", endpoint_name, dest_ps.conf.id ); - endpoint.reconfigure(Some(dest_ps.conf.id))?; + endpoint.reconfigure(Some(vec![dest_ps.conf.id]))?; } } @@ -181,7 +185,7 @@ pub fn migrate_tenant( "💤 Switching to secondary mode on pageserver {}", other_ps.conf.id ); - other_ps.location_config(tenant_id, secondary_conf, None)?; + other_ps.location_config(tenant_shard_id, secondary_conf, None)?; } println!( @@ -189,7 +193,7 @@ pub fn migrate_tenant( dest_ps.conf.id ); let dest_conf = build_location_config(LocationConfigMode::AttachedSingle, gen, None); - dest_ps.location_config(tenant_id, dest_conf, None)?; + dest_ps.location_config(tenant_shard_id, dest_conf, None)?; println!("✅ Migration complete"); diff --git a/libs/pageserver_api/src/models.rs b/libs/pageserver_api/src/models.rs index 2234a06501..a0aa265755 100644 --- a/libs/pageserver_api/src/models.rs +++ b/libs/pageserver_api/src/models.rs @@ -293,7 +293,7 @@ pub struct StatusResponse { #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct TenantLocationConfigRequest { - pub tenant_id: TenantId, + pub tenant_shard_id: TenantShardId, #[serde(flatten)] pub config: LocationConfig, // as we have a flattened field, we should reject all unknown fields in it }