diff options
Diffstat (limited to 'src/replica.rs')
| -rw-r--r-- | src/replica.rs | 130 |
1 files changed, 0 insertions, 130 deletions
diff --git a/src/replica.rs b/src/replica.rs deleted file mode 100644 index 4579029..0000000 --- a/src/replica.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! The local replica: the Automerge document together with the epoch it belongs to. -//! -//! Compaction (later steps) replaces the document with a snapshot whose history is -//! unrelated to the old one. Syncing two unrelated histories would make Automerge merge -//! them, turning every key into a conflict with its own copy, so nodes on different -//! epochs must never sync. Keeping the epoch and the document under one lock makes it -//! impossible to label a sync message from one document with the other's epoch. - -use std::fmt; -use std::ops::{Deref, DerefMut}; - -use automerge::AutoCommit; - -/// One generation of the cluster document. -/// -/// * `generation` orders epochs: every compaction increments it, so a node can tell -/// whether a peer is behind or ahead. -/// * `id` is the hash of the epoch's snapshot change. It tells two epochs of the same -/// generation apart, which only happens if two compactions committed concurrently. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct Epoch { - pub generation: u64, - pub id: [u8; 32], -} - -/// How a peer's epoch relates to ours. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum EpochRelation { - Same, - /// The peer is on an older generation and has to migrate. - PeerBehind, - /// The peer is on a newer generation; we have to migrate. - PeerAhead, - /// Same generation, different snapshot: competing compactions. Never sync. - Diverged, -} - -impl Epoch { - /// Clusters that have never been compacted. - pub const GENESIS: Epoch = Epoch { - generation: 0, - id: [0; 32], - }; - - pub fn relation_to(&self, peer: &Epoch) -> EpochRelation { - use std::cmp::Ordering; - match peer.generation.cmp(&self.generation) { - Ordering::Less => EpochRelation::PeerBehind, - Ordering::Greater => EpochRelation::PeerAhead, - Ordering::Equal if peer.id == self.id => EpochRelation::Same, - Ordering::Equal => EpochRelation::Diverged, - } - } - - pub fn id_hex(&self) -> String { - self.id.iter().map(|byte| format!("{byte:02x}")).collect() - } -} - -impl fmt::Display for Epoch { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if *self == Epoch::GENESIS { - write!(f, "0:genesis") - } else { - write!(f, "{}:{}", self.generation, &self.id_hex()[..12]) - } - } -} - -impl fmt::Debug for Epoch { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -/// The document and its epoch, always locked and swapped together. -/// -/// Derefs to the document, so code that only needs the `AutoCommit` keeps working -/// with a guard of `Replica`. -pub struct Replica { - pub epoch: Epoch, - pub doc: AutoCommit, -} - -impl Replica { - pub fn new(epoch: Epoch, doc: AutoCommit) -> Self { - Self { epoch, doc } - } -} - -impl Deref for Replica { - type Target = AutoCommit; - - fn deref(&self) -> &AutoCommit { - &self.doc - } -} - -impl DerefMut for Replica { - fn deref_mut(&mut self) -> &mut AutoCommit { - &mut self.doc - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn epoch(generation: u64, byte: u8) -> Epoch { - Epoch { - generation, - id: [byte; 32], - } - } - - #[test] - fn relation() { - let local = epoch(3, 1); - assert_eq!(local.relation_to(&epoch(3, 1)), EpochRelation::Same); - assert_eq!(local.relation_to(&epoch(2, 9)), EpochRelation::PeerBehind); - assert_eq!(local.relation_to(&epoch(4, 9)), EpochRelation::PeerAhead); - assert_eq!(local.relation_to(&epoch(3, 2)), EpochRelation::Diverged); - } - - #[test] - fn display() { - assert_eq!(Epoch::GENESIS.to_string(), "0:genesis"); - assert_eq!(epoch(2, 0xab).to_string(), "2:abababababab"); - } -} |
