Skip to content

Commit

Permalink
fixup! Update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
abangratz authored and alessiaplatogo committed Oct 30, 2023
1 parent 8487194 commit ed2428e
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 2 deletions.
108 changes: 108 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

This is a set of basic, composable and extensible CRDTs.

A CRDT is defined as Conflict-free Replicated Data Type,
see https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type

Please refer to the API Reference for usage documentation and examples.

## Installation
Expand All @@ -21,3 +24,108 @@ Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_do
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/crdt>.

## Examples

These are simple examples that should give an idea of how to use some of the data types,
and what to expect in each case.

### `CRDT.GCounter`

A `CRDT.GCounter` is a growth-only counter. It can be initialized with positive values on
behalf of actors. The resulting value will always be the sum of the values across actors.
An empty counter will have the value '0'.


#### Initializing

``` elixir
counter = CRDT.GCounter.new
CRDT.value(counter) # => 0

counter = CRDT.GCounter.new(actor1: 5, actor2: 10)
CRDT.value(counter) # => 15
```

#### Incrementing

Incrementing a `CRDT.GCounter` is done via the `CRDT.GCounter.inc/2` function.
If the actor key does not exist yet, it is assumed that the given value is the starting
value.

``` elixir
counter = CRDT.GCounter.new
counter = counter |> CRDT.GCounter.inc(:a, 5) # => %CRDT.GCounter{value: %{a: 5}}
counter = counter |> CRDT.GCounter.inc(:a, 2) # => %CRDT.GCounter{value: %{a: 7}}
CRDT.value(counter) # => 7
```

#### Merging

Merging two GCounters preserves all actors in both, taking the higher value if an actor exists in both GCounters.

``` elixir
counter1 = CRDT.GCounter.new(actor1: 5, actor2: 3)
counter2 = CRDT.GCounter.new(actor2: 1, actor3: 8)
CRDT.merge(counter1, counter2) # => %CRDT.GCounter{value: %{actor1: 5, actor2: 3, actor3: 8}}
```

### `CRDT.PNCounter`

A `CRDT.PNCounter` is used to process events that can increment or decrement the value.

#### Initializing

When initialized without starting values, the `CRDT.PNCounter` initial value is '0'.

``` elixir
counter = CRDT.PNCounter.new #=> %CRDT.PNCounter{pos: %{}, neg: %{}}
CRDT.value(counter) #=> 0
```

Initial values can be supplied as positive and negative actor => value maps.

``` elixir
counter = CRDT.PNCounter.new(pos: %{a: 1, b: 2}, neg: %{a: 8, b: 7})
CRDT.value(counter) #=> -12
```

### `CRDT.AWORMap`

A `CRDT.AWORMap` is a map whose elements are crdts.
Merging strategy follows those of the crdts contained in the map.

#### Initializing

``` elixir
map = CRDT.AWORMap.new
CRDT.value(map) #=> %{}
```
When initialized the `CRDT.AWORMap` has this structure:

``` elixir
%CRDT.AWORMap{
keys: %CRDT.AWORSet{
dot_kernel: %CRDT.DotKernel{
dot_context: %CRDT.DotContext{version_vector: %{}, dot_cloud: []},
entries: %{}
}
},
entries: %{}
}
```

Inside `CRDT.DotKernel` are the operations performed on the map: which actor made the change, the number of operation and the value added.
Inside the last entries is the map keylist with the current values.

It's possible to put a crdt in the `CRDT.AWORMap` through the `CRDT.AWORMap.put/4` function.
It's necessary to specify the actor who make the change, the key under which the crdt will be stored and the crdt itself.

``` elixir
map = CRDT.AWORMap.new
map = map |> CRDT.AWORMap.put(:a, :key, CRDT.GCounter.new())
CRDT.value(map) #=> %{key: 0}
```

#### Updating

Updating a `CRDT.AWORMap` is done via the `CRDT.AWORMap.update!/4` function.
1 change: 0 additions & 1 deletion lib/crdt.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ defprotocol CRDT do
@moduledoc """
Protocol defining the interface for CRDTs.
For more information on CRDTs, see https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type
"""

@type actor :: term()
Expand Down
2 changes: 1 addition & 1 deletion lib/crdt/dot_kernel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ defmodule CRDT.DotKernel do
for {dot, entry_value} <- entries, entry_value == value, reduce: entries do
entries ->
# The corresponding dot says in the dot context and acts as a tombstone.
# So we can avoid readding it when merging with an out of date replica.
# So we can avoid reading it when merging with an out of date replica.
Map.delete(entries, dot)
end

Expand Down

0 comments on commit ed2428e

Please sign in to comment.