diff options
Diffstat (limited to 'src/remake/snapshot.rs')
| -rw-r--r-- | src/remake/snapshot.rs | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/src/remake/snapshot.rs b/src/remake/snapshot.rs index b4f1f35..fa1fa39 100644 --- a/src/remake/snapshot.rs +++ b/src/remake/snapshot.rs @@ -251,18 +251,20 @@ pub(crate) fn verify_document(doc: &Document, epoch: Epoch) -> Result<EpochMetad read_epoch_metadata(&at_snapshot, epoch.generation) } -/// The epoch `doc` is on: genesis if it holds no epoch metadata; otherwise the newest -/// generation it has metadata for, whose snapshot change is the document's only change -/// without dependencies (verified). For a document loaded from a backup. +/// The epoch `doc` is on: the newest generation the metadata at its only change without +/// dependencies names (that change, a snapshot, writes its own and carries the earlier +/// ones), verified; genesis if none names one, as the genesis change doesn't. Read at +/// that change, not as the document stands: an entry a member wrote since (with no +/// policy, its own change could) doesn't decide. For a document loaded from a backup. pub(crate) fn epoch_of(doc: &Document) -> Result<Epoch, String> { - let generation = store::keys_at(doc, &meta::path(&[EPOCHS])) - .map_err(|error| error.to_string())? - .unwrap_or_default() - .iter() - .filter_map(|key| key.parse::<u64>().ok()) - .max(); - let Some(generation) = generation.filter(|generation| *generation > 0) else { - return Ok(Epoch::GENESIS); + let generations = |doc: &Document| -> Result<Option<u64>, String> { + Ok(store::keys_at(doc, &meta::path(&[EPOCHS])) + .map_err(|error| error.to_string())? + .unwrap_or_default() + .iter() + .filter_map(|key| key.parse::<u64>().ok()) + .filter(|generation| *generation > 0) + .max()) }; let roots: Vec<ChangeHash> = doc .get_changes(&[]) @@ -271,10 +273,17 @@ pub(crate) fn epoch_of(doc: &Document) -> Result<Epoch, String> { .map(|change| change.hash()) .collect(); let [root] = roots.as_slice() else { - return Err(format!( - "generation {generation} needs exactly one change without dependencies (its snapshot), found {}", - roots.len() - )); + return match generations(doc)? { + None => Ok(Epoch::GENESIS), + Some(generation) => Err(format!( + "generation {generation} needs exactly one change without dependencies (its snapshot), found {}", + roots.len() + )), + }; + }; + let at_root = doc.fork_at(&[*root]).map_err(|error| error.to_string())?; + let Some(generation) = generations(&at_root)? else { + return Ok(Epoch::GENESIS); }; let epoch = Epoch { generation, @@ -690,7 +699,9 @@ mod tests { .fork() .with_actor(ActorId::from(b"actor-b".as_slice())); change(&mut later, |later| { - store::write_path(later, &["k".into()], &json!(2)).unwrap() + store::write_path(later, &["k".into()], &json!(2)).unwrap(); + // A generation planted since doesn't decide. + store::write_path(later, &meta::path(&[EPOCHS, "99"]), &json!({})).unwrap(); }); let backup = Document::load(&later.save()).unwrap(); assert_eq!(epoch_of(&backup), Ok(snap.epoch)); |
