use crate::triple::{Triple, TripleId};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LwwTriple {
pub triple: Triple,
pub timestamp: DateTime<Utc>,
pub node_id: u64,
}
impl LwwTriple {
pub fn new(triple: Triple, node_id: u64) -> Self {
Self {
triple,
timestamp: Utc::now(),
node_id,
}
}
pub fn with_timestamp(triple: Triple, timestamp: DateTime<Utc>, node_id: u64) -> Self {
Self {
triple,
timestamp,
node_id,
}
}
pub fn merge(a: &LwwTriple, b: &LwwTriple) -> LwwTriple {
if a.timestamp > b.timestamp {
a.clone()
} else if b.timestamp > a.timestamp {
b.clone()
} else {
if a.node_id >= b.node_id {
a.clone()
} else {
b.clone()
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrSet {
adds: HashSet<([u8; 32], Uuid)>,
removes: HashSet<([u8; 32], Uuid)>,
}
impl OrSet {
pub fn new() -> Self {
Self {
adds: HashSet::new(),
removes: HashSet::new(),
}
}
pub fn insert(&mut self, id: &TripleId) -> Uuid {
let tag = Uuid::new_v4();
self.adds.insert((*id.as_bytes(), tag));
tag
}
pub fn remove(&mut self, id: &TripleId) {
let id_bytes = *id.as_bytes();
let to_remove: Vec<_> = self
.adds
.iter()
.filter(|(tid, _)| *tid == id_bytes)
.cloned()
.collect();
for pair in to_remove {
self.adds.remove(&pair);
self.removes.insert(pair);
}
}
pub fn contains(&self, id: &TripleId) -> bool {
let id_bytes = *id.as_bytes();
self.adds
.iter()
.any(|(tid, tag)| *tid == id_bytes && !self.removes.contains(&(id_bytes, *tag)))
}
pub fn merge(&mut self, other: &OrSet) {
self.adds = self.adds.union(&other.adds).cloned().collect();
self.removes = self.removes.union(&other.removes).cloned().collect();
}
pub fn len(&self) -> usize {
self.adds
.iter()
.filter(|pair| !self.removes.contains(pair))
.count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Default for OrSet {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{NodeId, Predicate, Value};
use chrono::Duration;
fn make_triple(subject: &str) -> Triple {
Triple::new(
NodeId::named(subject),
Predicate::named("knows"),
Value::String("bob".into()),
)
}
#[test]
fn test_lww_later_timestamp_wins() {
let triple = make_triple("alice");
let now = Utc::now();
let earlier = now - Duration::seconds(10);
let a = LwwTriple::with_timestamp(triple.clone(), earlier, 1);
let b = LwwTriple::with_timestamp(triple, now, 2);
let winner = LwwTriple::merge(&a, &b);
assert_eq!(winner.node_id, 2); }
#[test]
fn test_lww_tiebreak_by_node_id() {
let triple = make_triple("alice");
let same_time = Utc::now();
let a = LwwTriple::with_timestamp(triple.clone(), same_time, 1);
let b = LwwTriple::with_timestamp(triple, same_time, 2);
let winner = LwwTriple::merge(&a, &b);
assert_eq!(winner.node_id, 2); }
#[test]
fn test_lww_merge_commutative() {
let triple = make_triple("test");
let now = Utc::now();
let a = LwwTriple::with_timestamp(triple.clone(), now, 1);
let b = LwwTriple::with_timestamp(triple, now - Duration::seconds(1), 2);
let winner1 = LwwTriple::merge(&a, &b);
let winner2 = LwwTriple::merge(&b, &a);
assert_eq!(winner1.node_id, winner2.node_id); }
#[test]
fn test_or_set_insert_contains() {
let mut set = OrSet::new();
let triple = make_triple("alice");
let id = TripleId::from_triple(&triple);
set.insert(&id);
assert!(set.contains(&id));
assert_eq!(set.len(), 1);
}
#[test]
fn test_or_set_remove() {
let mut set = OrSet::new();
let triple = make_triple("alice");
let id = TripleId::from_triple(&triple);
set.insert(&id);
assert!(set.contains(&id));
set.remove(&id);
assert!(!set.contains(&id));
assert!(set.is_empty());
}
#[test]
fn test_or_set_concurrent_insert_remove() {
let mut set = OrSet::new();
let triple = make_triple("alice");
let id = TripleId::from_triple(&triple);
set.insert(&id);
assert!(set.contains(&id));
set.remove(&id);
assert!(!set.contains(&id));
set.insert(&id);
assert!(set.contains(&id));
}
#[test]
fn test_or_set_merge() {
let triple = make_triple("alice");
let id = TripleId::from_triple(&triple);
let mut set_a = OrSet::new();
set_a.insert(&id);
let mut set_b = OrSet::new();
let triple2 = make_triple("bob");
let id2 = TripleId::from_triple(&triple2);
set_b.insert(&id2);
set_a.merge(&set_b);
assert!(set_a.contains(&id));
assert!(set_a.contains(&id2));
}
#[test]
fn test_or_set_merge_idempotent() {
let triple = make_triple("alice");
let id = TripleId::from_triple(&triple);
let mut set = OrSet::new();
set.insert(&id);
let snapshot = set.clone();
set.merge(&snapshot);
assert_eq!(set.len(), 1); }
#[test]
fn test_or_set_merge_with_removes() {
let triple = make_triple("alice");
let id = TripleId::from_triple(&triple);
let mut set_a = OrSet::new();
set_a.insert(&id);
let mut set_b = set_a.clone();
set_b.remove(&id);
assert!(set_a.contains(&id));
assert!(!set_b.contains(&id));
set_a.merge(&set_b);
assert!(!set_a.contains(&id));
}
}