diff --git a/pageserver/src/tenant/storage_layer.rs b/pageserver/src/tenant/storage_layer.rs index 9607546ce0..94a64a8644 100644 --- a/pageserver/src/tenant/storage_layer.rs +++ b/pageserver/src/tenant/storage_layer.rs @@ -457,6 +457,26 @@ pub enum ValueReconstructResult { Missing, } +#[derive(Debug, Clone)] +pub(crate) enum LayerVisibility { + /// A Visible layer might be read while serving a read, because there is not an image layer between it + /// and a readable LSN (the tip of the branch or a child's branch point) + Visible, + /// A Covered layer probably won't be read right now, but _can_ be read in future if someone creates + /// a branch or ephemeral endpoint at an LSN below the layer that covers this. + Covered, + /// Calculating layer visibilty requires I/O, so until this has happened layers are loaded + /// in this state. Note that newly written layers may be called Visible immediately, this uninitialized + /// state is for when existing layers are constructed while loading a timeline. + Uninitialized, +} + +impl Default for LayerVisibility { + fn default() -> Self { + Self::Uninitialized + } +} + #[derive(Debug)] pub struct LayerAccessStats(Mutex); @@ -468,6 +488,7 @@ pub struct LayerAccessStats(Mutex); struct LayerAccessStatsLocked { for_scraping_api: LayerAccessStatsInner, for_eviction_policy: LayerAccessStatsInner, + visibility: LayerVisibility, } impl LayerAccessStatsLocked { @@ -591,7 +612,13 @@ impl LayerAccessStats { inner.count_by_access_kind[access_kind] += 1; inner.task_kind_flag |= ctx.task_kind(); inner.last_accesses.write(this_access); - }) + }); + + // We may access a layer marked as Covered, if a new branch was created that depends on + // this layer, and background updates to layer visibility didn't notice it yet + if !matches!(locked.visibility, LayerVisibility::Visible) { + locked.visibility = LayerVisibility::Visible; + } } fn as_api_model( diff --git a/pageserver/src/tenant/storage_layer/layer.rs b/pageserver/src/tenant/storage_layer/layer.rs index afd11780e7..e2b4de9206 100644 --- a/pageserver/src/tenant/storage_layer/layer.rs +++ b/pageserver/src/tenant/storage_layer/layer.rs @@ -250,6 +250,8 @@ impl Layer { LayerResidenceStatus::Resident, LayerResidenceEventReason::LayerCreate, ); + // Newly created layers are marked visible by default: the usual case is that they were created to be read. + access_stats.set_visibility(super::LayerVisibility::Visible); let local_path = local_layer_path( conf, diff --git a/pageserver/src/tenant/timeline/layer_manager.rs b/pageserver/src/tenant/timeline/layer_manager.rs index 948237e06a..78ecf267c7 100644 --- a/pageserver/src/tenant/timeline/layer_manager.rs +++ b/pageserver/src/tenant/timeline/layer_manager.rs @@ -255,6 +255,14 @@ impl LayerManager { new_layer.layer_desc().lsn_range ); + // Transfer visibilty hint from old to new layer, since the new layer covers the same key space. This is not guaranteed to + // be accurate (as the new layer may cover a different subset of the key range), but is a sensible default, and prevents + // always marking rewritten layers as visible. + new_layer + .as_ref() + .access_stats() + .set_visibility(old_layer.access_stats().get_visibility()); + // Safety: we may never rewrite the same file in-place. Callers are responsible // for ensuring that they only rewrite layers after something changes the path, // such as an increment in the generation number.