Skip to content

Commit

Permalink
Don't upload duplicate manifests
Browse files Browse the repository at this point in the history
  • Loading branch information
jcsp committed Oct 29, 2024
1 parent c01aa89 commit 2f9a040
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 8 deletions.
26 changes: 20 additions & 6 deletions pageserver/src/tenant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,9 @@ pub struct Tenant {
/// Serialize writes of the tenant manifest to remote storage. If there are concurrent operations
/// affecting the manifest, such as timeline deletion and timeline offload, they must wait for
/// each other (this could be optimized to coalesce writes if necessary).
tenant_manifest_upload: tokio::sync::Mutex<()>,
///
/// The contents of the Mutex are the last manifest we successfully uploaded
tenant_manifest_upload: tokio::sync::Mutex<Option<TenantManifest>>,

// This mutex prevents creation of new timelines during GC.
// Adding yet another mutex (in addition to `timelines`) is needed because holding
Expand Down Expand Up @@ -3123,7 +3125,7 @@ impl Tenant {
}
}

let tenant_manifest = self.tenant_manifest();
let tenant_manifest = self.build_tenant_manifest();
// TODO: generation support
let generation = remote_timeline_client::TENANT_MANIFEST_GENERATION;
for child_shard in child_shards {
Expand Down Expand Up @@ -3318,7 +3320,8 @@ impl Tenant {
.unwrap_or(self.conf.default_tenant_conf.lsn_lease_length)
}

fn tenant_manifest(&self) -> TenantManifest {
/// Generate an up-to-date TenantManifest based on the state of this Tenant.
fn build_tenant_manifest(&self) -> TenantManifest {
let timelines_offloaded = self.timelines_offloaded.lock().unwrap();

let mut timeline_manifests = timelines_offloaded
Expand Down Expand Up @@ -4712,7 +4715,7 @@ impl Tenant {
// Only one manifest write may be done at at time, and the contents of the manifest
// must be loaded while holding this lock. This makes it safe to call this function
// from anywhere without worrying about colliding updates.
let _guard = tokio::select! {
let mut guard = tokio::select! {
g = self.tenant_manifest_upload.lock() => {
g
},
Expand All @@ -4721,7 +4724,12 @@ impl Tenant {
}
};

let manifest = self.tenant_manifest();
let manifest = self.build_tenant_manifest();
if Some(&manifest) == (*guard).as_ref() {
// Optimisation: skip uploads that don't change anything.
return Ok(());
}

upload_tenant_manifest(
&self.remote_storage,
&self.tenant_shard_id,
Expand All @@ -4736,7 +4744,13 @@ impl Tenant {
} else {
TenantManifestError::RemoteStorage(e)
}
})
})?;

// Store the successfully uploaded manifest, so that future callers can avoid
// re-uploading the same thing.
*guard = Some(manifest);

Ok(())
}
}

Expand Down
4 changes: 2 additions & 2 deletions pageserver/src/tenant/remote_timeline_client/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use utils::{id::TimelineId, lsn::Lsn};

/// Tenant-shard scoped manifest
#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct TenantManifest {
/// Debugging aid describing the version of this manifest.
/// Can also be used for distinguishing breaking changes later on.
Expand All @@ -23,7 +23,7 @@ pub struct TenantManifest {
/// Very similar to [`pageserver_api::models::OffloadedTimelineInfo`],
/// but the two datastructures serve different needs, this is for a persistent disk format
/// that must be backwards compatible, while the other is only for informative purposes.
#[derive(Clone, Serialize, Deserialize, Copy)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq)]
pub struct OffloadedTimelineManifest {
pub timeline_id: TimelineId,
/// Whether the timeline has a parent it has been branched off from or not
Expand Down

0 comments on commit 2f9a040

Please sign in to comment.