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
|
extern crate crdts;
use crdts::{CmRDT, CvRDT, VClock};
use std::cmp::Ordering::*;
fn main() {
#[derive(Debug, Default, Clone, PartialEq)]
struct VersionedString {
clock: VClock<String>,
data: String,
}
let shared_password = VersionedString::default();
// alice and bob take a copy ...
let mut bobs_copy = shared_password.clone();
let mut alices_copy = shared_password;
// bob edits the shared password..
bobs_copy
.clock
.apply(bobs_copy.clock.inc("BOB".to_string()));
bobs_copy.data = "pa$$w0rd".to_string();
// ... and shares it with alice.
// Alice first compares the vclock of her copy with bob's:
match alices_copy.clock.partial_cmp(&bobs_copy.clock) {
Some(Less) => { /* bob's clock is ahead */ }
_ => panic!("Bob's clock should be ahead!!"),
}
// Alice sees that bob's clock is ahead of hers.
// This tells her that Bob has seen every edit she has
// seen and his string is a more recent version.
alices_copy = bobs_copy.clone();
// Now, alice decides to changes the password.
alices_copy
.clock
.apply(alices_copy.clock.inc("ALICE".to_string()));
alices_copy.data = "letMein32".to_string();
// But! concurrently, bob edits the password again!
bobs_copy
.clock
.apply(bobs_copy.clock.inc("BOB".to_string()));
bobs_copy.data = "0sdjf0as9j13k0zc".to_string();
// Alice shares her edit with bob and bob compares clocks
match bobs_copy.clock.partial_cmp(&alices_copy.clock) {
None => { /* these clocks are not ordered! */ }
_ => panic!("These clocks are not ordered!"),
}
// If we take a look at the clocks we see the problem.
assert_eq!(format!("{}", bobs_copy.clock), "<BOB:2>");
assert_eq!(format!("{}", alices_copy.clock), "<ALICE:1, BOB:1>");
// bob's version counter is bigger on his copy but alices
// version counter is bigger on her copy
// (version counters default to 0 if not present in a clock)
// This is how VClocks can be used to detect conflicts.
// Bob needs to manually look at the two strings and decide
// how to manage this conflict.
// Bob decides to keep Alices string, he merges alices clock
// into his to signify that he has seen her edits.
bobs_copy.clock.merge(alices_copy.clock.clone());
bobs_copy.data = "letMein32".to_string();
// looking once more at bob's clock we see it includes all
// edits done by both bob and alice
assert_eq!(format!("{}", bobs_copy.clock), "<ALICE:1, BOB:2>");
// Once Alice receives bob's updated password she'll see that
// his clock is ahead of hers and choose to keep his versioned string.
match alices_copy.clock.partial_cmp(&bobs_copy.clock) {
Some(Less) => {
// bob's clock is ahead
alices_copy = bobs_copy.clone()
}
_ => panic!("Alice's clock should be behind!!"),
}
assert_eq!(alices_copy, bobs_copy);
}
|