neon_local: improved timeline creation and 'branch'

This commit is contained in:
John Spray
2023-12-12 16:28:58 +00:00
parent 583375e6f6
commit 14b0acbda7
4 changed files with 47 additions and 32 deletions

View File

@@ -3,7 +3,7 @@ use anyhow::anyhow;
use camino::Utf8PathBuf;
use hyper::{Method, StatusCode};
use pageserver_api::{
models::{TenantCreateRequest, TenantShardSplitRequest, TimelineCreateRequest},
models::{TenantCreateRequest, TenantShardSplitRequest, TimelineCreateRequest, TimelineInfo},
shard::TenantShardId,
};
use postgres_connection::parse_host_port;
@@ -271,7 +271,7 @@ impl AttachmentService {
&self,
tenant_id: TenantId,
req: TimelineCreateRequest,
) -> anyhow::Result<()> {
) -> anyhow::Result<TimelineInfo> {
self.dispatch(
Method::POST,
format!("tenant/{tenant_id}/timeline"),

View File

@@ -11,7 +11,7 @@ use hyper::{Method, StatusCode};
use pageserver_api::models::{
LocationConfig, LocationConfigMode, TenantConfig, TenantCreateRequest,
TenantLocationConfigRequest, TenantShardSplitRequest, TenantShardSplitResponse,
TimelineCreateRequest,
TimelineCreateRequest, TimelineInfo,
};
use pageserver_api::shard::{ShardCount, ShardIdentity, ShardNumber, TenantShardId};
use reqwest::Client;
@@ -225,7 +225,7 @@ impl TenantState {
&self,
node: &NodeState,
req: &TimelineCreateRequest,
) -> anyhow::Result<()> {
) -> anyhow::Result<TimelineInfo> {
let client = Client::new();
let response = client
.request(
@@ -239,9 +239,9 @@ impl TenantState {
.json(req)
.send()
.await?;
response.error_for_status()?;
response.error_for_status_ref()?;
Ok(())
Ok(response.json().await?)
}
fn schedule(&mut self, scheduler: &mut Scheduler) -> Result<(), ScheduleError> {
@@ -620,7 +620,7 @@ async fn handle_tenant_create(mut req: Request<Body>) -> Result<Response<Body>,
async fn handle_tenant_timeline_create(mut req: Request<Body>) -> Result<Response<Body>, ApiError> {
let tenant_id: TenantId = parse_request_param(&req, "tenant_id")?;
let create_req = json_request::<TimelineCreateRequest>(&mut req).await?;
let mut create_req = json_request::<TimelineCreateRequest>(&mut req).await?;
let state = get_state(&req).inner.clone();
let mut locked = state.write().await;
@@ -637,6 +637,8 @@ async fn handle_tenant_timeline_create(mut req: Request<Body>) -> Result<Respons
// Take a snapshot of pageservers
let pageservers = locked.pageservers.clone();
let mut timeline_info = None;
for (_tenant_shard_id, shard) in locked
.tenants
.range_mut(TenantShardId::tenant_range(tenant_id))
@@ -649,13 +651,26 @@ async fn handle_tenant_timeline_create(mut req: Request<Body>) -> Result<Respons
.get(&node_id)
.expect("Pageservers may not be deleted while referenced");
shard
let shard_timeline_info = shard
.timeline_create(node, &create_req)
.await
.map_err(|e| ApiError::Conflict(format!("Failed to create timeline: {e}")))?;
if timeline_info.is_none() {
// If the caller specified an ancestor but no ancestor LSN, we are responsible for
// propagating the LSN chosen by the first shard to the other shards: it is important
// that all shards end up with the same ancestor_start_lsn.
if create_req.ancestor_timeline_id.is_some() && create_req.ancestor_start_lsn.is_none()
{
create_req.ancestor_start_lsn = shard_timeline_info.ancestor_lsn;
}
// We will return the TimelineInfo from the first shard
timeline_info = Some(shard_timeline_info);
}
}
json_response(StatusCode::OK, ())
json_response(StatusCode::OK, timeline_info)
}
async fn handle_tenant_locate(req: Request<Body>) -> Result<Response<Body>, ApiError> {

View File

@@ -603,18 +603,19 @@ async fn handle_timeline(timeline_match: &ArgMatches, env: &mut local_env::Local
.context("Failed to parse postgres version from the argument string")?;
let new_timeline_id_opt = parse_timeline_id(create_match)?;
let new_timeline_id = new_timeline_id_opt.unwrap_or(TimelineId::generate());
let timeline_info = pageserver
.timeline_create(
tenant_id,
new_timeline_id_opt,
None,
None,
Some(pg_version),
None,
)
let attachment_service = AttachmentService::from_env(env);
let create_req = TimelineCreateRequest {
new_timeline_id: new_timeline_id,
ancestor_timeline_id: None,
existing_initdb_timeline_id: None,
ancestor_start_lsn: None,
pg_version: Some(pg_version),
};
let timeline_info = attachment_service
.tenant_timeline_create(tenant_id, create_req)
.await?;
let new_timeline_id = timeline_info.timeline_id;
let last_record_lsn = timeline_info.last_record_lsn;
env.register_branch_mapping(new_branch_name.to_string(), tenant_id, new_timeline_id)?;
@@ -695,17 +696,18 @@ async fn handle_timeline(timeline_match: &ArgMatches, env: &mut local_env::Local
.map(|lsn_str| Lsn::from_str(lsn_str))
.transpose()
.context("Failed to parse ancestor start Lsn from the request")?;
let timeline_info = pageserver
.timeline_create(
tenant_id,
None,
start_lsn,
Some(ancestor_timeline_id),
None,
None,
)
let new_timeline_id = TimelineId::generate();
let attachment_service = AttachmentService::from_env(env);
let create_req = TimelineCreateRequest {
new_timeline_id: new_timeline_id,
ancestor_timeline_id: Some(ancestor_timeline_id),
existing_initdb_timeline_id: None,
ancestor_start_lsn: start_lsn,
pg_version: None,
};
let timeline_info = attachment_service
.tenant_timeline_create(tenant_id, create_req)
.await?;
let new_timeline_id = timeline_info.timeline_id;
let last_record_lsn = timeline_info.last_record_lsn;

View File

@@ -498,14 +498,12 @@ impl PageServerNode {
pub async fn timeline_create(
&self,
tenant_id: TenantId,
new_timeline_id: Option<TimelineId>,
new_timeline_id: TimelineId,
ancestor_start_lsn: Option<Lsn>,
ancestor_timeline_id: Option<TimelineId>,
pg_version: Option<u32>,
existing_initdb_timeline_id: Option<TimelineId>,
) -> anyhow::Result<TimelineInfo> {
// If timeline ID was not specified, generate one
let new_timeline_id = new_timeline_id.unwrap_or(TimelineId::generate());
let req = models::TimelineCreateRequest {
new_timeline_id,
ancestor_start_lsn,