Skip to content
This repository was archived by the owner on May 27, 2022. It is now read-only.

Commit 998cafd

Browse files
authored
Merge pull request #24 from peterzeller/improved-types
Improved interface for dialyzer
2 parents 656a8c6 + 714a2c7 commit 998cafd

File tree

3 files changed

+194
-49
lines changed

3 files changed

+194
-49
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ otp_release:
55
- 20.1
66
install:
77
- mkdir -p ~/.config/rebar3/
8-
- echo "{plugins, [rebar3_proper]}." > ~/.config/rebar3/rebar.config
8+
- echo "{plugins, [{rebar3_proper, \"0.9.0\"}]}." > ~/.config/rebar3/rebar.config
99
- make
1010
- ./rebar3 update
1111
script:

README.md

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,98 @@
1-
# Antidote CRDT library
1+
# Antidote CRDT library
22
[![Build Status](https://travis-ci.org/SyncFree/antidote_crdt.svg?branch=master)](https://travis-ci.org/SyncFree/antidote_crdt)
33

4-
CRDT implementations to use with Antidote.
4+
Operation based CRDT implementations to use with Antidote.
5+
6+
# API
7+
8+
The `antidote_crdt` module provides the API below.
9+
For a more detailed description of the different data types, the [Antidote Documentation](http://syncfree.github.io/antidote/crdts.html) provides more information.
10+
11+
```erlang
12+
% The CRDTs supported by Antidote:
13+
-type typ() ::
14+
antidote_crdt_counter_pn % PN-Counter aka Positive Negative Counter
15+
| antidote_crdt_counter_b % Bounded Counter
16+
| antidote_crdt_counter_fat % Fat Counter
17+
| antidote_crdt_flag_ew % Enable Wins Flag aka EW-Flag
18+
| antidote_crdt_flag_dw % Disable Wins Flag DW-Flag
19+
| antidote_crdt_set_go % Grow Only Set aka G-Set
20+
| antidote_crdt_set_aw % Add Wins Set aka AW-Set
21+
| antidote_crdt_set_rw % Remove Wins Set aka RW-Set
22+
| antidote_crdt_register_lww % Last Writer Wins Register aka LWW-Reg
23+
| antidote_crdt_register_mv % MultiValue Register aka MV-Reg
24+
| antidote_crdt_map_go % Grow Only Map aka G-Map
25+
| antidote_crdt_map_rr. % Recursive Resets Map aka RR-Map
26+
27+
% Note: the crdt and effect types are not correct, the tags just help to find errors
28+
% The State of a CRDT:
29+
-opaque crdt() :: ...
30+
% The downstream effect, which has to be applied at each replica
31+
-opaque effect() :: ...
32+
% The update operation, consisting of operation name and parameters
33+
% (e.g. {increment, 1} to increment a counter by one)
34+
-type update() :: {atom(), term()}.
35+
% Result of reading a CRDT (state without meta data)
36+
-type value() :: term().
37+
```
38+
39+
```erlang
40+
% Check if the given type is supported by Antidote
41+
-spec is_type(typ()) -> boolean().
42+
43+
% Returns the initial CRDT state for the given Type
44+
-spec new(typ()) -> crdt().
45+
46+
% Reads the value from a CRDT state
47+
-spec value(typ(), crdt()) -> any().
48+
49+
% Computes the downstream effect for a given update operation and current state.
50+
% This has to be called once at the source replica.
51+
% The effect must then be applied on all replicas using the update function.
52+
% For some update operation it is not necessary to provide the current state
53+
% and the atom 'ignore' can be passed instead (see function require_state_downstream).
54+
-spec downstream(typ(), update(), crdt() | ignore) -> {ok, effect()} | {error, reason()}.
55+
56+
% Updates the state of a CRDT by applying a downstream effect calculated
57+
% using the downstream function.
58+
% For most types the update function must be called in causal order:
59+
% if Eff2 was calculated on a state where Eff1 was already replied,
60+
% then Eff1 has to be applied before Eff2 on all replicas.
61+
-spec update(typ(), effect(), crdt()) -> {ok, crdt()}.
62+
63+
% Checks whether the current state is required by the downstream function
64+
% for a specific type and update operation
65+
-spec require_state_downstream(typ(), update()) -> boolean().
66+
67+
% Checks whether the given update operation is valid for the given type
68+
-spec is_operation(typ(), update()) -> boolean().
69+
```
570

671
# Development
772

873
Use the following `make` targets to build and test the CRDT library:
974

1075

11-
# compile
12-
make compile
13-
# run unit tests:
14-
make test
15-
# check types:
16-
make dialyzer
17-
# check code style:
18-
make lint
76+
# compile
77+
make compile
78+
# run unit tests:
79+
make test
80+
# check types:
81+
make dialyzer
82+
# check code style:
83+
make lint
1984

2085

2186
## PropEr tests
2287

2388
To run the property based tests in the test directory install the [rebar3 PropEr plugin](https://www.rebar3.org/docs/using-available-plugins#proper) by adding the following line to `~/.config/rebar3/rebar.config`:
2489

25-
{plugins, [rebar3_proper]}.
90+
{plugins, [{rebar3_proper, "0.9.0"}]}.
2691

2792
Then execute the tests with:
2893

29-
make proper
94+
make proper
3095

3196
For more control, you can run PropEr manually and specify parameters like the tested module or the number of generated tests:
3297

33-
rebar3 proper -n 1000 -m prop_crdt_orset
98+
rebar3 proper -n 1000 -m prop_crdt_orset

src/antidote_crdt.erl

Lines changed: 115 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
%% Naming pattern of antidote crdts: <type>_<semantics>
2323
%% if there is only one kind of semantics implemented for a certain type
2424
%% only the type is used in the name e.g. rga
25-
%% counter_pn: PN-Counter aka Posistive Negative Counter
25+
%% counter_pn: PN-Counter aka Positive Negative Counter
2626
%% counter_b: Bounded Counter
2727
%% counter_fat: Fat Counter
2828
%% integer: Integer (Experimental)
@@ -35,48 +35,128 @@
3535
%% register_mv: MultiValue Register aka MV-Reg
3636
%% map_go: Grow Only Map aka G-Map
3737
%% map_aw: Add Wins Map aka AW-Map (Experimental)
38-
%% map_rr: Recursive Resets Map akak RR-Map
38+
%% map_rr: Recursive Resets Map aka RR-Map
3939
%% rga: Replicated Growable Array (Experimental)
4040

4141

4242

4343
-module(antidote_crdt).
4444

45-
-include("antidote_crdt.hrl").
46-
47-
-define(CRDTS, [antidote_crdt_counter_pn,
48-
antidote_crdt_counter_b,
49-
antidote_crdt_counter_fat,
50-
antidote_crdt_flag_ew,
51-
antidote_crdt_flag_dw,
52-
antidote_crdt_set_go,
53-
antidote_crdt_set_aw,
54-
antidote_crdt_set_rw,
55-
antidote_crdt_register_lww,
56-
antidote_crdt_register_mv,
57-
antidote_crdt_map_go,
58-
antidote_crdt_map_rr]).
59-
60-
-export([is_type/1
61-
]).
62-
63-
-callback new() -> crdt().
64-
-callback value(crdt()) -> value().
65-
-callback downstream(update(), crdt()) -> {ok, effect()} | {error, reason()}.
66-
-callback update(effect(), crdt()) -> {ok, crdt()}.
45+
46+
% The CRDTs supported by Antidote:
47+
-type typ() ::
48+
antidote_crdt_counter_pn
49+
| antidote_crdt_counter_b
50+
| antidote_crdt_counter_fat
51+
| antidote_crdt_flag_ew
52+
| antidote_crdt_flag_dw
53+
| antidote_crdt_set_go
54+
| antidote_crdt_set_aw
55+
| antidote_crdt_set_rw
56+
| antidote_crdt_register_lww
57+
| antidote_crdt_register_mv
58+
| antidote_crdt_map_go
59+
| antidote_crdt_map_rr.
60+
61+
% Note: the crdt and effect types are not correct, the tags just help to find errors
62+
% The State of a CRDT:
63+
-opaque crdt() :: {antidote_crdt, state, term()}.
64+
% The downstream effect, which has to be applied at each replica
65+
-opaque effect() :: {antidote_crdt, effect, term()}.
66+
% The update operation, consisting of operation name and parameters
67+
% (e.g. {increment, 1} to increment a counter by one)
68+
-type update() :: {atom(), term()}.
69+
% Result of reading a CRDT (state without meta data)
70+
-type value() :: term().
71+
72+
% reason for an error
73+
-type reason() :: term().
74+
75+
-export_type([
76+
crdt/0,
77+
update/0,
78+
effect/0,
79+
value/0,
80+
typ/0
81+
]).
82+
83+
84+
-type internal_crdt() :: term().
85+
-type internal_effect() :: term().
86+
87+
-export([is_type/1, new/1, value/2, downstream/3, update/3, require_state_downstream/2, is_operation/2]).
88+
89+
% Callbacks implemented by each concrete CRDT implementation
90+
-callback new() -> internal_crdt().
91+
-callback value(internal_crdt()) -> value().
92+
-callback downstream(update(), internal_crdt()) -> {ok, internal_effect()} | {error, reason()}.
93+
-callback update(internal_effect(), internal_crdt()) -> {ok, internal_crdt()}.
6794
-callback require_state_downstream(update()) -> boolean().
68-
-callback is_operation(update()) -> boolean(). %% Type check
95+
-callback is_operation(update()) -> boolean(). %% Type check
96+
97+
-callback equal(internal_crdt(), internal_crdt()) -> boolean().
98+
-callback to_binary(internal_crdt()) -> binary().
99+
-callback from_binary(binary()) -> {ok, internal_crdt()} | {error, reason()}.
100+
101+
% Check if the given type is supported by Antidote
102+
-spec is_type(typ()) -> boolean().
103+
is_type(antidote_crdt_counter_pn) -> true;
104+
is_type(antidote_crdt_counter_b) -> true;
105+
is_type(antidote_crdt_counter_fat) -> true;
106+
is_type(antidote_crdt_flag_ew) -> true;
107+
is_type(antidote_crdt_flag_dw) -> true;
108+
is_type(antidote_crdt_set_go) -> true;
109+
is_type(antidote_crdt_set_aw) -> true;
110+
is_type(antidote_crdt_set_rw) -> true;
111+
is_type(antidote_crdt_register_lww) -> true;
112+
is_type(antidote_crdt_register_mv) -> true;
113+
is_type(antidote_crdt_map_go) -> true;
114+
is_type(antidote_crdt_map_rr) -> true;
115+
is_type(_) -> false.
116+
117+
118+
% Returns the initial CRDT state for the given Type
119+
-spec new(typ()) -> crdt().
120+
new(Type) ->
121+
true = is_type(Type),
122+
Type:new().
123+
124+
% Reads the value from a CRDT state
125+
-spec value(typ(), crdt()) -> any().
126+
value(Type, State) ->
127+
true = is_type(Type),
128+
Type:value(State).
69129

70-
-callback equal(crdt(), crdt()) -> boolean().
71-
-callback to_binary(crdt()) -> binary().
72-
-callback from_binary(binary()) -> {ok, crdt()} | {error, reason()}.
130+
% Computes the downstream effect for a given update operation and current state.
131+
% This has to be called once at the source replica.
132+
% The effect must then be applied on all replicas using the update function.
133+
% For some update operation it is not necessary to provide the current state
134+
% and the atom 'ignore' can be passed instead (see function require_state_downstream).
135+
-spec downstream(typ(), update(), crdt() | ignore) -> {ok, effect()} | {error, reason()}.
136+
downstream(Type, Update, State) ->
137+
true = is_type(Type),
138+
true = Type:is_operation(Update),
139+
Type:downstream(Update, State).
73140

74-
%% Following callbacks taken from riak_dt
75-
%% Not sure if it is useful for antidote
76-
%-callback stats(crdt()) -> [{atom(), number()}].
77-
%-callback stat(atom(), crdt()) -> number() | undefined.
141+
% Updates the state of a CRDT by applying a downstream effect calculated
142+
% using the downstream function.
143+
% For most types the update function must be called in causal order:
144+
% if Eff2 was calculated on a state where Eff1 was already replied,
145+
% then Eff1 has to be applied before Eff2 on all replicas.
146+
-spec update(typ(), effect(), crdt()) -> {ok, crdt()}.
147+
update(Type, Effect, State) ->
148+
true = is_type(Type),
149+
Type:update(Effect, State).
78150

79-
is_type(Type) ->
80-
is_atom(Type) andalso lists:member(Type, ?CRDTS).
151+
% Checks whether the current state is required by the downstream function
152+
% for a specific type and update operation
153+
-spec require_state_downstream(typ(), update()) -> boolean().
154+
require_state_downstream(Type, Update) ->
155+
true = is_type(Type),
156+
Type:require_state_downstream(Update).
81157

82-
%% End of Module.
158+
% Checks whether the given update operation is valid for the given type
159+
-spec is_operation(typ(), update()) -> boolean().
160+
is_operation(Type, Update) ->
161+
true = is_type(Type),
162+
Type:is_operation(Update).

0 commit comments

Comments
 (0)