aboutsummaryrefslogtreecommitdiffstats
path: root/src/remake/catchup.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-09 03:41:00 +0000
committertv <tv@krebsco.de>2026-11-09 04:41:41 +0100
commit19535a851b2a528bcd19f44b73a2d90b46adaa5e (patch)
tree53f0364ed1cbe3f01e0b5fc1d58ba94a7c3f6e5c /src/remake/catchup.rs
parentb52d4b6181e7fcf806a655d218e74de71b54cbf8 (diff)
remake: earlier epochs' metadata read as the snapshot built it
A snapshot leaving out earlier epochs' metadata (`snapshot::left_out`) and a rejoining node (`catchup::prepare`) read it from the document as it stands. It is never writable through the store, and under a policy the rules take a change to it only from an epoch's first; but with no policy, screening judges only an epoch's root, so a member's own change could rewrite or remove an entry: a replay window narrowed or widened, a translation or a clock changed, a strict remake's entry gone. Now both read it at the epoch's snapshot change (`snapshot::as_built`), as `verify_document` reads the epoch's own: a snapshot from its base's epoch's, a rejoining node from the target's. Every earlier entry is a copy in that change, so what was written there since doesn't count. The first epoch follows none and has none.
Diffstat (limited to 'src/remake/catchup.rs')
-rw-r--r--src/remake/catchup.rs13
1 files changed, 10 insertions, 3 deletions
diff --git a/src/remake/catchup.rs b/src/remake/catchup.rs
index a3443ba..68fa117 100644
--- a/src/remake/catchup.rs
+++ b/src/remake/catchup.rs
@@ -520,12 +520,19 @@ fn prepare(
Ok(metadata) => metadata,
Err(reason) => return Prepared::Failed(reason),
};
+ // Earlier epochs' metadata as the target's snapshot built it, whatever was written
+ // there since.
+ let built = match snapshot::as_built(copy, target) {
+ Ok(Some(built)) => built,
+ Ok(None) => return Prepared::Failed(format!("{target} follows no epoch")),
+ Err(reason) => return Prepared::Failed(reason),
+ };
// The epoch that followed ours records (per actor) which of our changes it took.
// Snapshots carry earlier epochs' metadata forward while a node can replay through
// it (`snapshot::left_out`), so this works across any gap within the window; beyond
// it, the metadata missing quarantines as the window would.
- let next = snapshot::read_epoch_metadata(copy, previous.generation + 1)
+ let next = snapshot::read_epoch_metadata(&built, previous.generation + 1)
.ok()
.filter(|next| next.previous == previous);
let gap = target.generation - previous.generation;
@@ -537,7 +544,7 @@ fn prepare(
// this sees every one since our epoch, however far behind.
let mut strict = crate::access::policy::of(old).strict();
for generation in previous.generation + 1..=target.generation {
- if let Ok(epoch) = snapshot::read_epoch_metadata(copy, generation)
+ if let Ok(epoch) = snapshot::read_epoch_metadata(&built, generation)
&& epoch.kind == "strict"
{
if strict {
@@ -552,7 +559,7 @@ fn prepare(
let translator = (previous.generation + 1..=target.generation).try_fold(
Translator::default(),
|mut translator, generation| {
- let epoch = snapshot::read_epoch_metadata(copy, generation)?;
+ let epoch = snapshot::read_epoch_metadata(&built, generation)?;
let kind = epoch.kind().ok_or_else(|| {
format!(
"epoch generation {generation} was made by unknown remake kind {:?}",