Prechádzať zdrojové kódy

script/research/crdt: implementing POC crdt

ghassmo 4 rokov pred
rodič
commit
db7d5ccbbd

+ 1 - 0
script/research/crdt/.gitignore

@@ -0,0 +1 @@
+target/

+ 65 - 0
script/research/crdt/Cargo.lock

@@ -0,0 +1,65 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "crdt"
+version = "0.1.0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"

+ 12 - 0
script/research/crdt/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "crdt"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+serde = {version = "1.0.136", features = ["derive"]}
+
+
+[workspace]

+ 154 - 0
script/research/crdt/src/gset.rs

@@ -0,0 +1,154 @@
+use std::{
+    cmp::{max, Ordering},
+    collections::BTreeSet,
+};
+
+use serde::{Deserialize, Serialize};
+
+// CRDT using gset and lamport clock
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+struct Node<T: Ord + Clone> {
+    // name to idnetifie the node
+    name: String,
+    // a grow-only set
+    gset: GSet<Event<T>>,
+    // a counter for the node
+    time: u64,
+}
+
+impl<T: Ord + Clone> Node<T> {
+    pub fn new(name: &str) -> Self {
+        Self { name: name.into(), gset: GSet::new(), time: 0 }
+    }
+
+    pub fn receive_event(&mut self, event: Event<T>) {
+        self.time = max(self.time, event.counter) + 1;
+        self.gset.insert(event);
+    }
+
+    pub fn send_event(&mut self, value: T) -> Event<T> {
+        self.time += 1;
+        let event = Event::new(value, self.time, self.name.clone());
+        self.gset.insert(event.clone());
+        event
+    }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd)]
+struct Event<T> {
+    // the msg in the event
+    value: T,
+    // the counter for lamport clock
+    counter: u64,
+    // It might be necessary to attach the node's name to the timestamp
+    // so that it is possible to differentiate between events
+    name: String,
+}
+
+impl<T> Event<T> {
+    pub fn new(value: T, counter: u64, name: String) -> Self {
+        Self { value, counter, name }
+    }
+}
+
+impl<T: Eq + PartialOrd> Ord for Event<T> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        let ord = self.counter.cmp(&other.counter);
+        if ord == Ordering::Equal {
+            return self.name.cmp(&other.name)
+        }
+        ord
+    }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub struct GSet<T: Ord> {
+    set: BTreeSet<T>,
+}
+
+impl<T: Ord> GSet<T> {
+    pub fn new() -> Self {
+        Self { set: BTreeSet::new() }
+    }
+
+    pub fn insert(&mut self, element: T) {
+        self.set.insert(element);
+    }
+
+    pub fn contains(&self, element: &T) -> bool {
+        self.set.contains(element)
+    }
+
+    pub fn len(&self) -> usize {
+        self.set.len()
+    }
+
+    pub fn merge(&mut self, other: Self) {
+        other.set.into_iter().for_each(|e| self.insert(e))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    fn sync_simulation(
+        mut a: Node<String>,
+        mut b: Node<String>,
+        mut c: Node<String>,
+    ) -> (Node<String>, Node<String>, Node<String>) {
+        a.gset.merge(b.gset.clone());
+        a.gset.merge(c.gset.clone());
+
+        b.gset.merge(a.gset.clone());
+        b.gset.merge(c.gset.clone());
+
+        c.gset.merge(a.gset.clone());
+        c.gset.merge(b.gset.clone());
+
+        (a, b, c)
+    }
+
+    #[test]
+    fn test_crdt_gset() {
+        let mut a: Node<String> = Node::new("Node A");
+        let mut b: Node<String> = Node::new("Node B");
+        let mut c: Node<String> = Node::new("Node C");
+
+        // node a
+        a.send_event("a_msg1".into());
+        a.send_event("a_msg2".into());
+
+        // node b
+        b.send_event("b_msg1".into());
+
+        // node c
+        c.send_event("c_msg1".into());
+
+        // node b
+        b.send_event("b_msg2".into());
+
+        let (a, mut b, mut c) = sync_simulation(a, b, c);
+
+        assert_eq!(a.gset.len(), 5);
+        assert_eq!(b.gset.len(), 5);
+        assert_eq!(c.gset.len(), 5);
+
+        // node c
+        c.send_event("c_msg2".into());
+        c.send_event("c_msg3".into());
+        c.send_event("c_msg4".into());
+        c.send_event("c_msg5".into());
+
+        // node b
+        b.send_event("b_msg3".into());
+
+        let (a, b, c) = sync_simulation(a, b, c);
+
+        assert_eq!(a.gset.len(), 10);
+        assert_eq!(b.gset.len(), 10);
+        assert_eq!(c.gset.len(), 10);
+    }
+}

+ 1 - 0
script/research/crdt/src/lib.rs

@@ -0,0 +1 @@
+pub mod gset;

+ 3 - 0
script/research/crdt/src/main.rs

@@ -0,0 +1,3 @@
+fn main() {
+    println!("Hello, world!");
+}