cvrdt_exposition/
lib.rs

1//! # Understanding Convergent Replicated Data Types
2//!
3//! I wanted to understand CRDTs more, so I put together this Rust library for state-based CRDTs a.k.a. convergent replicated data types a.k.a. `CvRDTs`.
4//! It aims to present an explicit (with strong types, etc.) and unified description of the `CvRDTs` presented in the _wonderful_ paper [A comprehensive study of Convergent and Commutative Replicated Data Types](https://hal.inria.fr/inria-00555588/).
5//!
6//! ## Do not use this!
7//!
8//! This code is solely for my own edification and is _not_ meant for production use.
9//! There are already much better options for usable CRDTs in Rust; see the [`rust-crdt`](https://github.com/rust-crdt/rust-crdt) project.
10//!
11//! ## What makes a `CvRDT`?
12//!
13//! Quoting the [Wikipedia article on CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type),
14//! > `CvRDTs` send their full local state to other replicas, where the states are merged by a function which must be commutative, associative, and idempotent.
15//!
16//! So suppose we've just written a brand new data type, and we'd like to demonstrate it's a `CvRDT`.
17//! Suppose further that _x_, _y_, and _z_ are any arbitrary members of our data type, and that our data type has a merge function called _merge_; for our type to be a `CvRDT`, we need the following three things to be true for **any** values of _x_, _y_, and _z_:
18//! 1. _merge(x, y) = merge(y, x)_
19//! 2. _merge(x, merge(y, z)) = merge(merge(x, y), z)_
20//! 3. _merge(merge(x, y), y) = merge(x, y)_
21//!
22//! [Wikipedia](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) continues,
23//! > The merge function provides a join for any pair of replica states, so the set of all states forms a semilattice.
24//!
25//! Our merge function _merge_ induces a partial order on all the elements of our type, akin to Rust's [`PartialOrd` trait](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html), where _x ≤ y_ if _merge(x, y) = y_.
26//! Since for any two elements _x_ and _y_, both _x ≤ merge(x, y)_ and _y ≤ merge(x, y)_, and _merge(x, y)_ is the "smallest" such elment for which this occurs, it is the least upper bound (or **join**) of _x_ and _y_.
27//!
28//! [The article](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) goes on
29//! > The update function must monotonically increase the internal state, according to the same partial order rules as the semilattice.
30//!
31//! Suppose our type has an update function which we'll call _add_.
32//! For any element _x_ and update value _u_, we need _x ≤ update(x, u)_; that is, we need _merge(x, update(x, u)) = update(x, u)_.
33//!
34//! Many types of CvRDT—in this library especially—are grow-only; they can add new elements/values/etc., but once added, these cannot be removed.
35//! Some types allow removal as well; if our type does so, we **must** ensure that removal maintains the partial order given by our _merge_ function.
36//! That is, we need the internal state to increase monotonically (as Wikipedia says in the above quote) even in the face of removals.
37//! Suppose our type has a function
38//!
39//! Ideally we'd check these requirements for **every** possible configuration of our new `CvRDT` implementation.
40//! This can be impossible to do via brute force, as the number of states to check can quickly get prohibitively large.
41//! See the [penultimate section](#how-cvrdt-exposition-verifies-properties) for how `cvrdt-exposition` verifies these properties.
42//!
43//! ## Examples
44//!
45//! Per the [Wikipedia article](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type), consider a one-way boolean flag that, once true, can never revert to false:
46//! ```
47//! use cvrdt_exposition::{Grow, OneWayBoolean};
48//! let x = OneWayBoolean::new(false);
49//! let mut y = OneWayBoolean::new(false);
50//! y.add(());
51//! assert_eq!(y.payload(), true);
52//! assert_eq!(y.merge(&x).payload(), true);
53//! ```
54//!
55//! As the internal state of a `OneWayBoolean` is only a single boolean value, we could verify the implementation fulfills the `CvRDT` requirements  by hand (though I'd rather get my computer to do it! see below).
56//!
57//! ## How `cvrdt-exposition` verifies properties
58//!
59//! In the absence of using formal methods like [TLA+](https://learntla.com/introduction/) (see also Hillel Wayne's [excellent book!](https://learntla.com/book/)), we resort to property-based testing via the [`proptest` crate](https://crates.io/crates/proptest).
60//! This excellent crate is listed as a `dev-dependency` in [`cvrdt-exposition`'s `Cargo.tml` file](https://github.com/genos/cvrdt-exposition/blob/main/Cargo.toml), so if you can just use the stuff in this library (although [you shouldn't!](do-not-use-this)) without pulling in an extra dependency.
61//! That said, I highly recommend learning to use `proptest`, [`quickcheck`](https://crates.io/crates/quickcheck), or some other [property testing framework](https://crates.io/search?q=property%20testing) for Rust.
62//!
63//! In the `cfg(test)`-only [`properties` module](https://github.com/genos/cvrdt/blob/main/src/properties.rs), `cvrdt-exposition` defines macros for automating checking `CvRDT` properties.
64//! For instance, given a function `arb_cvrdt2` that yields an arbitrary pair of elements of our `CvRDT` type, `proptest`'s `proptest!` macro allows us to test that our `CvRDT`'s merge function is commutative via:
65//!
66//! ```ignore
67//! proptest! {
68//!     #[test]
69//!     fn merge_commutative((x, y) in $arb_cvrdt2()) {
70//!         prop_assert_eq!(
71//!             Grow::payload(&Grow::merge(&x, &y)),
72//!             Grow::payload(&Grow::merge(&y, &x))
73//!         );
74//!     }
75//! };
76//! ```
77//!
78//! Note that the above code uses the `cvrdt-exposition` nomenclature `Grow`, which we use to distinguish `CvRDTs` that are grow-only from those that can also remove elements.
79//! See the [traits documentation](traits/index.html) for more.
80//!
81//! ## References
82//!
83//! - [A comprehensive study of Convergent and Commutative Replicated Data Types](https://hal.inria.fr/inria-00555588/)
84//! - Wikipedia article on [Conflict-free replicated data type](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
85//! - [`rust-crdt`](https://github.com/rust-crdt/rust-crdt)
86//! - [`meangirls`](https://github.com/aphyr/meangirls)
87//! - [The `proptest` book](https://altsysrq.github.io/proptest-book/intro.html)
88#![forbid(unsafe_code)]
89#![forbid(missing_docs)]
90
91/// Our two traits defining `CvRDTs`
92pub mod traits;
93
94/// Grow-Only Counter
95pub mod g_counter;
96/// Grow-Only Set
97pub mod g_set;
98/// Last-Writer-Wins Register
99pub mod lww_register;
100/// The simplest `CvRDT` example: a boolean flag that, once true, can never revert to false
101pub mod one_way_boolean;
102/// Positive-Negative Counter
103pub mod pn_counter;
104/// Two-Phase Set
105pub mod two_phase_set;
106
107/// Top-level re-exports for CRDT structures and traits
108pub use crate::{
109    g_counter::GCounter,
110    g_set::GSet,
111    lww_register::LWWRegister,
112    one_way_boolean::OneWayBoolean,
113    pn_counter::PNCounter,
114    traits::{Grow, Shrink},
115    two_phase_set::TwoPhaseSet,
116};
117
118/// PBT for `CvRDT` properties
119#[cfg(test)]
120pub(crate) mod properties;