Skip to content

Commit d11f23a

Browse files
authored
pageserver: refactor read path for multi LSN batching support (#11463)
## Problem We wish to improve pageserver batching such that one batch can contain requests for pages at different LSNs. The current shape of the code doesn't lend itself to the change. ## Summary of changes Refactor the read path such that the fringe gets initialized upfront. This is where the multi LSN change will plug in. A couple other small changes fell out of this. There should be NO behaviour change here. If you smell one, shout! I recommend reviewing commits individually (intentionally made them as small as possible). Related: #10765
1 parent e7502a3 commit d11f23a

File tree

2 files changed

+72
-52
lines changed

2 files changed

+72
-52
lines changed

pageserver/src/tenant/timeline.rs

Lines changed: 38 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ use crate::pgdatadir_mapping::{
115115
use crate::task_mgr::TaskKind;
116116
use crate::tenant::config::AttachmentMode;
117117
use crate::tenant::gc_result::GcResult;
118-
use crate::tenant::layer_map::{LayerMap, SearchResult};
118+
use crate::tenant::layer_map::LayerMap;
119119
use crate::tenant::metadata::TimelineMetadata;
120120
use crate::tenant::storage_layer::delta_layer::DeltaEntry;
121121
use crate::tenant::storage_layer::inmemory_layer::IndexEntry;
@@ -4104,12 +4104,6 @@ impl Timeline {
41044104
cancel: &CancellationToken,
41054105
ctx: &RequestContext,
41064106
) -> Result<TimelineVisitOutcome, GetVectoredError> {
4107-
let mut unmapped_keyspace = keyspace.clone();
4108-
let mut fringe = LayerFringe::new();
4109-
4110-
let mut completed_keyspace = KeySpace::default();
4111-
let mut image_covered_keyspace = KeySpaceRandomAccum::new();
4112-
41134107
// Prevent GC from progressing while visiting the current timeline.
41144108
// If we are GC-ing because a new image layer was added while traversing
41154109
// the timeline, then it will remove layers that are required for fulfilling
@@ -4120,11 +4114,44 @@ impl Timeline {
41204114
// See `compaction::compact_with_gc` for why we need this.
41214115
let _guard = timeline.gc_compaction_layer_update_lock.read().await;
41224116

4123-
loop {
4117+
// Initialize the fringe
4118+
let mut fringe = {
4119+
let mut fringe = LayerFringe::new();
4120+
4121+
let guard = timeline.layers.read().await;
4122+
guard.update_search_fringe(&keyspace, cont_lsn, &mut fringe)?;
4123+
4124+
fringe
4125+
};
4126+
4127+
let mut completed_keyspace = KeySpace::default();
4128+
let mut image_covered_keyspace = KeySpaceRandomAccum::new();
4129+
4130+
while let Some((layer_to_read, keyspace_to_read, lsn_range)) = fringe.next_layer() {
41244131
if cancel.is_cancelled() {
41254132
return Err(GetVectoredError::Cancelled);
41264133
}
41274134

4135+
if let Some(ref mut read_path) = reconstruct_state.read_path {
4136+
read_path.record_layer_visit(&layer_to_read, &keyspace_to_read, &lsn_range);
4137+
}
4138+
4139+
// Visit the layer and plan IOs for it
4140+
let next_cont_lsn = lsn_range.start;
4141+
layer_to_read
4142+
.get_values_reconstruct_data(
4143+
keyspace_to_read.clone(),
4144+
lsn_range,
4145+
reconstruct_state,
4146+
ctx,
4147+
)
4148+
.await?;
4149+
4150+
let mut unmapped_keyspace = keyspace_to_read;
4151+
cont_lsn = next_cont_lsn;
4152+
4153+
reconstruct_state.on_layer_visited(&layer_to_read);
4154+
41284155
let (keys_done_last_step, keys_with_image_coverage) =
41294156
reconstruct_state.consume_done_keys();
41304157
unmapped_keyspace.remove_overlapping_with(&keys_done_last_step);
@@ -4135,31 +4162,15 @@ impl Timeline {
41354162
image_covered_keyspace.add_range(keys_with_image_coverage);
41364163
}
41374164

4165+
// Query the layer map for the next layers to read.
4166+
//
41384167
// Do not descent any further if the last layer we visited
41394168
// completed all keys in the keyspace it inspected. This is not
41404169
// required for correctness, but avoids visiting extra layers
41414170
// which turns out to be a perf bottleneck in some cases.
41424171
if !unmapped_keyspace.is_empty() {
41434172
let guard = timeline.layers.read().await;
4144-
let layers = guard.layer_map()?;
4145-
4146-
for range in unmapped_keyspace.ranges.iter() {
4147-
let results = layers.range_search(range.clone(), cont_lsn);
4148-
4149-
results
4150-
.found
4151-
.into_iter()
4152-
.map(|(SearchResult { layer, lsn_floor }, keyspace_accum)| {
4153-
(
4154-
guard.upgrade(layer),
4155-
keyspace_accum.to_keyspace(),
4156-
lsn_floor..cont_lsn,
4157-
)
4158-
})
4159-
.for_each(|(layer, keyspace, lsn_range)| {
4160-
fringe.update(layer, keyspace, lsn_range)
4161-
});
4162-
}
4173+
guard.update_search_fringe(&unmapped_keyspace, cont_lsn, &mut fringe)?;
41634174

41644175
// It's safe to drop the layer map lock after planning the next round of reads.
41654176
// The fringe keeps readable handles for the layers which are safe to read even
@@ -4173,28 +4184,6 @@ impl Timeline {
41734184
// at two different time points.
41744185
drop(guard);
41754186
}
4176-
4177-
if let Some((layer_to_read, keyspace_to_read, lsn_range)) = fringe.next_layer() {
4178-
if let Some(ref mut read_path) = reconstruct_state.read_path {
4179-
read_path.record_layer_visit(&layer_to_read, &keyspace_to_read, &lsn_range);
4180-
}
4181-
let next_cont_lsn = lsn_range.start;
4182-
layer_to_read
4183-
.get_values_reconstruct_data(
4184-
keyspace_to_read.clone(),
4185-
lsn_range,
4186-
reconstruct_state,
4187-
ctx,
4188-
)
4189-
.await?;
4190-
4191-
unmapped_keyspace = keyspace_to_read;
4192-
cont_lsn = next_cont_lsn;
4193-
4194-
reconstruct_state.on_layer_visited(&layer_to_read);
4195-
} else {
4196-
break;
4197-
}
41984187
}
41994188

42004189
Ok(TimelineVisitOutcome {

pageserver/src/tenant/timeline/layer_manager.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ use std::sync::Arc;
33

44
use anyhow::{Context, bail, ensure};
55
use itertools::Itertools;
6+
use pageserver_api::keyspace::KeySpace;
67
use pageserver_api::shard::TenantShardId;
78
use tokio_util::sync::CancellationToken;
89
use tracing::trace;
910
use utils::id::TimelineId;
1011
use utils::lsn::{AtomicLsn, Lsn};
1112

12-
use super::{ReadableLayer, TimelineWriterState};
13+
use super::{LayerFringe, ReadableLayer, TimelineWriterState};
1314
use crate::config::PageServerConf;
1415
use crate::context::RequestContext;
1516
use crate::metrics::TimelineMetrics;
16-
use crate::tenant::layer_map::{BatchedUpdates, LayerMap};
17+
use crate::tenant::layer_map::{BatchedUpdates, LayerMap, SearchResult};
1718
use crate::tenant::storage_layer::{
1819
AsLayerDesc, InMemoryLayer, Layer, LayerVisibilityHint, PersistentLayerDesc,
1920
PersistentLayerKey, ReadableLayerWeak, ResidentLayer,
@@ -38,7 +39,7 @@ impl Default for LayerManager {
3839
}
3940

4041
impl LayerManager {
41-
pub(crate) fn upgrade(&self, weak: ReadableLayerWeak) -> ReadableLayer {
42+
fn upgrade(&self, weak: ReadableLayerWeak) -> ReadableLayer {
4243
match weak {
4344
ReadableLayerWeak::PersistentLayer(desc) => {
4445
ReadableLayer::PersistentLayer(self.get_from_desc(&desc))
@@ -147,6 +148,36 @@ impl LayerManager {
147148
self.layers().keys().cloned().collect_vec()
148149
}
149150

151+
/// Update the [`LayerFringe`] of a read request
152+
///
153+
/// Take a key space at a given LSN and query the layer map below each range
154+
/// of the key space to find the next layers to visit.
155+
pub(crate) fn update_search_fringe(
156+
&self,
157+
keyspace: &KeySpace,
158+
cont_lsn: Lsn,
159+
fringe: &mut LayerFringe,
160+
) -> Result<(), Shutdown> {
161+
let map = self.layer_map()?;
162+
163+
for range in keyspace.ranges.iter() {
164+
let results = map.range_search(range.clone(), cont_lsn);
165+
results
166+
.found
167+
.into_iter()
168+
.map(|(SearchResult { layer, lsn_floor }, keyspace_accum)| {
169+
(
170+
self.upgrade(layer),
171+
keyspace_accum.to_keyspace(),
172+
lsn_floor..cont_lsn,
173+
)
174+
})
175+
.for_each(|(layer, keyspace, lsn_range)| fringe.update(layer, keyspace, lsn_range));
176+
}
177+
178+
Ok(())
179+
}
180+
150181
fn layers(&self) -> &HashMap<PersistentLayerKey, Layer> {
151182
use LayerManager::*;
152183
match self {

0 commit comments

Comments
 (0)