mirror of
https://github.com/neondatabase/neon.git
synced 2026-06-02 04:50:38 +00:00
pageserver: add GcInfo::notify_child_keyspace
This commit is contained in:
@@ -19,6 +19,7 @@ use enumset::EnumSet;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::FutureExt;
|
||||
use futures::StreamExt;
|
||||
use pageserver_api::keyspace::KeySpace;
|
||||
use pageserver_api::models;
|
||||
use pageserver_api::models::AuxFilePolicy;
|
||||
use pageserver_api::models::TimelineState;
|
||||
@@ -2786,11 +2787,12 @@ impl Tenant {
|
||||
|
||||
// Scan all timelines. For each timeline, remember the timeline ID and
|
||||
// the branch point where it was created.
|
||||
let mut all_branchpoints: BTreeMap<TimelineId, Vec<(Lsn, TimelineId)>> = BTreeMap::new();
|
||||
let mut all_branchpoints: BTreeMap<TimelineId, Vec<(Lsn, TimelineId, Option<KeySpace>)>> =
|
||||
BTreeMap::new();
|
||||
timelines.iter().for_each(|(timeline_id, timeline_entry)| {
|
||||
if let Some(ancestor_timeline_id) = &timeline_entry.get_ancestor_timeline_id() {
|
||||
let ancestor_children = all_branchpoints.entry(*ancestor_timeline_id).or_default();
|
||||
ancestor_children.push((timeline_entry.get_ancestor_lsn(), *timeline_id));
|
||||
ancestor_children.push((timeline_entry.get_ancestor_lsn(), *timeline_id, None));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2799,7 +2801,7 @@ impl Tenant {
|
||||
|
||||
// Populate each timeline's GcInfo with information about its child branches
|
||||
for timeline in timelines.values() {
|
||||
let mut branchpoints: Vec<(Lsn, TimelineId)> = all_branchpoints
|
||||
let mut branchpoints: Vec<(Lsn, TimelineId, Option<KeySpace>)> = all_branchpoints
|
||||
.remove(&timeline.timeline_id)
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -4305,7 +4307,7 @@ mod tests {
|
||||
{
|
||||
let branchpoints = &tline.gc_info.read().unwrap().retain_lsns;
|
||||
assert_eq!(branchpoints.len(), 1);
|
||||
assert_eq!(branchpoints[0], (Lsn(0x40), NEW_TIMELINE_ID));
|
||||
assert_eq!(branchpoints[0], (Lsn(0x40), NEW_TIMELINE_ID, None));
|
||||
}
|
||||
|
||||
// You can read the key from the child branch even though the parent is
|
||||
|
||||
@@ -271,10 +271,14 @@ pub(super) async fn gather_inputs(
|
||||
let mut lsns: Vec<(Lsn, LsnKind)> = gc_info
|
||||
.retain_lsns
|
||||
.iter()
|
||||
.filter(|(lsn, _child_id)| lsn > &ancestor_lsn)
|
||||
.copied()
|
||||
// this assumes there are no other retain_lsns than the branchpoints
|
||||
.map(|(lsn, _child_id)| (lsn, LsnKind::BranchPoint))
|
||||
.filter_map(|(lsn, _child_id, _)| {
|
||||
if lsn > &ancestor_lsn {
|
||||
// this assumes there are no other retain_lsns than the branchpoints
|
||||
Some((*lsn, LsnKind::BranchPoint))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
lsns.extend(lease_points.iter().map(|&lsn| (lsn, LsnKind::LeasePoint)));
|
||||
|
||||
@@ -453,12 +453,12 @@ pub struct WalReceiverInfo {
|
||||
/// Garbage Collection.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct GcInfo {
|
||||
/// Specific LSNs that are needed.
|
||||
/// Record which parts of this timeline's history are still needed by children
|
||||
///
|
||||
/// Currently, this includes all points where child branches have
|
||||
/// been forked off from. In the future, could also include
|
||||
/// explicit user-defined snapshot points.
|
||||
pub(crate) retain_lsns: Vec<(Lsn, TimelineId)>,
|
||||
/// Optionally store each child's keyspace at their branch LSN: parts of the keyspace not covered here may be dropped during GC, as
|
||||
/// the child will never read them. For example, a child which has covered its whole keyspace with image layers
|
||||
/// will put an empty keyspace here. Children populate this: if it is None, presume the child may read any part of the keyspace.
|
||||
pub(crate) retain_lsns: Vec<(Lsn, TimelineId, Option<KeySpace>)>,
|
||||
|
||||
/// The cutoff coordinates, which are combined by selecting the minimum.
|
||||
pub(crate) cutoffs: GcCutoffs,
|
||||
@@ -476,13 +476,21 @@ impl GcInfo {
|
||||
}
|
||||
|
||||
pub(super) fn insert_child(&mut self, child_id: TimelineId, child_lsn: Lsn) {
|
||||
self.retain_lsns.push((child_lsn, child_id));
|
||||
self.retain_lsns.push((child_lsn, child_id, None));
|
||||
self.retain_lsns.sort_by_key(|i| i.0);
|
||||
}
|
||||
|
||||
pub(super) fn remove_child(&mut self, child_id: TimelineId) {
|
||||
self.retain_lsns.retain(|i| i.1 != child_id);
|
||||
}
|
||||
|
||||
/// When the child re-calculates which parts of the keyspace it will read from the ancestor, it posts
|
||||
/// and update to the parent using this function, to enable the parent to perhaps GC more layers.
|
||||
pub(super) fn notify_child_keyspace(&mut self, child_id: TimelineId, key_space: KeySpace) {
|
||||
if let Ok(idx) = self.retain_lsns.binary_search_by_key(&child_id, |i| i.1) {
|
||||
self.retain_lsns.get_mut(idx).unwrap().2 = Some(key_space);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `GcInfo` component describing which Lsns need to be retained.
|
||||
@@ -5089,7 +5097,7 @@ impl Timeline {
|
||||
let retain_lsns = gc_info
|
||||
.retain_lsns
|
||||
.iter()
|
||||
.map(|(lsn, _child_id)| *lsn)
|
||||
.map(|(lsn, _child_id, _)| *lsn)
|
||||
.collect();
|
||||
|
||||
// Gets the maximum LSN that holds the valid lease.
|
||||
|
||||
Reference in New Issue
Block a user