├── .gitignore ├── src ├── lib.rs ├── vclock.rs └── dvv.rs ├── Cargo.toml ├── LICENSE ├── examples └── kv.rs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod dvv; 2 | mod vclock; 3 | pub use dvv::VersionVector; 4 | pub use dvv::Dot; 5 | pub use vclock::VectorClock; 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | #[test] 10 | fn it_works() { 11 | assert_eq!(2 + 2, 4); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "logical_clock" 3 | version = "0.1.3" 4 | authors = ["Kaviraj "] 5 | description = "Modern logical clocks implementation in Rust" 6 | edition = "2018" 7 | repository = "https://github.com/kavirajk/clock" 8 | categories = ["distributed-systems", "logical-clocks"] 9 | keywords = ["vector-clocks", "version-vector", "clock"] 10 | license = "MIT" 11 | readme = "README.md" 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | nanoid = "0.2.0" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kaviraj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/kv.rs: -------------------------------------------------------------------------------- 1 | use logical_clock::{VersionVector, Dot}; 2 | use std::collections::HashMap; 3 | 4 | type Key = String; 5 | 6 | #[derive(Clone, Debug)] 7 | struct Value{ 8 | val:i64, 9 | dot:Dot 10 | } 11 | 12 | struct KVStore { 13 | store:HashMap>, 14 | vv:VersionVector, 15 | 16 | } 17 | 18 | impl KVStore { 19 | fn new() -> KVStore { 20 | KVStore{ 21 | store: HashMap::new(), 22 | vv: VersionVector::new(), 23 | } 24 | } 25 | 26 | fn get(&self, key: &str) -> (Option>, VersionVector) { 27 | match self.store.get(key) { 28 | None => (None, self.vv.clone()), 29 | Some(v) => (Some(v.clone()), self.vv.clone()) 30 | } 31 | } 32 | 33 | fn set(mut self, client_id:&str, context: &VersionVector, key: &str, val: i64) -> Self{ 34 | // if incoming request context descends from local clock, just overwrite. 35 | if context.descends(&self.vv) { 36 | self.vv = self.vv.inc(client_id); 37 | let dot = self.vv.get_dot(client_id); 38 | let new_obj = Value{val: val, dot: dot}; 39 | 40 | // overwrite all the siblings 41 | self.store.insert(key.to_string(), vec![new_obj]); 42 | return self 43 | } 44 | 45 | let mut frontier = self.vv.merge(&context); 46 | frontier = frontier.inc(client_id); 47 | let dot = frontier.get_dot(client_id); 48 | let new_obj = Value{val: val, dot: dot}; 49 | self.vv = frontier; 50 | return self.merge_siblings(key, new_obj) 51 | } 52 | 53 | fn merge_siblings(mut self, key: &str, new_val: Value) -> Self{ 54 | // replace values that dominated by given value's dot 55 | let (old, _) = self.get(key); 56 | 57 | match old { 58 | None => { 59 | self.store.insert(key.to_string(), vec![new_val]); 60 | return self 61 | }, 62 | Some(values) => { 63 | let mut updated = Vec::new(); 64 | for v in values { 65 | if new_val.dot.descends(&v.dot) { 66 | continue; 67 | } 68 | updated.push(v); 69 | } 70 | updated.push(new_val); 71 | self.store.insert(key.to_string(), updated); 72 | return self 73 | } 74 | } 75 | } 76 | } 77 | 78 | fn main() { 79 | let mut kv = KVStore::new(); 80 | 81 | // always get before put - Semantics followed in any High Available Key value store 82 | 83 | // Client A and Client B 84 | let (_, ctx_a) = kv.get("x"); 85 | let (_, ctx_b) = kv.get("x"); 86 | 87 | 88 | kv = kv.set("A", &ctx_a, "x", 10); // A try to write x=10 with empty context 89 | kv = kv.set("B", &ctx_b, "x", 15); // B try to write x=12 with same empty context 90 | 91 | // both are concurrent from the client views, so both values should be kept 92 | assert_eq!(2, kv.store["x"].len()); 93 | 94 | // Client C comes in. 95 | let (_, ctx_c) = kv.get("x"); 96 | // now client C knows all the causal contex, so it replaces the key with all causal past. 97 | kv = kv.set("C", &ctx_c, "x", 20); 98 | assert_eq!(1, kv.store["x"].len()); 99 | 100 | // now B set with old empty context. 101 | kv = kv.set("B", &ctx_b, "x", 30); // I know contex is empty just set it as 30. 102 | 103 | // From client views latest B write is concurrent to C latest write. so both should be kept. 104 | assert_eq!(2, kv.store["x"].len()); 105 | 106 | for (k, v) in kv.store { 107 | println!("key: {}, values: {:?}", k, v) 108 | // val: {}, dot: {:?}", k, v, v.dot); 109 | } 110 | println!("vv: {:?}", kv.vv); 111 | 112 | } 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Logical clocks 2 | 3 | [![Package version](https://img.shields.io/crates/v/logical_clock.svg)](https://crates.io/crates/logical_clock) 4 | [![Package docs](https://docs.rs/logical_clock/badge.svg)](https://docs.rs/logical_clock) 5 | [![License](https://img.shields.io/badge/license-MIT%20License-blue.svg)](https://github.com/kavirajk/clock/blob/master/LICENSE) 6 | 7 | 8 | `clocks` implements some of the modern logical clocks (vector clocks and dotted version vector). 9 | 10 | A logical clock is a mechanism for capturing chronological and causal relationships(cause and effect, Event A caused event B, also called as *happened-before* relation) in a distributed system. 11 | 12 | Given any two events across multiple nodes in the distributed system, logical clocks help in answering queries like "Does event A *happened-before* B" or "Is event B *concurrent* to event A" 13 | 14 | Implementation of dotted version vector is based on the paper [Scalable and Accurate Causality Tracking for Eventually Consistent Stores](https://haslab.uminho.pt/tome/files/dvvset-dais.pdf) 15 | 16 | ## Vector clocks vs Version Vectors 17 | Although they both have same data structure representation, they solve different problems. 18 | 19 | Vector clocks are used to partial order between any two events in the distributed systems, where as Version Vectors are used to partial order only events that changes datum(say you want to keep multiple versions of same key that are updated concurrently). 20 | 21 | For more details about the differences, there good article [here](https://haslab.wordpress.com/2011/07/08/version-vectors-are-not-vector-clocks/) 22 | 23 | ## Usage (A simple Key Value Store, simulating multiple clients) 24 | ```rust 25 | use logical_clock::{VersionVector, Dot}; 26 | use std::collections::HashMap; 27 | 28 | type Key = String; 29 | 30 | #[derive(Clone, Debug)] 31 | struct Value{ 32 | val:i64, 33 | dot:Dot 34 | } 35 | 36 | struct KVStore { 37 | store:HashMap>, 38 | vv:VersionVector, 39 | 40 | } 41 | 42 | impl KVStore { 43 | fn new() -> KVStore { 44 | KVStore{ 45 | store: HashMap::new(), 46 | vv: VersionVector::new(), 47 | } 48 | } 49 | 50 | fn get(&self, key: &str) -> (Option>, VersionVector) { 51 | match self.store.get(key) { 52 | None => (None, self.vv.clone()), 53 | Some(v) => (Some(v.clone()), self.vv.clone()) 54 | } 55 | } 56 | 57 | fn set(mut self, client_id:&str, context: &VersionVector, key: &str, val: i64) -> Self{ 58 | // if incoming request context descends from local clock, just overwrite. 59 | if context.descends(&self.vv) { 60 | self.vv = self.vv.inc(client_id); 61 | let dot = self.vv.get_dot(client_id); 62 | let new_obj = Value{val: val, dot: dot}; 63 | 64 | // overwrite all the siblings 65 | self.store.insert(key.to_string(), vec![new_obj]); 66 | return self 67 | } 68 | 69 | let mut frontier = self.vv.merge(&context); 70 | frontier = frontier.inc(client_id); 71 | let dot = frontier.get_dot(client_id); 72 | let new_obj = Value{val: val, dot: dot}; 73 | self.vv = frontier; 74 | return self.merge_siblings(key, new_obj) 75 | } 76 | 77 | fn merge_siblings(mut self, key: &str, new_val: Value) -> Self{ 78 | // replace values that dominated by given value's dot 79 | let (old, _) = self.get(key); 80 | 81 | match old { 82 | None => { 83 | self.store.insert(key.to_string(), vec![new_val]); 84 | return self 85 | }, 86 | Some(values) => { 87 | let mut updated = Vec::new(); 88 | for v in values { 89 | if new_val.dot.descends(&v.dot) { 90 | continue; 91 | } 92 | updated.push(v); 93 | } 94 | updated.push(new_val); 95 | self.store.insert(key.to_string(), updated); 96 | return self 97 | } 98 | } 99 | } 100 | } 101 | 102 | fn main() { 103 | let mut kv = KVStore::new(); 104 | 105 | // always get before put - Semantics followed in any High Available Key value store 106 | 107 | // Client A and Client B 108 | let (_, ctx_a) = kv.get("x"); 109 | let (_, ctx_b) = kv.get("x"); 110 | 111 | 112 | kv = kv.set("A", &ctx_a, "x", 10); // A try to write x=10 with empty context 113 | kv = kv.set("B", &ctx_b, "x", 15); // B try to write x=12 with same empty context 114 | 115 | // both are concurrent from the client views, so both values should be kept 116 | assert_eq!(2, kv.store["x"].len()); 117 | 118 | // Client C comes in. 119 | let (_, ctx_c) = kv.get("x"); 120 | // now client C knows all the causal contex, so it replaces the key with all causal past. 121 | kv = kv.set("C", &ctx_c, "x", 20); 122 | assert_eq!(1, kv.store["x"].len()); 123 | 124 | // now B set with old empty context. 125 | kv = kv.set("B", &ctx_b, "x", 30); // I know contex is empty just set it as 30. 126 | 127 | // From client views latest B write is concurrent to C latest write. so both should be kept. 128 | assert_eq!(2, kv.store["x"].len()); 129 | 130 | for (k, v) in kv.store { 131 | println!("key: {}, values: {:?}", k, v) 132 | // val: {}, dot: {:?}", k, v, v.dot); 133 | } 134 | println!("vv: {:?}", kv.vv); 135 | 136 | } 137 | 138 | ``` 139 | 140 | ## Logical clocks in Real time 141 | 1. Go race detector uses vector clocks to detect data race between go routines. Basic idea is every go routine have its own vector clock and when a shared memory is accessed by multiple goroutines, their vector clocks are compared to find if they are concurrent! 142 | https://www.slideshare.net/InfoQ/looking-inside-a-race-detector 143 | 144 | 2. Riak Key Value store uses Dotted Version Vector to track concurrent versions of same key in multiple replica. 145 | https://riak.com/posts/technical/vector-clocks-revisited-part-2-dotted-version-vectors/index.html?p=9929.html 146 | 147 | ## References 148 | 1. https://haslab.uminho.pt/tome/files/dvvset-dais.pdf 149 | 2. https://github.com/ricardobcl/Dotted-Version-Vectors 150 | 3. https://lamport.azurewebsites.net/pubs/time-clocks.pdf 151 | 152 | ## Licence 153 | MIT 154 | -------------------------------------------------------------------------------- /src/vclock.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::HashSet; 3 | 4 | #[derive(Default)] 5 | pub struct VectorClock { 6 | vector: HashMap, 7 | // TODO(kavi): Add support mutex for thread-safe? 8 | } 9 | 10 | impl VectorClock { 11 | pub fn new() -> VectorClock { 12 | VectorClock { 13 | vector: HashMap::new(), 14 | } 15 | } 16 | 17 | pub fn inc(mut self, node_id: &str) -> Self { 18 | self.vector 19 | .entry(node_id.to_string()) 20 | .and_modify(|e| *e += 1) 21 | .or_insert(1); 22 | self 23 | } 24 | 25 | pub fn happened_before(&self, w: &VectorClock) -> bool { 26 | // happened_before check partial order between two vector clocks given. 27 | // If v *happens-before* w iff for every element i in v should be less than equal 28 | // to its corresponding element in w And at least one element should be strictly smaller 29 | // https://en.wikipedia.org/wiki/Vector_clock 30 | let keys = VectorClock::all_keys(&[&self.vector, &w.vector]); 31 | 32 | let mut sc = 0; 33 | 34 | for k in keys.iter() { 35 | let v1 = match self.vector.get(k) { 36 | None => 0, 37 | Some(v) => *v, 38 | }; 39 | let v2 = match w.vector.get(k) { 40 | None => 0, 41 | Some(v) => *v, 42 | }; 43 | 44 | if v1 > v2 { 45 | return false 46 | } 47 | 48 | if v1 < v2 { 49 | sc +=1; 50 | } 51 | } 52 | sc > 0 53 | } 54 | 55 | pub fn concurrent(&self, w: &VectorClock) -> bool { 56 | !(self.happened_before(w) || w.happened_before(self)) 57 | } 58 | 59 | /// merges the two given vectors via point-wise max. 60 | pub fn merge(&self, w: &VectorClock) -> VectorClock { 61 | let slice = vec![&self.vector, &w.vector]; 62 | let keys = VectorClock::all_keys(&slice[..]); 63 | let mut res: HashMap = HashMap::new(); 64 | println!("keys: {:?}", keys); 65 | for k in keys.iter() { 66 | let e1 = match self.vector.get(k) { 67 | None => 0, 68 | Some(v) => *v, 69 | }; 70 | let e2 = match w.vector.get(k) { 71 | None => 0, 72 | Some(v) => *v, 73 | }; 74 | 75 | res.insert(k.to_string(), std::cmp::max(e1, e2)); 76 | } 77 | 78 | VectorClock { vector: res } 79 | } 80 | 81 | fn all_keys(clocks: &[&HashMap]) -> HashSet { 82 | let mut res = HashSet::new(); 83 | 84 | for clock in clocks { 85 | for (k, _) in clock.iter() { 86 | res.insert(k.to_string()); 87 | } 88 | } 89 | res 90 | } 91 | } 92 | 93 | #[test] 94 | fn test_vv_new() { 95 | let mut vv = VectorClock::new(); 96 | vv = vv.inc("A").inc("B"); 97 | 98 | assert_eq!(vv.vector.get("A").unwrap(), &1_i64); 99 | assert_eq!(vv.vector.get("B").unwrap(), &1_i64); 100 | 101 | vv = vv.inc("A").inc("C"); 102 | 103 | assert_eq!(vv.vector.get("A").unwrap(), &2_i64); 104 | assert_eq!(vv.vector.get("C").unwrap(), &1_i64); 105 | } 106 | 107 | #[test] 108 | fn test_vv_merge() { 109 | // [2, 1] 110 | let v1 = VectorClock::new() 111 | .inc("A") 112 | .inc("A") 113 | .inc("B"); 114 | // [1, 2] 115 | let v2 = VectorClock::new() 116 | .inc("B") 117 | .inc("B") 118 | .inc("A"); 119 | 120 | let v3 = v1.merge(&v2); 121 | 122 | // [2, 2] 123 | assert_eq!(v3.vector.get("A").unwrap(), &2_i64); 124 | assert_eq!(v3.vector.get("B").unwrap(), &2_i64); 125 | } 126 | 127 | #[test] 128 | fn test_vv_happened_before() { 129 | // Case 0: v1 happened_before v2 130 | // [2, 3, 2] 131 | let v1 = VectorClock::new() 132 | .inc("A") 133 | .inc("A") 134 | .inc("B") 135 | .inc("B") 136 | .inc("B") 137 | .inc("C") 138 | .inc("C"); 139 | 140 | // [2, 4, 2] 141 | let v2 = VectorClock::new() 142 | .inc("A") 143 | .inc("A") 144 | .inc("B") 145 | .inc("B") 146 | .inc("B") 147 | .inc("B") 148 | .inc("C") 149 | .inc("C"); 150 | assert!(v1.happened_before(&v2)); 151 | assert!(!v2.happened_before(&v1)); 152 | 153 | // Case 1: Concurrent 154 | // [2, 3, 2] 155 | let v1 = VectorClock::new() 156 | .inc("A") 157 | .inc("A") 158 | .inc("B") 159 | .inc("B") 160 | .inc("B") 161 | .inc("C") 162 | .inc("C"); 163 | 164 | // [1, 4, 1] 165 | let v2 = VectorClock::new() 166 | .inc("A") 167 | .inc("B") 168 | .inc("B") 169 | .inc("B") 170 | .inc("B") 171 | .inc("C"); 172 | assert!(!v1.happened_before(&v2)); 173 | assert!(!v2.happened_before(&v1)); 174 | } 175 | 176 | #[test] 177 | fn test_vv_concurrent() { 178 | // Case 0: not concurrent 179 | // [2, 3, 2] 180 | let v1 = VectorClock::new() 181 | .inc("A") 182 | .inc("A") 183 | .inc("B") 184 | .inc("B") 185 | .inc("B") 186 | .inc("C") 187 | .inc("C"); 188 | 189 | // [3, 4, 2] 190 | let v2 = VectorClock::new() 191 | .inc("A") 192 | .inc("B") 193 | .inc("B") 194 | .inc("C"); 195 | assert!(!v1.concurrent(&v2)); 196 | assert!(!v2.concurrent(&v1)); 197 | 198 | // Case 1: Concurrent 199 | // [2, 3, 2] 200 | let v1 = VectorClock::new() 201 | .inc("A") 202 | .inc("A") 203 | .inc("B") 204 | .inc("B") 205 | .inc("B") 206 | .inc("C") 207 | .inc("C"); 208 | 209 | // [1, 4, 1] 210 | let v2 = VectorClock::new() 211 | .inc("A") 212 | .inc("B") 213 | .inc("B") 214 | .inc("B") 215 | .inc("B") 216 | .inc("C"); 217 | assert!(v1.concurrent(&v2)); 218 | assert!(v2.concurrent(&v1)); 219 | } 220 | -------------------------------------------------------------------------------- /src/dvv.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::HashSet; 3 | 4 | #[derive(Clone, Default, Debug)] 5 | pub struct VersionVector { 6 | vector:HashMap, 7 | // TODO(kavi): Add support mutex for thread-safe? 8 | } 9 | 10 | #[derive(Clone, Debug)] 11 | pub struct Dot (String, i64); 12 | 13 | impl VersionVector { 14 | pub fn new() -> VersionVector { 15 | VersionVector{ 16 | vector: HashMap::new(), 17 | } 18 | } 19 | 20 | pub fn inc(mut self, node_id:&str) -> Self{ 21 | self.vector.entry(node_id.to_string()).and_modify(|e| *e += 1).or_insert(1); 22 | self 23 | } 24 | 25 | pub fn descends(&self, w:&VersionVector) -> bool { 26 | let keys = VersionVector::all_keys(&[&self.vector, &w.vector]); 27 | // All the keys from 'self' should be greater than or equal to same key from 'w'. 28 | // So, now if both self and w are same, then it descends(v, v) => true 29 | for k in keys.iter() { 30 | let v1 = match self.vector.get(k) { 31 | None => 0, 32 | Some(v) => *v 33 | }; 34 | let v2 = match w.vector.get(k) { 35 | None => 0, 36 | Some(v) => *v 37 | }; 38 | if v1 < v2 { 39 | return false 40 | } 41 | } 42 | true 43 | } 44 | 45 | pub fn concurrent(&self, w:&VersionVector) -> bool { 46 | // if neither of them descends from another, then they are concurrent 47 | !(self.descends(w) || w.descends(self)) 48 | } 49 | 50 | pub fn descends_dot(&self, w:&Dot) -> bool { 51 | let v = match self.vector.get(&w.0) { 52 | None => 0, 53 | Some(v) => *v 54 | }; 55 | v >= w.1 56 | } 57 | 58 | /// merges the two given vectors via point-wise max. 59 | pub fn merge(&self, w:&VersionVector) -> VersionVector { 60 | let slice = vec![&self.vector, &w.vector]; 61 | let keys = VersionVector::all_keys(&slice[..]); 62 | let mut res:HashMap = HashMap::new(); 63 | for k in keys.iter() { 64 | let e1 = match self.vector.get(k) { 65 | None => 0, 66 | Some(v) => *v 67 | }; 68 | let e2 = match w.vector.get(k) { 69 | None => 0, 70 | Some(v) => *v, 71 | }; 72 | 73 | res.insert(k.to_string(), std::cmp::max(e1, e2)); 74 | } 75 | 76 | VersionVector{ 77 | vector: res, 78 | } 79 | } 80 | 81 | pub fn get_dot(&self, node_id:&str) -> Dot { 82 | let count = match self.vector.get(node_id) { 83 | None => 0, 84 | Some(v) => *v 85 | }; 86 | Dot(node_id.to_string(), count) 87 | } 88 | 89 | fn all_keys(clocks: &[&HashMap]) -> HashSet { 90 | let mut res = HashSet::new(); 91 | 92 | for clock in clocks { 93 | for (k, _) in clock.iter() { 94 | res.insert(k.to_string()); 95 | } 96 | } 97 | res 98 | } 99 | } 100 | 101 | 102 | impl Dot { 103 | pub fn descends_vv(&self, w:&VersionVector) -> bool { 104 | let v = match w.vector.get(&self.0) { 105 | None => 0, 106 | Some(v) => *v 107 | }; 108 | 109 | self.1 >= v 110 | } 111 | pub fn descends(&self, w:&Dot) -> bool { 112 | self.0 == w.0 && self.1 >= w.1 113 | } 114 | } 115 | 116 | 117 | #[test] 118 | fn test_vv_new() { 119 | let mut vv = VersionVector::new(); 120 | vv = vv.inc("A").inc("B"); 121 | 122 | assert_eq!(vv.vector.get("A").unwrap(), &1_i64); 123 | assert_eq!(vv.vector.get("B").unwrap(), &1_i64); 124 | 125 | vv = vv.inc("A").inc("C"); 126 | 127 | assert_eq!(vv.vector.get("A").unwrap(), &2_i64); 128 | assert_eq!(vv.vector.get("C").unwrap(), &1_i64); 129 | } 130 | 131 | #[test] 132 | fn test_vv_merge() { 133 | // [2, 1] 134 | let v1 = VersionVector::new() 135 | .inc("A") 136 | .inc("A") 137 | .inc("B"); 138 | // [1, 2] 139 | let v2 = VersionVector::new() 140 | .inc("B") 141 | .inc("B") 142 | .inc("A"); 143 | 144 | let v3 = v1.merge(&v2); 145 | 146 | // [2, 2] 147 | assert_eq!(v3.vector.get("A").unwrap(), &2_i64); 148 | assert_eq!(v3.vector.get("B").unwrap(), &2_i64); 149 | } 150 | 151 | #[test] 152 | fn test_vv_descends() { 153 | // Case 0: v2 descends v1 154 | // [2, 3, 2] 155 | let v1 = VersionVector::new() 156 | .inc("A") 157 | .inc("A") 158 | .inc("B") 159 | .inc("B") 160 | .inc("B") 161 | .inc("C") 162 | .inc("C"); 163 | 164 | // [3, 4, 2] 165 | let v2 = VersionVector::new() 166 | .inc("A") 167 | .inc("B") 168 | .inc("B") 169 | .inc("C"); 170 | 171 | assert!(v1.descends(&v2)); 172 | assert!(!v2.descends(&v1)); 173 | 174 | // Case 1: Concurrent 175 | // [2, 3, 2] 176 | let v1 = VersionVector::new() 177 | .inc("A") 178 | .inc("A") 179 | .inc("B") 180 | .inc("B") 181 | .inc("B") 182 | .inc("C") 183 | .inc("C"); 184 | 185 | // [1, 4, 1] 186 | let v2 = VersionVector::new() 187 | .inc("A") 188 | .inc("B") 189 | .inc("B") 190 | .inc("B") 191 | .inc("B") 192 | .inc("C"); 193 | 194 | assert!(!v1.descends(&v2)); 195 | assert!(!v2.descends(&v1)); // neither v2 descends Case 196 | } 197 | 198 | #[test] 199 | fn test_vv_concurrent() { 200 | // Case 0: not concurrent 201 | // [2, 3, 2] 202 | let v1 = VersionVector::new(). 203 | inc("A") 204 | .inc("A") 205 | .inc("B") 206 | .inc("B") 207 | .inc("B") 208 | .inc("C") 209 | .inc("C"); 210 | 211 | // [3, 4, 2] 212 | let v2 = VersionVector::new() 213 | .inc("A") 214 | .inc("B") 215 | .inc("B") 216 | .inc("C"); 217 | 218 | assert!(!v1.concurrent(&v2)); 219 | assert!(!v2.concurrent(&v1)); 220 | 221 | // Case 1: Concurrent 222 | // [2, 3, 2] 223 | let v1 = VersionVector::new() 224 | .inc("A") 225 | .inc("A") 226 | .inc("B") 227 | .inc("B") 228 | .inc("B") 229 | .inc("C") 230 | .inc("C"); 231 | 232 | // [1, 4, 1] 233 | let v2 = VersionVector::new(). 234 | inc("A"). 235 | inc("B"). 236 | inc("B"). 237 | inc("B"). 238 | inc("B"). 239 | inc("C"); 240 | assert!(v1.concurrent(&v2)); 241 | assert!(v2.concurrent(&v1)); 242 | } 243 | 244 | #[test] 245 | fn test_get_dot() { 246 | let v = VersionVector::new().inc("A").inc("B"); 247 | let dot = v.get_dot("A"); 248 | 249 | assert_eq!("A", dot.0); 250 | assert_eq!(1, dot.1); 251 | } 252 | 253 | #[test] 254 | fn test_descends_dot() { 255 | let v = VersionVector::new() 256 | .inc("A") 257 | .inc("A") 258 | .inc("B"); 259 | 260 | let dot = Dot("A".to_string(), 3); 261 | 262 | assert!(dot.descends_vv(&v)); 263 | assert!(!v.descends_dot(&dot)); 264 | 265 | let v = VersionVector::new() 266 | .inc("A") 267 | .inc("A") 268 | .inc("B"); 269 | 270 | let dot = Dot("A".to_string(), 1); 271 | assert!(!dot.descends_vv(&v)); 272 | assert!(v.descends_dot(&dot)); 273 | 274 | } 275 | --------------------------------------------------------------------------------