# [−][src]Crate cvrdt_exposition

# Understanding Convergent Replicated Data Types

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.
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.

## Do not use this!

This code is solely for my own edification and is *not* meant for production use.
There are already much better options for usable CRDTs in Rust; see the `rust-crdt`

project.

## What makes a CvRDT?

Quoting the Wikipedia article on CRDTs,

CvRDTs send their full local state to other replicas, where the states are merged by a function which must be commutative, associative, and idempotent.

So suppose we've just written a brand new data type, and we'd like to demonstrate it's a CvRDT.
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*:

*merge(x, y) = merge(y, x)**merge(x, merge(y, z)) = merge(merge(x, y), z)**merge(merge(x, y), y) = merge(x, y)*

Wikipedia continues,

The merge function provides a join for any pair of replica states, so the set of all states forms a semilattice.

Our merge function *merge* induces a partial order on all the elements of our type, akin to Rust's `PartialOrd`

trait, where *x ≤ y* if *merge(x, y) = y*.
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*.

The article goes on

The update function must monotonically increase the internal state, according to the same partial order rules as the semilattice.

Suppose our type has an update function which we'll call *add*.
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)*.

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.
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.
That is, we need the internal state to increase monotonically (as Wikipedia says in the above quote) even in the face of removals.
Suppose our type has a function

Ideally we'd check these requirements for **every** possible configuration of our new CvRDT implementation.
This can be impossible to do via brute force, as the number of states to check can quickly get prohibitively large.
See the penultimate section for how `cvrdt-exposition`

verifies these properties.

## Examples

Per the Wikipedia article, consider a one-way boolean flag that, once true, can never revert to false:

use cvrdt_exposition::{Grow, OneWayBoolean}; let x = OneWayBoolean::new(false); let mut y = OneWayBoolean::new(false); y.add(()); assert_eq!(y.payload(), true); assert_eq!(y.merge(&x).payload(), true);

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).

## How `cvrdt-exposition`

verifies properties

In the absence of using formal methods like TLA+ (see also Hillel Wayne's excellent book!), we resort to property-based testing via the `proptest`

crate.
This excellent crate is listed as a `dev-dependency`

in `cvrdt-exposition`

's `Cargo.tml`

file, so if you can just use the stuff in this library (although you shouldn't!) without pulling in an extra dependency.
That said, I highly recommend learning to use `proptest`

, `quickcheck`

, or some other property testing framework for Rust.

In the `cfg(test)`

-only `properties`

module, `cvrdt-exposition`

defines macros for automating checking CvRDT properties.
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:

proptest! { #[test] fn merge_commutative((x, y) in $arb_cvrdt2()) { prop_assert_eq!( Grow::payload(&Grow::merge(&x, &y)), Grow::payload(&Grow::merge(&y, &x)) ); } };

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.
See the traits documentation for more.

## References

## Re-exports

`pub use crate::g_counter::GCounter;` |

`pub use crate::g_set::GSet;` |

`pub use crate::lww_register::LWWRegister;` |

`pub use crate::one_way_boolean::OneWayBoolean;` |

`pub use crate::pn_counter::PNCounter;` |

`pub use crate::traits::Grow;` |

`pub use crate::traits::Shrink;` |

`pub use crate::two_phase_set::TwoPhaseSet;` |

## Modules

g_counter | Grow-Only Counter |

g_set | Grow-Only Set |

lww_register | Last-Writer-Wins Register |

one_way_boolean | The simplest CvRDT example: a boolean flag that, once true, can never revert to false |

pn_counter | Positive-Negative Counter |

traits | Our two traits defining CvRDTs |

two_phase_set | Two-Phase Set |