1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! [VClock](https://gitlab.com/ufoot/vclock) is an experimental vector clock implementated in [Rust](https://www.rust-lang.org/).
//!
//! It offers a [partial order of events in a distributed system](https://en.wikipedia.org/wiki/Vector_clock).
//! In practice, it implements [the rust partial order trait](https://doc.rust-lang.org/stable/std/cmp/trait.PartialOrd.html) over hash maps which maintain an integer count of each modification, per key.
//!
//! ![VClock icon](https://gitlab.com/ufoot/vclock/raw/master/vclock.png)
//!
//! # Examples
//!
//! Basic usage:
//!
//! ```
//! use vclock::VClock;
//!
//! let c1 = VClock::new("a");     // c1 is now a:1
//! let mut c2 = VClock::new("b"); // c2 is now b:1
//! c2.incr("a");                  // c1 is now a:1,b:1
//! assert!(c1 < c2, "c1 is a parent of c2");
//! ```
//!
//! Here is how to generate a conflict:
//!
//! ```
//! use vclock::VClock;
//!
//! let mut c1: VClock<&str>=VClock::default();
//!
//! c1.incr("a");
//! c1.incr("a");
//! c1.incr("a");
//! c1.incr("a");
//! c1.incr("b"); // c1 is now a:4, b:1
//!
//! let c2 = c1.next("a"); // c2 is now a:5,b:1
//! let c3 = c1.next("b"); // c3 is now a:4,b:2
//!
//! // Now let's assert there is a relationship c1 -> c2 and c1 -> c3.
//! // By relationship, we mean that there is a forward path of updates
//! // using incr() or next() from one member to the other.
//! assert!(c1<=c2, "c1={} is a parent of c2={}", c1, c2);
//! assert!(c2>=c1, "c2={} is a child of c1={}", c2, c1);
//! assert!(c1<=c3, "c1={} c1 is a parent of c3={}", c1, c3);
//! assert!(c3>=c1, "c3={} c1 is a child of c1={}", c3, c1);
//!
//! // But there is no relationship between c2 and c3.
//! // In a distributed system with concurrent updates of an object,
//! // this allows the detection of a conflict.
//! assert!(!(c2<=c3), "c2={} is not a parent of c3={}", c2, c3);
//! assert!(!(c2>=c3), "c2={} is not a child of c3={}", c2, c3);
//! assert!(!(c3<=c2), "c3={} is not a parent of c2={}", c3, c2);
//! assert!(!(c3>=c2), "c3={} is not a child of c2={}", c3, c2);
//! ```
//!
//! Also, two objects having a different history path, but the same
//! final state, will be considered the same. It is up to the program
//! to ensure only one agent updates a given key:
//!
//! ```
//! use vclock::VClock;
//!
//! let mut c1 = VClock::new("a"); // c1 is now a:1
//! let mut c2 = VClock::new("b"); // c2 is now b:1
//! c1.incr("c"); // c1 is now a:1, c:1
//! c1.incr("b"); // c1 is now a:1, b:1, c:1
//! c2.incr("a"); // c2 is now a:1, b:1, which is a value c1 never had
//! c2.incr("c"); // c2 is now a:1, b:1, c:1
//!
//! // The following test shows clocks are considered the same.
//! // In a distributed system using vector clocks, only one agent
//! // would update a given key, and when updating this key, it should
//! // ensure than there is no conflict with the latest version it had locally.
//! assert_eq!(c1, c2, "c1={} and c2={} represent the same final state", c1, c2);
//! ```
//!
//! A typical conflict resolution:
//!
//! ```
//! use std::collections::HashMap;
//! use vclock::VClock;
//!
//! // A dummy object with maintains a vector clock along with some data,
//! // here just a byte, to avoid complexifying the example with other issues.
//! #[derive(Default)]
//! struct Obj<'a> {
//!     vc: VClock<&'a str>,
//!     val: u8,
//! }
//!
//! impl<'a> Obj<'a> {
//!     // Update the data stored within the object. If there's no conflict,
//!     // the data is updated, the clock is set to the last value received,
//!     // and the function returns None.
//!     // If there is a conflict, then the previous data is returned, along
//!     // with the merged clock.
//!     fn update(
//!         &mut self,
//!         remote_clock: &VClock<&'a str>,
//!         val: u8,
//!     ) -> Option<(u8, VClock<&str>)> {
//!         if &self.vc < remote_clock {
//!             // Update value, no conflict
//!             self.vc = remote_clock.clone();
//!             self.val = val;
//!             None
//!         } else {
//!             // History conflict, update the clock, return the value,
//!             // and let the caller deal with it.
//!             Some((self.val, self.vc.merge(remote_clock)))
//!         }
//!     }
//! }
//!
//! let mut obj = Obj::default();
//! let mut h1 = HashMap::<&str,u64>::new();
//! h1.insert("a", 42);
//! let c1 = VClock::<&str>::from(h1);
//! assert_eq!(None, obj.update(&c1, 10), "no history, no problem");
//!
//! let mut h2 = HashMap::<&str,u64>::new();
//! h2.insert("a", 42);
//! h2.insert("b", 10);
//! let c2 = VClock::<&str>::from(h2);
//! assert_eq!(None, obj.update(&c2, 100), "forward history, updating");
//!
//! let mut h3 = HashMap::<&str,u64>::new();
//! h3.insert("a", 43);
//! h3.insert("b", 9);
//! let c3 = VClock::<&str>::from(h3);
//! let mut h4 = HashMap::<&str,u64>::new();
//! h4.insert("a", 43);
//! h4.insert("b", 10);
//! let c4 = VClock::<&str>::from(h4);
//! assert_eq!(Some((100, c4)), obj.update(&c3, 50), "conflict between keys");
//! ```

mod base;
pub use base::*;