summaryrefslogtreecommitdiffstats
path: root/src/replica.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/replica.rs')
-rw-r--r--src/replica.rs130
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");
- }
-}