diff --git a/include/antidote_crdt.hrl b/include/antidote_crdt.hrl index 07f300b..1a484ec 100644 --- a/include/antidote_crdt.hrl +++ b/include/antidote_crdt.hrl @@ -4,19 +4,8 @@ -type value() :: term(). -type reason() :: term(). --type pncounter() :: integer(). --type pncounter_update() :: {increment, integer()} | - {decrement, integer()}. --type pncounter_effect() :: integer(). --type pncounter_value() :: integer(). - - -export_type([ crdt/0, update/0, effect/0, - value/0, - pncounter/0, - pncounter_update/0, - pncounter_effect/0, - pncounter_value/0 + value/0 ]). diff --git a/src/antidote_crdt.erl b/src/antidote_crdt.erl index 848487c..dc4555c 100644 --- a/src/antidote_crdt.erl +++ b/src/antidote_crdt.erl @@ -19,29 +19,43 @@ %% ------------------------------------------------------------------- %% antidote_crdt.erl : behaviour for op-based CRDTs +%% Naming pattern of antidote crdts: _ +%% if there is only one kind of semantics implemented for a certain type +%% only the type is used in the name e.g. rga +%% counter_pn: PN-Counter aka Posistive Negative Counter +%% counter_b: Bounded Counter +%% counter_fat: Fat Counter +%% integer: Integer (Experimental) +%% flag_ew: Enable Wins Flag aka EW-Flag +%% flag_dw: Disable Wins Flag DW-Flag +%% set_go: Grow Only Set aka G-Set +%% set_aw: Add Wins Set aka AW-Set, previously OR-Set (Observed Remove Set) +%% set_rw: Remove Wins Set aka RW-Set +%% register_lww: Last Writer Wins Register aka LWW-Reg +%% register_mv: MultiValue Register aka MV-Reg +%% map_go: Grow Only Map aka G-Map +%% map_aw: Add Wins Map aka AW-Map (Experimental) +%% map_rr: Recursive Resets Map akak RR-Map +%% rga: Replicated Growable Array (Experimental) + -module(antidote_crdt). -include("antidote_crdt.hrl"). --define(CRDTS, [antidote_crdt_counter, - antidote_crdt_orset, - antidote_crdt_gset, - antidote_crdt_rga, - antidote_crdt_bcounter, - antidote_crdt_mvreg, - antidote_crdt_map, - antidote_crdt_lwwreg, - antidote_crdt_gmap, - antidote_crdt_set_rw, - antidote_crdt_integer, - antidote_crdt_map_aw, - antidote_crdt_map_rr, - antidote_crdt_fat_counter, +-define(CRDTS, [antidote_crdt_counter_pn, + antidote_crdt_counter_b, + antidote_crdt_counter_fat, antidote_crdt_flag_ew, - antidote_crdt_flag_dw - ]). + antidote_crdt_flag_dw, + antidote_crdt_set_go, + antidote_crdt_set_aw, + antidote_crdt_set_rw, + antidote_crdt_register_lww, + antidote_crdt_register_mv, + antidote_crdt_map_go, + antidote_crdt_map_rr]). -export([is_type/1 ]). diff --git a/src/antidote_crdt_bcounter.erl b/src/antidote_crdt_counter_b.erl similarity index 80% rename from src/antidote_crdt_bcounter.erl rename to src/antidote_crdt_counter_b.erl index 7be58e0..4c70356 100644 --- a/src/antidote_crdt_bcounter.erl +++ b/src/antidote_crdt_counter_b.erl @@ -1,7 +1,7 @@ %% -*- coding: utf-8 -*- %% -------------------------------------------------------------------------- %% -%% crdt_bcounter: A convergent, replicated, operation based bounded counter. +%% antidote_crdt_counter_b: A convergent, replicated, operation-based bounded counter. %% %% -------------------------------------------------------------------------- @@ -12,7 +12,7 @@ %% All operations on this CRDT are monotonic and do not keep extra tombstones. %% @end --module(antidote_crdt_bcounter). +-module(antidote_crdt_counter_b). -behaviour(antidote_crdt). @@ -40,23 +40,23 @@ -include_lib("eunit/include/eunit.hrl"). -endif. --export_type([bcounter/0, binary_bcounter/0, bcounter_op/0, id/0]). +-export_type([antidote_crdt_counter_b/0, binary_antidote_crdt_counter_b/0, antidote_crdt_counter_b_op/0, id/0]). --opaque bcounter() :: {orddict:orddict(), orddict:orddict()}. --type binary_bcounter() :: binary(). --type bcounter_op() :: bcounter_anon_op() | bcounter_src_op(). --type bcounter_anon_op() :: {transfer, {pos_integer(), id(), id()}} | +-opaque antidote_crdt_counter_b() :: {orddict:orddict(), orddict:orddict()}. +-type binary_antidote_crdt_counter_b() :: binary(). +-type antidote_crdt_counter_b_op() :: antidote_crdt_counter_b_anon_op() | antidote_crdt_counter_b_src_op(). +-type antidote_crdt_counter_b_anon_op() :: {transfer, {pos_integer(), id(), id()}} | {increment, {pos_integer(), id()}} | {decrement, {pos_integer(), id()}}. --type bcounter_src_op() :: {bcounter_anon_op(), id()}. +-type antidote_crdt_counter_b_src_op() :: {antidote_crdt_counter_b_anon_op(), id()}. -opaque id() :: term. %% A replica's identifier. -%% @doc Return a new, empty `bcounter()'. --spec new() -> bcounter(). +%% @doc Return a new, empty `antidote_crdt_counter_b()'. +-spec new() -> antidote_crdt_counter_b(). new() -> {orddict:new(), orddict:new()}. -%% @doc Return the available permissions of replica `Id' in a `bcounter()'. --spec localPermissions(id(), bcounter()) -> non_neg_integer(). +%% @doc Return the available permissions of replica `Id' in a `antidote_crdt_counter_b()'. +-spec localPermissions(id(), antidote_crdt_counter_b()) -> non_neg_integer(). localPermissions(Id, {P, D}) -> Received = lists:foldl( fun( @@ -88,8 +88,8 @@ localPermissions(Id, {P, D}) -> Received - Granted end. -%% @doc Return the total available permissions in a `bcounter()'. --spec permissions(bcounter()) -> non_neg_integer(). +%% @doc Return the total available permissions in a `antidote_crdt_counter_b()'. +-spec permissions(antidote_crdt_counter_b()) -> non_neg_integer(). permissions({P, D}) -> TotalIncrements = orddict:fold( fun @@ -105,8 +105,8 @@ permissions({P, D}) -> end, 0, D), TotalIncrements - TotalDecrements. -%% @doc Return the read value of a given `bcounter()', itself. --spec value(bcounter()) -> bcounter(). +%% @doc Return the read value of a given `antidote_crdt_counter_b()', itself. +-spec value(antidote_crdt_counter_b()) -> antidote_crdt_counter_b(). value(Counter) -> Counter. %% @doc Generate a downstream operation. @@ -114,13 +114,13 @@ value(Counter) -> Counter. %% which specify the operation and amount, or `{transfer, pos_integer(), id()}' %% that additionally specifies the target replica. %% The second parameter is an `actor()' who identifies the source replica, -%% and the third parameter is a `bcounter()' which holds the current snapshot. +%% and the third parameter is a `antidote_crdt_counter_b()' which holds the current snapshot. %% %% Return a tuple containing the operation and source replica. %% This operation fails and returns `{error, no_permissions}' %% if it tries to consume resources unavailable to the source replica %% (which prevents logging of forbidden attempts). --spec downstream(bcounter_op(), bcounter()) -> {ok, term()} | {error, no_permissions}. +-spec downstream(antidote_crdt_counter_b_op(), antidote_crdt_counter_b()) -> {ok, term()} | {error, no_permissions}. downstream({increment, {V, Actor}}, _Counter) when is_integer(V), V > 0 -> {ok, {{increment, V}, Actor}}; downstream({decrement, {V, Actor}}, Counter) when is_integer(V), V > 0 -> @@ -134,11 +134,11 @@ generate_downstream_check(Op, Actor, Counter, V) -> Available < V -> {error, no_permissions} end. -%% @doc Update a `bcounter()' with a downstream operation, +%% @doc Update a `antidote_crdt_counter_b()' with a downstream operation, %% usually created with `generate_downstream'. %% -%% Return the resulting `bcounter()' after applying the operation. --spec update(term(), bcounter()) -> {ok, bcounter()}. +%% Return the resulting `antidote_crdt_counter_b()' after applying the operation. +-spec update(term(), antidote_crdt_counter_b()) -> {ok, antidote_crdt_counter_b()}. update({{increment, V}, Id}, Counter) -> increment(Id, V, Counter); update({{decrement, V}, Id}, Counter) -> @@ -158,12 +158,12 @@ decrement(Id, V, {P, D}) -> transfer(From, To, V, {P, D}) -> {ok, {orddict:update_counter({From, To}, V, P), D}}. -%% doc Return the binary representation of a `bcounter()'. --spec to_binary(bcounter()) -> binary(). +%% doc Return the binary representation of a `antidote_crdt_counter_b()'. +-spec to_binary(antidote_crdt_counter_b()) -> binary(). to_binary(C) -> term_to_binary(C). -%% doc Return a `bcounter()' from its binary representation. --spec from_binary(binary()) -> {ok, bcounter()}. +%% doc Return a `antidote_crdt_counter_b()' from its binary representation. +-spec from_binary(binary()) -> {ok, antidote_crdt_counter_b()}. from_binary(<>) -> {ok, binary_to_term(B)}. %% @doc The following operation verifies @@ -199,7 +199,7 @@ apply_op(Op, Counter) -> {ok, NewCounter} = update(OP_DS, Counter), NewCounter. -%% Tests creating a new `bcounter()'. +%% Tests creating a new `antidote_crdt_counter_b()'. new_test() -> ?assertEqual({[], []}, new()). @@ -271,22 +271,22 @@ transfer_test() -> %% Tests the function `value()'. value_test() -> - %% Test on `bcounter()' resulting from applying all kinds of operation. + %% Test on `antidote_crdt_counter_b()' resulting from applying all kinds of operation. Counter0 = new(), Counter1 = apply_op({increment, {10, r1}}, Counter0), Counter2 = apply_op({decrement, {6, r1}}, Counter1), Counter3 = apply_op({transfer, {2, r2, r1}}, Counter2), - %% Assert `value()' returns `bcounter()' itself. + %% Assert `value()' returns `antidote_crdt_counter_b()' itself. ?assertEqual(Counter3, value(Counter3)). %% Tests serialization functions `to_binary()' and `from_binary()'. binary_test() -> - %% Test on `bcounter()' resulting from applying all kinds of operation. + %% Test on `antidote_crdt_counter_b()' resulting from applying all kinds of operation. Counter0 = new(), Counter1 = apply_op({increment, {10, r1}}, Counter0), Counter2 = apply_op({decrement, {6, r1}}, Counter1), Counter3 = apply_op({transfer, {2, r2, r1}}, Counter2), - %% Assert marshaling and unmarshaling holds the same `bcounter()'. + %% Assert marshaling and unmarshaling holds the same `antidote_crdt_counter_b()'. B = to_binary(Counter3), ?assertEqual({ok, Counter3}, from_binary(B)). diff --git a/src/antidote_crdt_fat_counter.erl b/src/antidote_crdt_counter_fat.erl similarity index 97% rename from src/antidote_crdt_fat_counter.erl rename to src/antidote_crdt_counter_fat.erl index 8fd1ec7..9bf7738 100644 --- a/src/antidote_crdt_fat_counter.erl +++ b/src/antidote_crdt_counter_fat.erl @@ -18,7 +18,7 @@ %% %% ------------------------------------------------------------------- -%% antidote_crdt_fat_counter: A convergent, replicated, operation based Fat Counter +%% antidote_crdt_counter_fat: A convergent, replicated, operation based Fat Counter %% The state of this fat counter is list of pairs where each pair is an integer %% and a related token. %% Basically when the counter recieves {increment, N} or {decrement, N} it generates @@ -26,8 +26,9 @@ %% On update, all seen tokens are removed and the new pair is then added to the state. %% This token keeps growing ("Fat" Counter) but it useful as it allows the reset %% functionaility, On reset(), all seen tokens are removed. +%% link to paper: http://haslab.uminho.pt/cbm/files/a3-younes.pdf --module(antidote_crdt_fat_counter). +-module(antidote_crdt_counter_fat). -behaviour(antidote_crdt). diff --git a/src/antidote_crdt_counter.erl b/src/antidote_crdt_counter_pn.erl similarity index 89% rename from src/antidote_crdt_counter.erl rename to src/antidote_crdt_counter_pn.erl index 38c3536..703b412 100644 --- a/src/antidote_crdt_counter.erl +++ b/src/antidote_crdt_counter_pn.erl @@ -18,15 +18,13 @@ %% %% ------------------------------------------------------------------- -%% antidote_crdt_pncounter: A convergent, replicated, operation +%% antidote_crdt_counter_pn: A convergent, replicated, operation %% based PN-Counter --module(antidote_crdt_counter). +-module(antidote_crdt_counter_pn). -behaviour(antidote_crdt). --include("antidote_crdt.hrl"). - -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. @@ -43,19 +41,25 @@ require_state_downstream/1 ]). -%% @doc Create a new, empty 'pncounter()' + +-type state() :: integer(). +-type op() :: {increment, integer()} | + {decrement, integer()}. +-type effect() :: integer(). + +%% @doc Create a new, empty 'antidote_crdt_counter_pn' new() -> 0. -%% @doc Create 'pncounter()' with initial value --spec new(integer()) -> pncounter(). +%% @doc Create 'antidote_crdt_counter_pn' with initial value +-spec new(integer()) -> state(). new(Value) when is_integer(Value) -> Value; new(_) -> new(). %% @doc The single, total value of a `pncounter()' --spec value(pncounter()) -> integer(). +-spec value(state()) -> integer(). value(PNCnt) when is_integer(PNCnt) -> PNCnt. @@ -63,7 +67,7 @@ value(PNCnt) when is_integer(PNCnt) -> %% The first parameter is either `increment' or `decrement' or the two tuples %% `{increment, pos_integer()}' or `{decrement, pos_integer()}'. The second parameter %% is the pncounter (this parameter is not actually used). --spec downstream(pncounter_update(), pncounter()) -> {ok, pncounter_effect()}. +-spec downstream(op(), state()) -> {ok, effect()}. downstream(increment, _PNCnt) -> {ok, 1}; downstream(decrement, _PNCnt) -> @@ -81,17 +85,17 @@ downstream({decrement, By}, _PNCnt) when is_integer(By) -> %% The 2nd argument is the `pncounter()' to update. %% %% returns the updated `pncounter()' --spec update(pncounter_effect(), pncounter()) -> {ok, pncounter()}. +-spec update(effect(), state()) -> {ok, state()}. update(N, PNCnt) -> {ok, PNCnt + N}. %% @doc Compare if two `pncounter()' are equal. Only returns `true()' if both %% of their positive and negative entries are equal. --spec equal(pncounter(), pncounter()) -> boolean(). +-spec equal(state(), state()) -> boolean(). equal(PNCnt1, PNCnt2) -> PNCnt1 =:= PNCnt2. --spec to_binary(pncounter()) -> binary(). +-spec to_binary(state()) -> binary(). to_binary(PNCounter) -> term_to_binary(PNCounter). diff --git a/src/antidote_crdt_flag_dw.erl b/src/antidote_crdt_flag_dw.erl index 0270052..4fdaf4a 100644 --- a/src/antidote_crdt_flag_dw.erl +++ b/src/antidote_crdt_flag_dw.erl @@ -47,10 +47,10 @@ -define(V1_VERS, 1). -export_type([flag_dw/0]). --opaque flag_dw() :: {antidote_crdt_flag:tokens(), antidote_crdt_flag:tokens()}. +-opaque flag_dw() :: {antidote_crdt_flag_helper:tokens(), antidote_crdt_flag_helper:tokens()}. %% SeenTokens, NewEnableTokens, NewDisableTokens --type downstream_op() :: {antidote_crdt_flag:tokens(), antidote_crdt_flag:tokens(), antidote_crdt_flag:tokens()}. +-type downstream_op() :: {antidote_crdt_flag_helper:tokens(), antidote_crdt_flag_helper:tokens(), antidote_crdt_flag_helper:tokens()}. -spec new() -> flag_dw(). new() -> @@ -60,11 +60,11 @@ new() -> value({EnableTokens, DisableTokens}) -> DisableTokens == [] andalso EnableTokens =/= []. --spec downstream(antidote_crdt_flag:op(), flag_dw()) -> {ok, downstream_op()}. +-spec downstream(antidote_crdt_flag_helper:op(), flag_dw()) -> {ok, downstream_op()}. downstream({disable, {}}, {EnableTokens, DisableTokens}) -> - {ok, {EnableTokens ++ DisableTokens, [], [antidote_crdt_flag:unique()]}}; + {ok, {EnableTokens ++ DisableTokens, [], [antidote_crdt_flag_helper:unique()]}}; downstream({enable, {}}, {EnableTokens, DisableTokens}) -> - {ok, {EnableTokens ++ DisableTokens, [antidote_crdt_flag:unique()], []}}; + {ok, {EnableTokens ++ DisableTokens, [antidote_crdt_flag_helper:unique()], []}}; downstream({reset, {}}, {EnableTokens, DisableTokens}) -> {ok, {EnableTokens ++ DisableTokens, [], []}}. @@ -78,7 +78,7 @@ downstream({reset, {}}, {EnableTokens, DisableTokens}) -> equal(Flag1, Flag2) -> Flag1 == Flag2. --spec to_binary(flag_dw()) -> antidote_crdt_flag:binary_flag(). +-spec to_binary(flag_dw()) -> antidote_crdt_flag_helper:binary_flag(). to_binary(Flag) -> %% @TODO something smarter <>. @@ -87,12 +87,12 @@ from_binary(<>) -> %% @TODO something smarter {ok, binary_to_term(Bin)}. -is_operation(A) -> antidote_crdt_flag:is_operation(A). +is_operation(A) -> antidote_crdt_flag_helper:is_operation(A). is_bottom(Flag) -> Flag == new(). -require_state_downstream(A) -> antidote_crdt_flag:require_state_downstream(A). +require_state_downstream(A) -> antidote_crdt_flag_helper:require_state_downstream(A). %% =================================================================== diff --git a/src/antidote_crdt_flag_ew.erl b/src/antidote_crdt_flag_ew.erl index 55a085a..c248bd0 100644 --- a/src/antidote_crdt_flag_ew.erl +++ b/src/antidote_crdt_flag_ew.erl @@ -47,10 +47,10 @@ -define(V1_VERS, 1). -export_type([flag_ew/0]). --opaque flag_ew() :: antidote_crdt_flag:flag(). +-opaque flag_ew() :: antidote_crdt_flag_helper:flag(). %% SeenTokens, NewTokens --type downstream_op() :: {antidote_crdt_flag:tokens(), antidote_crdt_flag:tokens()}. +-type downstream_op() :: {antidote_crdt_flag_helper:tokens(), antidote_crdt_flag_helper:tokens()}. -spec new() -> flag_ew(). new() -> @@ -60,11 +60,11 @@ new() -> value(EnableTokens) -> EnableTokens =/= []. --spec downstream(antidote_crdt_flag:op(), flag_ew()) -> {ok, downstream_op()}. +-spec downstream(antidote_crdt_flag_helper:op(), flag_ew()) -> {ok, downstream_op()}. downstream({disable, {}}, Tokens) -> {ok, {Tokens, []}}; downstream({enable, {}}, Tokens) -> - {ok, {Tokens, [antidote_crdt_flag:unique()]}}; + {ok, {Tokens, [antidote_crdt_flag_helper:unique()]}}; downstream({reset, {}}, Tokens) -> {ok, {Tokens, []}}. @@ -77,7 +77,7 @@ downstream({reset, {}}, Tokens) -> equal(Flag1, Flag2) -> Flag1 == Flag2. % Everything inside is ordered, so this should work --spec to_binary(flag_ew()) -> antidote_crdt_flag:binary_flag(). +-spec to_binary(flag_ew()) -> antidote_crdt_flag_helper:binary_flag(). to_binary(Flag) -> %% @TODO something smarter <>. @@ -86,12 +86,12 @@ from_binary(<>) -> %% @TODO something smarter {ok, binary_to_term(Bin)}. -is_operation(A) -> antidote_crdt_flag:is_operation(A). +is_operation(A) -> antidote_crdt_flag_helper:is_operation(A). is_bottom(Flag) -> Flag == new(). -require_state_downstream(A) -> antidote_crdt_flag:require_state_downstream(A). +require_state_downstream(A) -> antidote_crdt_flag_helper:require_state_downstream(A). %% =================================================================== %% EUnit tests diff --git a/src/antidote_crdt_flag.erl b/src/antidote_crdt_flag_helper.erl similarity index 94% rename from src/antidote_crdt_flag.erl rename to src/antidote_crdt_flag_helper.erl index 85f122f..a89c44e 100644 --- a/src/antidote_crdt_flag.erl +++ b/src/antidote_crdt_flag_helper.erl @@ -19,10 +19,10 @@ %% ------------------------------------------------------------------- %% @doc -%% A wrapper for operation-based flags, enable wins flag and disable wins flag. +%% A helper for operation-based flags, enable wins flag and disable wins flag. %% @end --module(antidote_crdt_flag). +-module(antidote_crdt_flag_helper). %% Callbacks diff --git a/src/antidote_crdt_integer.erl b/src/antidote_crdt_integer.erl deleted file mode 100644 index 300fb72..0000000 --- a/src/antidote_crdt_integer.erl +++ /dev/null @@ -1,136 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - -%% antidote_crdt_integer: A convergent, replicated, operation based integer -%% -%% This is an extension of the counter: -%% Besides the increment operation, it also provides a set operation -%% -%% Semantics: -%% The value of the counter is determines as follows: -%% 1) as base-values take the value of the latest (concurrent) set-operations (or 0 if there are none) -%% 2) for each base-value: add the increments which are concurrent to or after the respective set and add them to the base value -%% 3) take the maximum of these values (note that this is not always the maximum base-value) -%% -%% Implementation: -%% A mix of counter and mvreg - --module(antidote_crdt_integer). - --behaviour(antidote_crdt). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - --export([ new/0, - value/1, - downstream/2, - update/2, - equal/2, - to_binary/1, - from_binary/1, - is_operation/1, - require_state_downstream/1 - ]). - --type state() :: {[{term(), integer()}], integer()}. --type update() :: - {increment, integer()} - | {set, integer()} - | {reset, {}}. --type effect() :: - {increment, integer()} - | {set, term(), [term()], integer()}. --type value() :: integer(). - - --spec new() -> state(). -new() -> - {[{initial, 0}], 0}. - --spec value(state()) -> value(). -value({Vals, Delta}) -> - lists:max([X || {_, X} <- Vals]) + Delta. - --spec downstream(update(), state()) -> {ok, effect()}. -downstream({increment, N}, _State) -> - {ok, {increment, N}}; -downstream({set, N}, {Vals, Delta}) -> - Overridden = [Tag || {Tag, _} <- Vals], - {ok, {set, unique(), Overridden, N-Delta}}; -downstream({reset, {}}, State) -> - % resetting means setting value to 0 - downstream({set, 0}, State). - -unique() -> - crypto:strong_rand_bytes(20). - -%% @doc Update a `pncounter()'. The first argument is either the atom -%% `increment' or `decrement' or the two tuples `{increment, pos_integer()}' or -%% `{decrement, pos_integer()}'. -%% In the case of the former, the operation's amount -%% is `1'. Otherwise it is the value provided in the tuple's second element. -%% The 2nd argument is the `pncounter()' to update. -%% -%% returns the updated `pncounter()' --spec update(effect(), state()) -> {ok, state()}. -update({set, Token, Overridden, N}, {Vals, Delta}) -> - Surviving = [{T, V} || {T, V} <- Vals, not lists:member(T, Overridden)], - {ok, {[{Token, N}|Surviving], Delta}}; -update({increment, N}, {Vals, Delta}) -> - {ok, {Vals, Delta + N}}. - -%% @doc Compare if two `pncounter()' are equal. Only returns `true()' if both -%% of their positive and negative entries are equal. --spec equal(state(), state()) -> boolean(). -equal(PNCnt1, PNCnt2) -> - PNCnt1 =:= PNCnt2. - --spec to_binary(state()) -> binary(). -to_binary(PNCounter) -> - term_to_binary(PNCounter). - -from_binary(Bin) -> - %% @TODO something smarter - {ok, binary_to_term(Bin)}. - -%% @doc The following operation verifies -%% that Operation is supported by this particular CRDT. --spec is_operation(term()) -> boolean(). -is_operation({increment, N}) when is_integer(N) -> true; -is_operation({set, N}) when is_integer(N) -> true; -is_operation({reset, {}}) -> true; -is_operation(_) -> false. - -%% @doc Returns true if ?MODULE:downstream/2 needs the state of crdt -%% to generate downstream effect -require_state_downstream({set, _}) -> true; -require_state_downstream({reset, {}}) -> true; -require_state_downstream(_) -> false. - - - -%% =================================================================== -%% EUnit tests -%% =================================================================== --ifdef(TEST). - --endif. diff --git a/src/antidote_crdt_map.erl b/src/antidote_crdt_map.erl deleted file mode 100644 index 1992f74..0000000 --- a/src/antidote_crdt_map.erl +++ /dev/null @@ -1,95 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - -%% @doc module antidote_crdt_gset - A wrapper around riak_dt_gset - --module(antidote_crdt_map). - --behaviour(antidote_crdt). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - --define(RIAK_MODULE, riak_dt_map). - --export([ new/0, - value/1, - downstream/2, - update/2, - equal/2, - to_binary/1, - from_binary/1, - is_operation/1, - require_state_downstream/1 - ]). - - --type map_op() :: {update, {[map_field_update() | map_field_op()], actorordot()}}. --type actorordot() :: riak_dt:actor() | riak_dt:dot(). - --type map_field_op() :: {remove, field()}. --type map_field_update() :: {update, field(), crdt_op()}. --type crdt_op() :: term(). %% Valid riak_dt udpates --type field() :: term(). %% riak_dt_map:field(). - -new() -> - ?RIAK_MODULE:new(). - -value(Map) -> - ?RIAK_MODULE:value(Map). - --spec downstream(map_op(), riak_dt:riak_dt_map()) -> {ok, term()}. -downstream({Op, {OpParam, Actor}}, State) -> - {ok, S0} = ?RIAK_MODULE:update({Op, OpParam}, Actor, State), - {ok, {merge, S0}}. - -update({merge, State1}, State2) -> - {ok, ?RIAK_MODULE:merge(State1, State2)}. - -require_state_downstream(_Operation) -> true. - -is_operation({Op, {Param, _Actor}}) -> - ?RIAK_MODULE:is_operation({Op, Param}). - -equal(CRDT1, CRDT2) -> - ?RIAK_MODULE:equal(CRDT1, CRDT2). - -to_binary(CRDT) -> - ?RIAK_MODULE:to_binary(CRDT). - -from_binary(Bin) -> - ?RIAK_MODULE:from_binary(Bin). - --ifdef(TEST). -all_test() -> - S0 = new(), - Field = {'C', riak_dt_pncounter}, - {ok, Downstream} = downstream({update, {[{update, Field, {increment, 1}}], actor}}, S0), - {ok, S1} = update(Downstream, S0), - ?assertEqual([{Field, 1}], value(S1)). - -type_check_test() -> - Res = is_operation({update, {[{update, {key, riak_dt_lwwreg}, {assign, <<"A">>}}, - {update, {val, riak_dt_pncounter}, {increment, 1}}], - hardcodedactor}}), - ?assertEqual(true, Res). - --endif. diff --git a/src/antidote_crdt_map_aw.erl b/src/antidote_crdt_map_aw.erl deleted file mode 100644 index 9207a39..0000000 --- a/src/antidote_crdt_map_aw.erl +++ /dev/null @@ -1,242 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - -%% @doc module antidote_crdt_map - A add-wins map -%% -%% This map forwards all operations to the embedded CRDTs. -%% Deleting a key tries to reset the entry to its initial state -%% -%% An element exists in a map, if there is at least one update on the key, which is not followed by a remove -%% -%% Resetting the map means removing all the current entries -%% -%% Implementation note: -%% The implementation of Add-wins semantic is the same as in orset: -%% Each element has a set of add-tokens. -%% An entry is in the map, if the set of add-tokens is not empty. -%% When an entry is removed, the value is still kept in the state, so that -%% concurrent updates can be reconciled. -%% This could be optimized for certain types - --module(antidote_crdt_map_aw). - --behaviour(antidote_crdt). - -%% API --export([new/0, value/1, update/2, equal/2, - to_binary/1, from_binary/1, is_operation/1, downstream/2, require_state_downstream/1]). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - - --type typedKey() :: {Key::term(), Type::atom()}. --type token() :: term(). --type state() :: dict:dict(typedKey(), {[token()], NestedState::term()}). --type op() :: - {update, nested_op()} - | {update, [nested_op()]} - | {remove, typedKey()} - | {remove, [typedKey()]} - | {batch, {Updates::[nested_op()], Removes::[typedKey()]}} - | {reset, {}}. --type nested_op() :: {typedKey(), Op::term()}. --type effect() :: - {Adds::[nested_downstream()], Removed::[nested_downstream()], AddedToken::token()}. --type nested_downstream() :: {typedKey(), none | {ok, Effect::term()}, RemovedTokens::[token()]}. - --spec new() -> state(). -new() -> - dict:new(). - --spec value(state()) -> [{typedKey(), Value::term()}]. -value(Map) -> - lists:sort([{{Key, Type}, Type:value(Value)} || {{Key, Type}, {Tokens, Value}} <- dict:to_list(Map), Tokens =/= []]). - --spec require_state_downstream(op()) -> boolean(). -require_state_downstream(_Op) -> - true. - --spec downstream(op(), state()) -> {ok, effect()}. -downstream({update, {{Key, Type}, Op}}, CurrentMap) -> - downstream({update, [{{Key, Type}, Op}]}, CurrentMap); -downstream({update, NestedOps}, CurrentMap) -> - downstream({batch, {NestedOps, []}}, CurrentMap); -downstream({remove, {Key, Type}}, CurrentMap) -> - downstream({remove, [{Key, Type}]}, CurrentMap); -downstream({remove, Keys}, CurrentMap) -> - downstream({batch, {[], Keys}}, CurrentMap); -downstream({batch, {Updates, Removes}}, CurrentMap) -> - UpdateEffects = [generate_downstream_update(Op, CurrentMap) || Op <- Updates], - RemoveEffects = [generate_downstream_remove(Key, CurrentMap) || Key <- Removes], - Token = - case UpdateEffects of - [] -> <<>>; % no token required - _ -> unique() - end, - {ok, {UpdateEffects, RemoveEffects, Token}}; -downstream({reset, {}}, CurrentMap) -> - % reset removes all keys - AllKeys = [Key || {Key, _Val} <- value(CurrentMap)], - downstream({remove, AllKeys}, CurrentMap). - - --spec generate_downstream_update({typedKey(), Op::term()}, state()) -> nested_downstream(). -generate_downstream_update({{Key, Type}, Op}, CurrentMap) -> - {CurrentTokens, CurrentState} = - case dict:is_key({Key, Type}, CurrentMap) of - true -> dict:fetch({Key, Type}, CurrentMap); - false -> {[], Type:new()} - end, - {ok, DownstreamEffect} = Type:downstream(Op, CurrentState), - {{Key, Type}, {ok, DownstreamEffect}, CurrentTokens}. - - --spec generate_downstream_remove(typedKey(), state()) -> nested_downstream(). -generate_downstream_remove({Key, Type}, CurrentMap) -> - {CurrentTokens, CurrentState} = - case dict:is_key({Key, Type}, CurrentMap) of - true -> dict:fetch({Key, Type}, CurrentMap); - false -> {[], Type:new()} - end, - DownstreamEffect = - case Type:is_operation({reset, {}}) of - true -> - {ok, _} = Type:downstream({reset, {}}, CurrentState); - false -> - none - end, - {{Key, Type}, DownstreamEffect, CurrentTokens}. - - -unique() -> - crypto:strong_rand_bytes(20). - --spec update(effect(), state()) -> {ok, state()}. -update({Updates, Removes, AddedToken}, State) -> - State2 = lists:foldl(fun(E, S) -> update(E, [AddedToken], S) end, State, Updates), - State3 = lists:foldl(fun(E, S) -> update(E, [], S) end, State2, Removes), - {ok, State3}. - -update({{Key, Type}, {ok, Op}, RemovedTokens}, NewTokens, Map) -> - case dict:find({Key, Type}, Map) of - {ok, {Tokens, State}} -> - UpdatedTokens = NewTokens ++ (Tokens -- RemovedTokens), - {ok, UpdatedState} = Type:update(Op, State), - dict:store({Key, Type}, {UpdatedTokens, UpdatedState}, Map); - error -> - NewValue = Type:new(), - {ok, NewValueUpdated} = Type:update(Op, NewValue), - dict:store({Key, Type}, {NewTokens, NewValueUpdated}, Map) - end; -update({{Key, Type}, none, RemovedTokens}, NewTokens, Map) -> - case dict:find({Key, Type}, Map) of - {ok, {Tokens, State}} -> - UpdatedTokens = NewTokens ++ (Tokens -- RemovedTokens), - dict:store({Key, Type}, {UpdatedTokens, State}, Map); - error -> - NewValue = Type:new(), - dict:store({Key, Type}, {NewTokens, NewValue}, Map) - end. - - - -equal(Map1, Map2) -> - Map1 == Map2. % TODO better implementation (recursive equals) - - --define(TAG, 101). --define(V1_VERS, 1). - -to_binary(Policy) -> - <>. - -from_binary(<>) -> - {ok, binary_to_term(Bin)}. - -is_operation(Operation) -> - case Operation of - {update, {{_Key, Type}, Op}} -> - antidote_crdt:is_type(Type) - andalso Type:is_operation(Op); - {update, Ops} when is_list(Ops) -> - distinct([Key || {Key, _} <- Ops]) - andalso lists:all(fun(Op) -> is_operation({update, Op}) end, Ops); - {remove, {_Key, Type}} -> - antidote_crdt:is_type(Type); - {remove, Keys} when is_list(Keys) -> - distinct(Keys) - andalso lists:all(fun(Key) -> is_operation({remove, Key}) end, Keys); - {batch, {Updates, Removes}} -> - is_list(Updates) - andalso is_list(Removes) - andalso distinct(Removes ++ [Key || {Key, _} <- Updates]) - andalso lists:all(fun(Key) -> is_operation({remove, Key}) end, Removes) - andalso lists:all(fun(Op) -> is_operation({update, Op}) end, Updates); - {reset, {}} -> true; - _ -> - false - end. - -distinct([]) -> true; -distinct([X|Xs]) -> - not lists:member(X, Xs) andalso distinct(Xs). - - - - -%% =================================================================== -%% EUnit tests -%% =================================================================== --ifdef(TEST). - -reset1_test() -> - Set0 = new(), - % DC1: a.incr - {ok, Incr1} = downstream({update, {{a, antidote_crdt_integer}, {increment, 1}}}, Set0), - {ok, Set1a} = update(Incr1, Set0), - % DC1 reset - {ok, Reset1} = downstream({reset, {}}, Set1a), - {ok, Set1b} = update(Reset1, Set1a), - % DC2 a.remove - {ok, Remove1} = downstream({remove, {a, antidote_crdt_integer}}, Set0), - {ok, Set2a} = update(Remove1, Set0), - % DC2 --> DC1 - {ok, Set1c} = update(Remove1, Set1b), - % DC1 reset - {ok, Reset2} = downstream({reset, {}}, Set1c), - {ok, Set1d} = update(Reset2, Set1c), - % DC1: a.incr - {ok, Incr2} = downstream({update, {{a, antidote_crdt_integer}, {increment, 0}}}, Set1d), - {ok, Set1e} = update(Incr2, Set1d), - - ?assertEqual([], value(Set2a)), - ?assertEqual([], value(Set1d)), - ?assertEqual([{{a, antidote_crdt_integer}, 1}], value(Set1e)). - - - - - --endif. - - - diff --git a/src/antidote_crdt_gmap.erl b/src/antidote_crdt_map_go.erl similarity index 72% rename from src/antidote_crdt_gmap.erl rename to src/antidote_crdt_map_go.erl index 98b5dbf..0c42a61 100644 --- a/src/antidote_crdt_gmap.erl +++ b/src/antidote_crdt_map_go.erl @@ -18,14 +18,14 @@ %% %% ------------------------------------------------------------------- -%% @doc module antidote_crdt_gmap - A grow-only map +%% @doc module antidote_crdt_map_go - A grow-only map %% %% This map simply forwards all operations to the embedded CRDTs. %% There is no real remove-operation. %% %% The reset operation, forwards the reset to all values in the map. --module(antidote_crdt_gmap). +-module(antidote_crdt_map_go). -behaviour(antidote_crdt). @@ -38,31 +38,30 @@ -endif. --type gmap() :: dict:dict({Key::term(), Type::atom()}, NestedState::term()). --type gmap_op() :: +-type antidote_crdt_map_go() :: dict:dict({Key::term(), Type::atom()}, NestedState::term()). +-type antidote_crdt_map_go_op() :: {update, nested_op()} - | {update, [nested_op()]} - | {reset, {}}. + | {update, [nested_op()]}. -type nested_op() :: {{Key::term(), Type::atom() }, Op::term()}. --type gmap_effect() :: +-type antidote_crdt_map_go_effect() :: {update, nested_downstream()} | {update, [nested_downstream()]}. -type nested_downstream() :: {{Key::term(), Type::atom() }, Op::term()}. --spec new() -> gmap(). +-spec new() -> antidote_crdt_map_go(). new() -> dict:new(). --spec value(gmap()) -> [{{Key::term(), Type::atom()}, Value::term()}]. +-spec value(antidote_crdt_map_go()) -> [{{Key::term(), Type::atom()}, Value::term()}]. value(Map) -> lists:sort([{{Key, Type}, Type:value(Value)} || {{Key, Type}, Value} <- dict:to_list(Map)]). --spec require_state_downstream(gmap_op()) -> boolean(). +-spec require_state_downstream(antidote_crdt_map_go_op()) -> boolean(). require_state_downstream(_Op) -> true. --spec downstream(gmap_op(), gmap()) -> {ok, gmap_effect()}. +-spec downstream(antidote_crdt_map_go_op(), antidote_crdt_map_go()) -> {ok, antidote_crdt_map_go_effect()}. downstream({update, {{Key, Type}, Op}}, CurrentMap) -> % TODO could be optimized for some types CurrentValue = case dict:is_key({Key, Type}, CurrentMap) of @@ -72,22 +71,9 @@ downstream({update, {{Key, Type}, Op}}, CurrentMap) -> {ok, DownstreamOp} = Type:downstream(Op, CurrentValue), {ok, {update, {{Key, Type}, DownstreamOp}}}; downstream({update, Ops}, CurrentMap) when is_list(Ops) -> - {ok, {update, lists:map(fun(Op) -> {ok, DSOp} = downstream({update, Op}, CurrentMap), DSOp end, Ops)}}; -downstream({reset, {}}, CurrentMap) -> - % calls reset on all embedded keys which support reset - Reset = - fun({{Key, Type}, State}) -> - case Type:is_operation({reset, {}}) of - true -> - {ok, EmbeddedEffect} = Type:downstream({reset, {}}, State), - [{update, {{Key, Type}, EmbeddedEffect}}]; - false -> [] - end - end, - DownstreamResets = lists:flatmap(Reset, dict:to_list(CurrentMap)), - {ok, {update, DownstreamResets}}. + {ok, {update, lists:map(fun(Op) -> {ok, DSOp} = downstream({update, Op}, CurrentMap), DSOp end, Ops)}}. --spec update(gmap_effect(), gmap()) -> {ok, gmap()}. +-spec update(antidote_crdt_map_go_effect(), antidote_crdt_map_go()) -> {ok, antidote_crdt_map_go()}. update({update, {{Key, Type}, Op}}, Map) -> case dict:is_key({Key, Type}, Map) of true -> {ok, dict:update({Key, Type}, fun(V) -> {ok, Value} = Type:update(Op, V), Value end, Map)}; @@ -129,7 +115,7 @@ is_operation(Operation) -> {update, Ops} when is_list(Ops) -> distinct([Key || {Key, _} <- Ops]) andalso lists:all(fun(Op) -> is_operation({update, Op}) end, Ops); - {reset, {}} -> true; + {reset, {}} -> false; _ -> false end. @@ -149,18 +135,16 @@ new_test() -> update_test() -> Map1 = new(), - {ok, DownstreamOp} = downstream({update, {{key1, antidote_crdt_lwwreg}, {assign, <<"test">>}}}, Map1), - ?assertMatch({update, {{key1, antidote_crdt_lwwreg}, {_TS, <<"test">>}}}, DownstreamOp), + {ok, DownstreamOp} = downstream({update, {{key1, antidote_crdt_register_lww}, {assign, <<"test">>}}}, Map1), + ?assertMatch({update, {{key1, antidote_crdt_register_lww}, {_TS, <<"test">>}}}, DownstreamOp), {ok, Map2} = update(DownstreamOp, Map1), - ?assertEqual([{{key1, antidote_crdt_lwwreg}, <<"test">>}], value(Map2)). + ?assertEqual([{{key1, antidote_crdt_register_lww}, <<"test">>}], value(Map2)). update2_test() -> Map1 = new(), - {ok, Effect1} = downstream({update, [{{a, antidote_crdt_orset}, {add, a}}]}, Map1), + {ok, Effect1} = downstream({update, [{{a, antidote_crdt_set_aw}, {add, a}}]}, Map1), {ok, Map2} = update(Effect1, Map1), - {ok, Effect2} = downstream({reset, {}}, Map2), - {ok, Map3} = update(Effect2, Map2), - ?assertEqual([{{a, antidote_crdt_orset}, []}], value(Map3)). + ?assertEqual([{{a, antidote_crdt_set_aw}, [a]}], value(Map2)). -endif. diff --git a/src/antidote_crdt_map_rr.erl b/src/antidote_crdt_map_rr.erl index b337374..4f39b41 100644 --- a/src/antidote_crdt_map_rr.erl +++ b/src/antidote_crdt_map_rr.erl @@ -241,13 +241,13 @@ is_bottom(Map) -> reset1_test() -> Map0 = new(), % DC1: a.incr - {ok, Incr1} = downstream({update, {{a, antidote_crdt_fat_counter}, {increment, 1}}}, Map0), + {ok, Incr1} = downstream({update, {{a, antidote_crdt_counter_fat}, {increment, 1}}}, Map0), {ok, Map1a} = update(Incr1, Map0), % DC1 reset {ok, Reset1} = downstream({reset, {}}, Map1a), {ok, Map1b} = update(Reset1, Map1a), % DC2 a.remove - {ok, Remove1} = downstream({remove, {a, antidote_crdt_fat_counter}}, Map0), + {ok, Remove1} = downstream({remove, {a, antidote_crdt_counter_fat}}, Map0), {ok, Map2a} = update(Remove1, Map0), % DC2 --> DC1 {ok, Map1c} = update(Remove1, Map1b), @@ -255,7 +255,7 @@ reset1_test() -> {ok, Reset2} = downstream({reset, {}}, Map1c), {ok, Map1d} = update(Reset2, Map1c), % DC1: a.incr - {ok, Incr2} = downstream({update, {{a, antidote_crdt_fat_counter}, {increment, 2}}}, Map1d), + {ok, Incr2} = downstream({update, {{a, antidote_crdt_counter_fat}, {increment, 2}}}, Map1d), {ok, Map1e} = update(Incr2, Map1d), io:format("Map0 = ~p~n", [Map0]), @@ -272,12 +272,12 @@ reset1_test() -> io:format("Map1e = ~p~n", [Map1e]), ?assertEqual([], value(Map0)), - ?assertEqual([{{a, antidote_crdt_fat_counter}, 1}], value(Map1a)), + ?assertEqual([{{a, antidote_crdt_counter_fat}, 1}], value(Map1a)), ?assertEqual([], value(Map1b)), ?assertEqual([], value(Map2a)), ?assertEqual([], value(Map1c)), ?assertEqual([], value(Map1d)), - ?assertEqual([{{a, antidote_crdt_fat_counter}, 2}], value(Map1e)). + ?assertEqual([{{a, antidote_crdt_counter_fat}, 2}], value(Map1e)). reset2_test() -> @@ -375,14 +375,14 @@ remove_test() -> ?assertEqual([], value(M1)), ?assertEqual(true, is_bottom(M1)), M2 = upd({update, [ - {{<<"a">>, antidote_crdt_orset}, {add, <<"1">>}}, - {{<<"b">>, antidote_crdt_mvreg}, {assign, <<"2">>}}, - {{<<"c">>, antidote_crdt_fat_counter}, {increment, 1}} + {{<<"a">>, antidote_crdt_set_aw}, {add, <<"1">>}}, + {{<<"b">>, antidote_crdt_register_mv}, {assign, <<"2">>}}, + {{<<"c">>, antidote_crdt_counter_fat}, {increment, 1}} ]}, M1), ?assertEqual([ - {{<<"a">>, antidote_crdt_orset}, [<<"1">>]}, - {{<<"b">>, antidote_crdt_mvreg}, [<<"2">>]}, - {{<<"c">>, antidote_crdt_fat_counter}, 1} + {{<<"a">>, antidote_crdt_set_aw}, [<<"1">>]}, + {{<<"b">>, antidote_crdt_register_mv}, [<<"2">>]}, + {{<<"c">>, antidote_crdt_counter_fat}, 1} ], value(M2)), ?assertEqual(false, is_bottom(M2)), M3 = upd({reset, {}}, M2), @@ -391,4 +391,4 @@ remove_test() -> ?assertEqual(true, is_bottom(M3)), ok. --endif. +-endif. \ No newline at end of file diff --git a/src/antidote_crdt_lwwreg.erl b/src/antidote_crdt_register_lww.erl similarity index 83% rename from src/antidote_crdt_lwwreg.erl rename to src/antidote_crdt_register_lww.erl index caa4a8b..9f8083a 100644 --- a/src/antidote_crdt_lwwreg.erl +++ b/src/antidote_crdt_register_lww.erl @@ -18,13 +18,13 @@ %% %% ------------------------------------------------------------------- -%% @doc module antidote_crdt_lwwreg - An operation based last-writer-wins register +%% @doc module antidote_crdt_register_lww - An operation based last-writer-wins register %% Each operation is assigned a timestamp, which is guaranteed to be greater than %% the current timestamp. %% The current value of the register is the value assigned with the greatest timestamp %% or the empty binary if there was no assignment yet. --module(antidote_crdt_lwwreg). +-module(antidote_crdt_register_lww). -behaviour(antidote_crdt). @@ -43,11 +43,11 @@ require_state_downstream/1 ]). --export_type([lwwreg/0, lwwreg_op/0]). +-export_type([antidote_crdt_register_lww/0, antidote_crdt_register_lww_op/0]). --opaque lwwreg() :: {non_neg_integer(), term()}. +-opaque antidote_crdt_register_lww() :: {non_neg_integer(), term()}. --type lwwreg_op() :: {assign, term(), non_neg_integer()} | {assign, term()}. +-type antidote_crdt_register_lww_op() :: {assign, term(), non_neg_integer()} | {assign, term()}. new() -> {0, <<>>}. @@ -55,7 +55,7 @@ new() -> value({_Time, Val}) -> Val. --spec downstream(lwwreg_op(), lwwreg()) -> {ok, term()}. +-spec downstream(antidote_crdt_register_lww_op(), antidote_crdt_register_lww()) -> {ok, term()}. downstream({assign, Value, Time}, {OldTime, _OldValue}) -> {ok, {max(Time, OldTime + 1), Value}}; downstream({assign, Value}, State) -> diff --git a/src/antidote_crdt_mvreg.erl b/src/antidote_crdt_register_mv.erl similarity index 77% rename from src/antidote_crdt_mvreg.erl rename to src/antidote_crdt_register_mv.erl index b7efbc0..007d5b8 100644 --- a/src/antidote_crdt_mvreg.erl +++ b/src/antidote_crdt_register_mv.erl @@ -1,6 +1,6 @@ %% ------------------------------------------------------------------- %% -%% riak_dt_mvreg: A DVVSet based multi value register +%% riak_dt_antidote_crdt_register_mv: A DVVSet based multi value register %% %% Copyright (c) 2007-2013 Basho Technologies, Inc. All Rights Reserved. %% @@ -31,7 +31,7 @@ %% %% @end --module(antidote_crdt_mvreg). +-module(antidote_crdt_register_mv). -behaviour(antidote_crdt). @@ -53,34 +53,34 @@ -include_lib("eunit/include/eunit.hrl"). -endif. --export_type([mvreg/0, mvreg_op/0]). +-export_type([antidote_crdt_register_mv/0, antidote_crdt_register_mv_op/0]). %% TODO: make opaque --type mvreg() :: [{term(), uniqueToken()}]. +-type antidote_crdt_register_mv() :: [{term(), uniqueToken()}]. -type uniqueToken() :: term(). --type mvreg_effect() :: +-type antidote_crdt_register_mv_effect() :: {Value::term(), uniqueToken(), Overridden::[uniqueToken()]} | {reset, Overridden::[uniqueToken()]}. --type mvreg_op() :: {assign, term()}. +-type antidote_crdt_register_mv_op() :: {assign, term()}. -%% @doc Create a new, empty `mvreg()' --spec new() -> mvreg(). +%% @doc Create a new, empty `antidote_crdt_register_mv()' +-spec new() -> antidote_crdt_register_mv(). new() -> []. -%% @doc The values of this `mvreg()'. Multiple values can be returned, +%% @doc The values of this `antidote_crdt_register_mv()'. Multiple values can be returned, %% since there can be diverged value in this register. --spec value(mvreg()) -> [term()]. +-spec value(antidote_crdt_register_mv()) -> [term()]. value(MVReg) -> [V || {V, _} <- MVReg]. --spec downstream(mvreg_op(), mvreg()) -> {ok, mvreg_effect()}. +-spec downstream(antidote_crdt_register_mv_op(), antidote_crdt_register_mv()) -> {ok, antidote_crdt_register_mv_effect()}. downstream({assign, Value}, MVReg) -> Token = unique(), Overridden = [Tok || {_, Tok} <- MVReg], @@ -94,7 +94,7 @@ unique() -> crypto:strong_rand_bytes(20). --spec update(mvreg_effect(), mvreg()) -> {ok, mvreg()}. +-spec update(antidote_crdt_register_mv_effect(), antidote_crdt_register_mv()) -> {ok, antidote_crdt_register_mv()}. update({Value, Token, Overridden}, MVreg) -> % remove overridden values MVreg2 = [{V, T} || {V, T} <- MVreg, not lists:member(T, Overridden)], @@ -110,19 +110,19 @@ insert_sorted(A, [X|Xs]) when A < X -> [A, X|Xs]; insert_sorted(A, [X|Xs]) -> [X|insert_sorted(A, Xs)]. --spec equal(mvreg(), mvreg()) -> boolean(). +-spec equal(antidote_crdt_register_mv(), antidote_crdt_register_mv()) -> boolean(). equal(MVReg1, MVReg2) -> MVReg1 == MVReg2. -define(TAG, 85). -define(V1_VERS, 1). --spec to_binary(mvreg()) -> binary(). +-spec to_binary(antidote_crdt_register_mv()) -> binary(). to_binary(MVReg) -> <>. -%% @doc Decode binary `mvreg()' --spec from_binary(binary()) -> {ok, mvreg()} | {error, term()}. +%% @doc Decode binary `antidote_crdt_register_mv()' +-spec from_binary(binary()) -> {ok, antidote_crdt_register_mv()} | {error, term()}. from_binary(<>) -> {ok, riak_dt:from_binary(Bin)}. diff --git a/src/antidote_crdt_rga.erl b/src/antidote_crdt_rga.erl deleted file mode 100644 index 14186f2..0000000 --- a/src/antidote_crdt_rga.erl +++ /dev/null @@ -1,316 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% Copyright (c) 2015 SyncFree Consortium. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - -%% @doc -%% -%% An operation-based Replicated Growable Array CRDT. -%% -%% As the data structure is operation-based, to issue an operation, one should -%% first call `generate_downstream/3', to get the downstream version of the -%% operation, and then call `update/2'. -%% -%% It provides two operations: addRight, which adds an element to the RGA, and remove, -%% which removes an element from the RGA. -%% -%% This implementation is based on the paper cited below. -%% -%% @reference Marc Shapiro, Nuno Preguiça, Carlos Baquero, Marek Zawirski (2011) A comprehensive study of -%% Convergent and Commutative Replicated Data Types. http://hal.upmc.fr/inria-00555588/ -%% -%% @end --module(antidote_crdt_rga). - --behaviour(antidote_crdt). - -%% Call backs --export([ new/0, - value/1, - downstream/2, - update/2, - equal/2, - to_binary/1, - from_binary/1, - is_operation/1, - require_state_downstream/1 - ]). - --export([purge_tombstones/1]). - --export_type([rga/0, rga_op/0, rga_downstream_op/0]). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - --type vertex() :: {ok | deleted, any(), number()}. - --type rga_downstream_op() :: {addRight, vertex(), vertex()} | {remove, {ok, any(), number()}}. - --type rga_op() :: {addRight, {any(), non_neg_integer()}} | {remove, non_neg_integer()}. - --type rga_result() :: {ok, rga()}. - --type rga() :: [vertex()]. - --spec new() -> []. -new() -> - []. - -%% @doc generate downstream operations. -%% If the operation is addRight, generates a unique token for the new element. -%% If the operation is remove, fetches the vertex of the element to be removed. --spec downstream(rga_op(), rga()) -> {ok, rga_downstream_op()} | {error, {invalid_position, number()}}. -downstream({addRight, {Elem, Position}}, Rga) -> - case (Position < 0) or (Position > length(Rga)) of - true -> {error, {invalid_position, Position}}; - false -> case (Rga == []) or (Position == 0) of - true -> {ok, {addRight, {ok, 0, 0}, {ok, Elem, unique()}}}; - false -> {ok, {addRight, lists:nth(Position, Rga), {ok, Elem, unique()}}} - end - end; -downstream({remove, Position}, Rga) -> - {ok, {remove, lists:nth(Position, Rga)}}. - -%% @doc given an RGA, returns the same RGA, as it represents its own value. --spec value(rga()) -> rga(). -value(Rga) -> - Rga. - -%% @doc This method takes in an rga operation and an rga to perform the operation. -%% It returns either the added Vertex and the new rga in case of an addRight, and the new rga in the case of a remove. --spec update(rga_downstream_op(), rga()) -> rga_result(). -update({addRight, RightVertex, NewVertex}, Rga) -> - recursive_insert(RightVertex, NewVertex, Rga, []); -update({remove, Vertex}, Rga) -> - remove_vertex(Vertex, Rga). - -%% Private -%% @doc recursively looks for the Vertex where the new element should be put to the right of. --spec recursive_insert(vertex(), vertex(), rga(), list()) -> rga_result(). -recursive_insert(_, NewVertex, [], []) -> - {ok, [NewVertex]}; -recursive_insert({ok, 0, 0}, NewVertex, L, []) -> - add_element(NewVertex, L, []); -recursive_insert(RightVertex, NewVertex, [RightVertex | T], L) -> - add_element(NewVertex, T, [RightVertex | L]); -recursive_insert(RightVertex, NewVertex, [H | T], L) -> - recursive_insert(RightVertex, NewVertex, T, [H | L]). - -%% Private -%% @doc the place for the insertion has been found, so now the UIDs are compared to see where to insert. --spec add_element(vertex(), rga(), list()) -> rga_result(). -add_element({Status, Value, UID}, [{Status1, Value1, UID1} | T], L) -> - case UID >= UID1 of - true -> - {ok, lists:reverse(L) ++ [{Status, Value, UID}] ++ [{Status1, Value1, UID1} | T]}; - _ -> - add_element({Status, Value, UID}, T, [{Status1, Value1, UID1} | L]) - end; -add_element(Insert, [], L) -> - {ok, lists:reverse([Insert | L])}. - -%% Private -%% @doc looks for the Vertex to be removed. Once it's found, it's marked as "deleted". -%% The Vertex is not removed from the list, to allow adding elements to its right. --spec remove_vertex(vertex(), rga()) -> rga_result(). -remove_vertex({ok, Value, UID}, Rga) -> - {ok, lists:keyreplace(UID, 3, Rga, {deleted, Value, UID})}. - -%% @doc given an rga, this mehtod looks for all tombstones and removes them, returning the tombstone free rga. --spec purge_tombstones(rga()) -> rga_result(). -purge_tombstones(Rga) -> - L = lists:filter(fun({Status, _, _}) -> Status == ok end, Rga), - {ok, L}. - -%% @doc generates a unique identifier based on the node and a unique reference. -unique() -> - {node(), make_ref()}. - -%% @doc The following operation verifies that Operation is supported by this particular CRDT. --spec is_operation(term()) -> boolean(). -is_operation({addRight, {_, Position}}) -> - (is_integer(Position) and (Position >= 0)); -is_operation({remove, Position})-> - (is_integer(Position) and (Position >= 0)); -is_operation(_) -> - false. - -require_state_downstream(_) -> - true. - -equal(Rga1, Rga2) -> - Rga1 == Rga2. - -to_binary(Rga1) -> - erlang:term_to_binary(Rga1). - -from_binary(Bin) -> - {ok, erlang:binary_to_term(Bin)}. - --ifdef(TEST). - -new_test() -> - ?assertEqual([], new()). - -generate_downstream_invalid_position_test() -> - L = new(), - Result1 = downstream({addRight, {1, 1}}, L), - ?assertMatch({error, {invalid_position, 1}}, Result1), - Result2 = downstream({addRight, {1, -1}}, L), - ?assertMatch({error, {invalid_position, -1}}, Result2). - -generate_downstream_empty_rga_test() -> - L = new(), - {ok, DownstreamOp} = downstream({addRight, {4, 0}}, L), - ?assertMatch({addRight, {ok, 0, 0}, {ok, 4, _}}, DownstreamOp). - -generate_downstream_non_empty_rga_test() -> - L = new(), - {ok, DownstreamOp} = downstream({addRight, {4, 0}}, L), - {ok, L1} = update(DownstreamOp, L), - {ok, DownstreamOp1} = downstream({addRight, {3, 1}}, L1), - ?assertMatch({addRight, {ok, 4, _}, {ok, 3, _}}, DownstreamOp1). - -add_right_in_empty_rga_test() -> - L = new(), - {ok, DownstreamOp} = downstream({addRight, {1, 0}}, L), - {ok, L1} = update(DownstreamOp, L), - ?assertMatch([{ok, 1, _}], L1). - -add_right_in_non_empty_rga_test() -> - L = new(), - {ok, DownstreamOp} = downstream({addRight, {1, 0}}, L), - {ok, L1} = update(DownstreamOp, L), - {ok, DownstreamOp1} = downstream({addRight, {2, 1}}, L1), - {ok, L2} = update(DownstreamOp1, L1), - ?assertMatch([{ok, 1, _}, {ok, 2, _}], L2). - -%% In the tests that follow, the values of generate_downstream are placed by hand ,so -%% the intended scenarios can be correctly tested, not depending on the unique() function. - -insert_in_the_middle_test() -> - L = new(), - DownstreamOp1 = {addRight, {ok, 0, 0}, {ok, 1, 1}}, - DownstreamOp2 = {addRight, {ok, 1, 1}, {ok, 3, 2}}, - DownstreamOp3 = {addRight, {ok, 1, 1}, {ok, 2, 3}}, - {ok, L1} = update(DownstreamOp1, L), - {ok, L2} = update(DownstreamOp2, L1), - {ok, L3} = update(DownstreamOp3, L2), - ?assertMatch([{ok, 1, _}, {ok, 2, _}, {ok, 3, _}], L3). - -remove_first_element_test() -> - L = new(), - DownstreamOp1 = {addRight, {ok, 0, 0}, {ok, 1, 1}}, - DownstreamOp2 = {addRight, {ok, 1, 1}, {ok, 3, 2}}, - DownstreamOp3 = {addRight, {ok, 1, 1}, {ok, 2, 3}}, - {ok, L1} = update(DownstreamOp1, L), - {ok, L2} = update(DownstreamOp2, L1), - {ok, L3} = update(DownstreamOp3, L2), - {ok, DownstreamOp4} = downstream({remove, 1}, L3), - {ok, L4} = update(DownstreamOp4, L3), - ?assertMatch([{deleted, 1, _}, {ok, 2, _}, {ok, 3, _}], L4). - -remove_middle_element_test() -> - L = new(), - DownstreamOp1 = {addRight, {ok, 0, 0}, {ok, 1, 1}}, - DownstreamOp2 = {addRight, {ok, 1, 1}, {ok, 3, 2}}, - DownstreamOp3 = {addRight, {ok, 1, 1}, {ok, 2, 3}}, - {ok, L1} = update(DownstreamOp1, L), - {ok, L2} = update(DownstreamOp2, L1), - {ok, L3} = update(DownstreamOp3, L2), - {ok, DownstreamOp4} = downstream({remove, 2}, L3), - {ok, L4} = update(DownstreamOp4, L3), - ?assertMatch([{ok, 1, _}, {deleted, 2, _}, {ok, 3, _}], L4). - -remove_last_element_test() -> - L = new(), - DownstreamOp1 = {addRight, {ok, 0, 0}, {ok, 1, 1}}, - DownstreamOp2 = {addRight, {ok, 1, 1}, {ok, 3, 2}}, - DownstreamOp3 = {addRight, {ok, 1, 1}, {ok, 2, 3}}, - {ok, L1} = update(DownstreamOp1, L), - {ok, L2} = update(DownstreamOp2, L1), - {ok, L3} = update(DownstreamOp3, L2), - {ok, DownstreamOp4} = downstream({remove, 3}, L3), - {ok, L4} = update(DownstreamOp4, L3), - ?assertMatch([{ok, 1, _}, {ok, 2, _}, {deleted, 3, _}], L4). - -insert_right_of_a_remove_test() -> - L = new(), - DownstreamOp1 = {addRight, {ok, 0, 0}, {ok, 1, 1}}, - DownstreamOp2 = {addRight, {ok, 1, 1}, {ok, 3, 2}}, - DownstreamOp3 = {addRight, {ok, 1, 1}, {ok, 2, 4}}, - {ok, L1} = update(DownstreamOp1, L), - {ok, L2} = update(DownstreamOp2, L1), - {ok, L3} = update(DownstreamOp3, L2), - {ok, DownstreamOp4} = downstream({remove, 2}, L3), - {ok, L4} = update(DownstreamOp4, L3), - DownstreamOp5 = {addRight, {deleted, 2, 4}, {ok, 4, 3}}, - {ok, L5} = update(DownstreamOp5, L4), - ?assertMatch([{ok, 1, _}, {deleted, 2, _}, {ok, 4, _}, {ok, 3, _}], L5). - -purge_tombstones_test() -> - L = new(), - DownstreamOp1 = {addRight, {ok, 0, 0}, {ok, 1, 1}}, - DownstreamOp2 = {addRight, {ok, 1, 1}, {ok, 3, 2}}, - DownstreamOp3 = {addRight, {ok, 1, 1}, {ok, 2, 3}}, - {ok, L1} = update(DownstreamOp1, L), - {ok, L2} = update(DownstreamOp2, L1), - {ok, L3} = update(DownstreamOp3, L2), - {ok, DownstreamOp4} = downstream({remove, 2}, L3), - {ok, L4} = update(DownstreamOp4, L3), - {ok, L5} = purge_tombstones(L4), - ?assertMatch([{ok, 1, _}, {ok, 3, _}], L5). - -%% This test creates two RGAs and performs updates, checking they reach -%% the same final state after the four updates are aplied in both replicas. -concurrent_updates_in_two_replicas_test() -> - R1_0 = new(), - R2_0 = new(), - {ok, DownstreamOp1} = downstream({addRight, {1, 0}}, R1_0), - {ok, DownstreamOp2} = downstream({addRight, {2, 0}}, R2_0), - {ok, R1_1} = update(DownstreamOp1, R1_0), - {ok, R2_1} = update(DownstreamOp2, R2_0), - {ok, R1_2} = update(DownstreamOp2, R1_1), - {ok, R2_2} = update(DownstreamOp1, R2_1), - ?assertEqual(R1_2, R2_2), - {ok, DownstreamOp3} = downstream({addRight, {3, 2}}, R1_2), - {ok, DownstreamOp4} = downstream({addRight, {4, 2}}, R2_2), - {ok, R1_3} = update(DownstreamOp3, R1_2), - {ok, R2_3} = update(DownstreamOp4, R2_2), - {ok, R1_4} = update(DownstreamOp4, R1_3), - {ok, R2_4} = update(DownstreamOp3, R2_3), - ?assertEqual(R1_4, R2_4). - -is_operation_test() -> - %% addRight checks - ?assertEqual(false, is_operation({addRight, {value, -1}})), - ?assertEqual(true, is_operation({addRight, {value, 0}})), - ?assertEqual(true, is_operation({addRight, {value, 1}})), - - %% remove checks - ?assertEqual(false, is_operation({remove, -1})), - ?assertEqual(true, is_operation({remove, 0})), - ?assertEqual(true, is_operation({remove, 1})), - - %% invalid operation checks - ?assertEqual(false, is_operation({anything, 1})), - ?assertEqual(false, is_operation({add, 1, 4})). - --endif. diff --git a/src/antidote_crdt_orset.erl b/src/antidote_crdt_set_aw.erl similarity index 75% rename from src/antidote_crdt_orset.erl rename to src/antidote_crdt_set_aw.erl index 69ede98..4e7b974 100644 --- a/src/antidote_crdt_orset.erl +++ b/src/antidote_crdt_set_aw.erl @@ -1,6 +1,6 @@ %% ------------------------------------------------------------------- %% -%% crdt_orset: A convergent, replicated, operation based observe remove set +%% crdt_antidote_crdt_set_aw: A convergent, replicated, operation based observe remove set %% %% Copyright (c) 2007-2012 Basho Technologies, Inc. All Rights Reserved. %% @@ -31,7 +31,7 @@ %% remove_all that removes a list of elements from the set; update, that contains %% a list of previous four commands. %% -%% This file is adapted from riak_dt_orset, a state-based implementation of +%% This file is adapted from riak_dt_antidote_crdt_set_aw, a state-based implementation of %% Observed-Remove Set. %% The changes are as follows: %% 1. `generate_downstream/3' is added, as this is necessary for op-based CRDTs. @@ -42,7 +42,7 @@ %% Convergent and Commutative Replicated Data Types. http://hal.upmc.fr/inria-00555588/ %% %% @end --module(antidote_crdt_orset). +-module(antidote_crdt_set_aw). -include("antidote_crdt.hrl"). @@ -65,12 +65,12 @@ -include_lib("eunit/include/eunit.hrl"). -endif. --export_type([orset/0, binary_orset/0, orset_op/0]). --opaque orset() :: orddict:orddict(member(), tokens()). +-export_type([antidote_crdt_set_aw/0, binary_antidote_crdt_set_aw/0, antidote_crdt_set_aw_op/0]). +-opaque antidote_crdt_set_aw() :: orddict:orddict(member(), tokens()). --type binary_orset() :: binary(). %% A binary that from_binary/1 will operate on. +-type binary_antidote_crdt_set_aw() :: binary(). %% A binary that from_binary/1 will operate on. --type orset_op() :: +-type antidote_crdt_set_aw_op() :: {add, member()} | {remove, member()} | {add_all, [member()]} @@ -88,60 +88,60 @@ -type token() :: binary(). -type tokens() :: [token()]. --spec new() -> orset(). +-spec new() -> antidote_crdt_set_aw(). new() -> orddict:new(). -%% @doc return all existing elements in the `orset()'. --spec value(orset()) -> [member()]. -value(ORSet) -> - orddict:fetch_keys(ORSet). +%% @doc return all existing elements in the `antidote_crdt_set_aw()'. +-spec value(antidote_crdt_set_aw()) -> [member()]. +value(Set_aw) -> + orddict:fetch_keys(Set_aw). %% @doc generate downstream operations. %% If the operation is add or add_all, generate unique tokens for %% each element and fetches the current supporting tokens. %% If the operation is remove or remove_all, fetches current -%% supporting tokens of these elements existing in the `orset()'. --spec downstream(orset_op(), orset()) -> {ok, downstream_op()}. -downstream({add, Elem}, ORSet) -> - downstream({add_all, [Elem]}, ORSet); -downstream({add_all, Elems}, ORSet) -> +%% supporting tokens of these elements existing in the `antidote_crdt_set_aw()'. +-spec downstream(antidote_crdt_set_aw_op(), antidote_crdt_set_aw()) -> {ok, downstream_op()}. +downstream({add, Elem}, Set_aw) -> + downstream({add_all, [Elem]}, Set_aw); +downstream({add_all, Elems}, Set_aw) -> CreateDownstream = fun(Elem, CurrentTokens) -> Token = unique(), {Elem, [Token], CurrentTokens} end, - DownstreamOps = create_downstreams(CreateDownstream, lists:usort(Elems), ORSet, []), + DownstreamOps = create_downstreams(CreateDownstream, lists:usort(Elems), Set_aw, []), {ok, lists:reverse(DownstreamOps)}; -downstream({remove, Elem}, ORSet) -> - downstream({remove_all, [Elem]}, ORSet); -downstream({remove_all, Elems}, ORSet) -> +downstream({remove, Elem}, Set_aw) -> + downstream({remove_all, [Elem]}, Set_aw); +downstream({remove_all, Elems}, Set_aw) -> CreateDownstream = fun(Elem, CurrentTokens) -> {Elem, [], CurrentTokens} end, - DownstreamOps = create_downstreams(CreateDownstream, lists:usort(Elems), ORSet, []), + DownstreamOps = create_downstreams(CreateDownstream, lists:usort(Elems), Set_aw, []), {ok, lists:reverse(DownstreamOps)}; -downstream({reset, {}}, ORSet) -> +downstream({reset, {}}, Set_aw) -> % reset is like removing all elements - downstream({remove_all, value(ORSet)}, ORSet). + downstream({remove_all, value(Set_aw)}, Set_aw). -%% @doc apply downstream operations and update an `orset()'. --spec update(downstream_op(), orset()) -> {ok, orset()}. -update(DownstreamOp, ORSet) -> - {ok, apply_downstreams(DownstreamOp, ORSet)}. +%% @doc apply downstream operations and update an `antidote_crdt_set_aw()'. +-spec update(downstream_op(), antidote_crdt_set_aw()) -> {ok, antidote_crdt_set_aw()}. +update(DownstreamOp, Set_aw) -> + {ok, apply_downstreams(DownstreamOp, Set_aw)}. --spec equal(orset(), orset()) -> boolean(). -equal(ORSetA, ORSetB) -> +-spec equal(antidote_crdt_set_aw(), antidote_crdt_set_aw()) -> boolean(). +equal(Set_awA, Set_awB) -> % Everything inside is ordered, so this should work - ORSetA == ORSetB. + Set_awA == Set_awB. -include_lib("riak_dt/include/riak_dt_tags.hrl"). -define(TAG, ?DT_ORSET_TAG). -define(V1_VERS, 1). --spec to_binary(orset()) -> binary_orset(). -to_binary(ORSet) -> +-spec to_binary(antidote_crdt_set_aw()) -> binary_antidote_crdt_set_aw(). +to_binary(Set_aw) -> %% @TODO something smarter - <>. + <>. from_binary(<>) -> %% @TODO something smarter @@ -153,7 +153,7 @@ unique() -> crypto:strong_rand_bytes(20). %% @private generic downstream op creation for adds and removals -create_downstreams(_CreateDownstream, [], _ORSet, DownstreamOps) -> +create_downstreams(_CreateDownstream, [], _Set_aw, DownstreamOps) -> DownstreamOps; create_downstreams(CreateDownstream, Elems, [], DownstreamOps) -> lists:foldl( @@ -164,37 +164,37 @@ create_downstreams(CreateDownstream, Elems, [], DownstreamOps) -> DownstreamOps, Elems ); -create_downstreams(CreateDownstream, [Elem1|ElemsRest]=Elems, [{Elem2, Tokens}|ORSetRest]=ORSet, DownstreamOps) -> +create_downstreams(CreateDownstream, [Elem1|ElemsRest]=Elems, [{Elem2, Tokens}|Set_awRest]=Set_aw, DownstreamOps) -> if Elem1 == Elem2 -> DownstreamOp = CreateDownstream(Elem1, Tokens), - create_downstreams(CreateDownstream, ElemsRest, ORSetRest, [DownstreamOp|DownstreamOps]); + create_downstreams(CreateDownstream, ElemsRest, Set_awRest, [DownstreamOp|DownstreamOps]); Elem1 > Elem2 -> - create_downstreams(CreateDownstream, Elems, ORSetRest, DownstreamOps); + create_downstreams(CreateDownstream, Elems, Set_awRest, DownstreamOps); true -> DownstreamOp = CreateDownstream(Elem1, Tokens), - create_downstreams(CreateDownstream, ElemsRest, ORSet, [DownstreamOp|DownstreamOps]) + create_downstreams(CreateDownstream, ElemsRest, Set_aw, [DownstreamOp|DownstreamOps]) end. -%% @private apply a list of downstream ops to a given orset -apply_downstreams([], ORSet) -> - ORSet; +%% @private apply a list of downstream ops to a given antidote_crdt_set_aw +apply_downstreams([], Set_aw) -> + Set_aw; apply_downstreams(Ops, []) -> lists:foldl( - fun({Elem, ToAdd, ToRemove}, ORSet) -> - ORSet ++ apply_downstream(Elem, [], ToAdd, ToRemove) + fun({Elem, ToAdd, ToRemove}, Set_aw) -> + Set_aw ++ apply_downstream(Elem, [], ToAdd, ToRemove) end, [], Ops ); -apply_downstreams([{Elem1, ToAdd, ToRemove}|OpsRest]=Ops, [{Elem2, CurrentTokens}|ORSetRest]=ORSet) -> +apply_downstreams([{Elem1, ToAdd, ToRemove}|OpsRest]=Ops, [{Elem2, CurrentTokens}|Set_awRest]=Set_aw) -> if Elem1 == Elem2 -> - apply_downstream(Elem1, CurrentTokens, ToAdd, ToRemove) ++ apply_downstreams(OpsRest, ORSetRest); + apply_downstream(Elem1, CurrentTokens, ToAdd, ToRemove) ++ apply_downstreams(OpsRest, Set_awRest); Elem1 > Elem2 -> - [{Elem2, CurrentTokens} | apply_downstreams(Ops, ORSetRest)]; + [{Elem2, CurrentTokens} | apply_downstreams(Ops, Set_awRest)]; true -> - apply_downstream(Elem1, [], ToAdd, ToRemove) ++ apply_downstreams(OpsRest, ORSet) + apply_downstream(Elem1, [], ToAdd, ToRemove) ++ apply_downstreams(OpsRest, Set_aw) end. %% @private create an orddict entry from a downstream op @@ -325,15 +325,15 @@ concurrent_add_test() -> ?assertEqual([], value(Set5)). binary_test() -> - ORSet1 = new(), - BinaryORSet1 = to_binary(ORSet1), - {ok, ORSet2} = from_binary(BinaryORSet1), - ?assert(equal(ORSet1, ORSet2)), - - {ok, Op1} = downstream({add, <<"foo">>}, ORSet1), - {ok, ORSet3} = update(Op1, ORSet1), - BinaryORSet3 = to_binary(ORSet3), - {ok, ORSet4} = from_binary(BinaryORSet3), - ?assert(equal(ORSet3, ORSet4)). + Set_aw1 = new(), + BinarySet_aw1 = to_binary(Set_aw1), + {ok, Set_aw2} = from_binary(BinarySet_aw1), + ?assert(equal(Set_aw1, Set_aw2)), + + {ok, Op1} = downstream({add, <<"foo">>}, Set_aw1), + {ok, Set_aw3} = update(Op1, Set_aw1), + BinarySet_aw3 = to_binary(Set_aw3), + {ok, Set_aw4} = from_binary(BinarySet_aw3), + ?assert(equal(Set_aw3, Set_aw4)). -endif. diff --git a/src/antidote_crdt_gset.erl b/src/antidote_crdt_set_go.erl similarity index 80% rename from src/antidote_crdt_gset.erl rename to src/antidote_crdt_set_go.erl index dad3df6..9707f54 100644 --- a/src/antidote_crdt_gset.erl +++ b/src/antidote_crdt_set_go.erl @@ -18,9 +18,9 @@ %% %% ------------------------------------------------------------------- -%% @doc module antidote_crdt_gset - An operation based grow-only set +%% @doc module antidote_crdt_set_go - An operation based grow-only set --module(antidote_crdt_gset). +-module(antidote_crdt_set_go). -behaviour(antidote_crdt). @@ -39,11 +39,11 @@ require_state_downstream/1 ]). --type gset() :: ordsets:ordset(member()). --type gset_op() :: {add, member()} +-type antidote_crdt_set_go() :: ordsets:ordset(member()). +-type antidote_crdt_set_go_op() :: {add, member()} | {add_all, [member()]}. --type gset_effect() :: gset(). +-type antidote_crdt_set_go_effect() :: antidote_crdt_set_go(). -type member() :: term(). new() -> @@ -52,7 +52,7 @@ new() -> value(Set) -> Set. --spec downstream(gset_op(), gset()) -> {ok, gset_effect()}. +-spec downstream(antidote_crdt_set_go_op(), antidote_crdt_set_go()) -> {ok, antidote_crdt_set_go_effect()}. downstream({add, Elem}, _State) -> {ok, ordsets:from_list([Elem])}; downstream({add_all, Elems}, _State) -> @@ -81,6 +81,6 @@ all_test() -> S0 = new(), {ok, Downstream} = downstream({add, a}, S0), {ok, S1} = update(Downstream, S0), - ?assertEqual(1, riak_dt_gset:stat(element_count, S1)). + ?assertEqual(1, riak_dt_antidote_crdt_set_go:stat(element_count, S1)). -endif. diff --git a/src/antidote_crdt_set_rw.erl b/src/antidote_crdt_set_rw.erl index 55c2b49..c7882e4 100644 --- a/src/antidote_crdt_set_rw.erl +++ b/src/antidote_crdt_set_rw.erl @@ -65,12 +65,12 @@ -include_lib("eunit/include/eunit.hrl"). -endif. --export_type([set/0, binary_set/0, set_op/0]). --opaque set() :: orddict:orddict(term(), {tokens(), tokens()}). +-export_type([antidote_crdt_set_rw/0, binary_antidote_crdt_set_rw/0, antidote_crdt_set_rw_op/0]). +-opaque antidote_crdt_set_rw() :: orddict:orddict(term(), {tokens(), tokens()}). --type binary_set() :: binary(). %% A binary that from_binary/1 will operate on. +-type binary_antidote_crdt_set_rw() :: binary(). %% A binary that from_binary/1 will operate on. --type set_op() :: +-type antidote_crdt_set_rw_op() :: {add, member()} | {remove, member()} | {add_all, [member()]} @@ -84,15 +84,15 @@ -type token() :: term(). -type tokens() :: [token()]. --spec new() -> set(). +-spec new() -> antidote_crdt_set_rw(). new() -> orddict:new(). --spec value(set()) -> [member()]. +-spec value(antidote_crdt_set_rw()) -> [member()]. value(RWSet) -> [Elem || {Elem, {_, []}} <- RWSet]. --spec downstream(set_op(), set()) -> {ok, downstream_op()}. +-spec downstream(antidote_crdt_set_rw_op(), antidote_crdt_set_rw()) -> {ok, downstream_op()}. downstream({add, Elem}, RWSet) -> downstream({add_all, [Elem]}, RWSet); downstream({add_all, Elems}, RWSet) -> @@ -146,7 +146,7 @@ create_downstreams(CreateDownstream, [Elem1|ElemsRest]=Elems, [{Elem2, {AddToken unique() -> crypto:strong_rand_bytes(20). --spec update(downstream_op(), set()) -> {ok, set()}. +-spec update(downstream_op(), antidote_crdt_set_rw()) -> {ok, antidote_crdt_set_rw()}. update(DownstreamOp, RWSet) -> RWSet1 = apply_downstreams(DownstreamOp, RWSet), RWSet2 = [Entry || {_, {AddTokens, RemoveTokens}} = Entry <- RWSet1, AddTokens =/= [] orelse RemoveTokens =/= []], @@ -175,14 +175,14 @@ apply_downstream(Elem, SeenTokens, ToAdd, ToRemove, CurrentAddTokens, CurrentRem --spec equal(set(), set()) -> boolean(). +-spec equal(antidote_crdt_set_rw(), antidote_crdt_set_rw()) -> boolean(). equal(ORDictA, ORDictB) -> ORDictA == ORDictB. % Everything inside is ordered, so this should work -define(TAG, 77). -define(V1_VERS, 1). --spec to_binary(set()) -> binary_set(). +-spec to_binary(antidote_crdt_set_rw()) -> binary_antidote_crdt_set_rw(). to_binary(Set) -> %% @TODO something smarter <>. diff --git a/test/prop_crdt_fat_counter.erl b/test/prop_counter_fat.erl similarity index 79% rename from test/prop_crdt_fat_counter.erl rename to test/prop_counter_fat.erl index b8f3bea..f343dcc 100644 --- a/test/prop_crdt_fat_counter.erl +++ b/test/prop_counter_fat.erl @@ -18,26 +18,26 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_fat_counter). +-module(prop_counter_fat). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_fat_counter_spec/0, fat_counter_op/0, fat_counter_spec/1]). +-export([prop_counter_fat_spec/0, op/0, spec/1]). -prop_fat_counter_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_fat_counter, fun fat_counter_op/0, fun fat_counter_spec/1). +prop_counter_fat_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_counter_fat, fun op/0, fun spec/1). -fat_counter_spec(Operations1) -> +spec(Operations1) -> Operations = crdt_properties:filter_resets(Operations1), lists:sum([X || {_, {increment, X}} <- Operations]) - lists:sum([X || {_, {decrement, X}} <- Operations]). % generates a random counter operation -fat_counter_op() -> +op() -> oneof([ {increment, integer()}, {decrement, integer()}, diff --git a/test/prop_crdt_counter.erl b/test/prop_counter_pn.erl similarity index 82% rename from test/prop_crdt_counter.erl rename to test/prop_counter_pn.erl index 39a56ae..75cdd87 100644 --- a/test/prop_crdt_counter.erl +++ b/test/prop_counter_pn.erl @@ -18,27 +18,27 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_counter). +-module(prop_counter_pn). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_counter_spec/0, counter_op/0, counter_spec/1]). +-export([prop_counter_pn_spec/0, op/0, spec/1]). -prop_counter_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_counter, fun counter_op/0, fun counter_spec/1). +prop_counter_pn_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_counter_pn, fun op/0, fun spec/1). -counter_spec(Operations) -> +spec(Operations) -> lists:sum([X || {_, {increment, X}} <- Operations]) + lists:sum([1 || {_, increment} <- Operations]) - lists:sum([X || {_, {decrement, X}} <- Operations]) - lists:sum([1 || {_, decrement} <- Operations]). % generates a random counter operation -counter_op() -> +op() -> oneof([ increment, decrement, diff --git a/test/prop_crdt_integer.erl b/test/prop_crdt_integer.erl deleted file mode 100644 index 069f44f..0000000 --- a/test/prop_crdt_integer.erl +++ /dev/null @@ -1,61 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - --module(prop_crdt_integer). - --define(PROPER_NO_TRANS, true). --include_lib("proper/include/proper.hrl"). - -%% API --export([prop_counter_spec/0, op/0, spec/1]). - - -prop_counter_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_integer, fun op/0, fun spec/1). - - -spec(Operations1) -> - Operations = [normalizeOp(Op) || Op <- Operations1], - ConcurrentValues = - [{Clock, Val} || {Clock, {set, Val}} <- Operations, - [] == [Clock2 || {Clock2, {set, _}} <- Operations, Clock =/= Clock2, crdt_properties:clock_le(Clock, Clock2)]], - ConcurrentValues2 = - case ConcurrentValues of - [] -> [{#{}, 0}]; - _ -> ConcurrentValues - end, - Delta = - fun(Clock) -> - lists:sum([X || {C, {increment, X}} <- Operations, not crdt_properties:clock_le(C, Clock)]) - end, - WithDelta = [Val + Delta(Clock) || {Clock,Val} <- ConcurrentValues2], - lists:max(WithDelta). - -normalizeOp({Clock, {reset, {}}}) -> {Clock, {set, 0}}; -normalizeOp(Op) -> Op. - -% generates a random counter operation -op() -> - oneof([ - {set, integer()}, - {increment, integer()}, - {reset, {}} - ]). - diff --git a/test/prop_crdt_map_aw.erl b/test/prop_crdt_map_aw.erl deleted file mode 100644 index ed3bb77..0000000 --- a/test/prop_crdt_map_aw.erl +++ /dev/null @@ -1,127 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - --module(prop_crdt_map_aw). - --define(PROPER_NO_TRANS, true). --include_lib("proper/include/proper.hrl"). - -%% API --export([prop_map_spec/0]). - - -prop_map_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_map_aw, fun op/0, fun spec/1). - - -spec(Operations1) -> - Operations = lists:flatmap(fun(Op) -> normalizeOp(Op, Operations1) end, Operations1), - % the keys in the map are the ones that were updated and not deleted yet - Keys = lists:usort([Key || - % has an update - {AddClock, {update, {Key, _}}} <- Operations, - % no remove after the update: - [] == [Y || {RemoveClock, {remove, Y}} <- Operations, Key == Y, crdt_properties:clock_le(AddClock, RemoveClock)] - ]), - GroupedByKey = [{Key, nestedOps(Operations, Key)} || Key <- Keys], - NestedSpec = [{{Key,Type}, nestedSpec(Type, Ops)} || {{Key,Type}, Ops} <- GroupedByKey], - %% TODO add reset operations - lists:sort(NestedSpec). - -nestedOps(Operations, {_,Type}=Key) -> - Resets = - case Type:is_operation({reset, {}}) of - true -> - [{Clock, {reset, {}}} || {Clock, {remove, Key2}} <- Operations, Key == Key2]; - false -> [] - end, - Resets ++ [{Clock, NestedOp} || {Clock, {update, {Key2, NestedOp}}} <- Operations, Key == Key2]. - -nestedSpec(antidote_crdt_map_aw, Ops) -> spec(Ops); -nestedSpec(antidote_crdt_orset, Ops) -> prop_crdt_orset:add_wins_set_spec(Ops); -nestedSpec(antidote_crdt_integer, Ops) -> prop_crdt_integer:spec(Ops). - -% normalizes operations (update-lists into single update-operations) -normalizeOp({Clock, {update, List}}, _) when is_list(List) -> - [{Clock, {update, X}} || X <- List]; -normalizeOp({Clock, {remove, List}}, _) when is_list(List) -> - [{Clock, {remove, X}} || X <- List]; -normalizeOp({Clock, {batch, {Updates, Removes}}}, _) -> - [{Clock, {update, X}} || X <- Updates] - ++ [{Clock, {remove, X}} || X <- Removes]; -normalizeOp({Clock, {reset, {}}}, Operations) -> - % reset is like removing all current keys - Map = spec(crdt_properties:subcontext(Clock, Operations)), - Keys = [Key || {Key, _Val} <- Map], - [{Clock, {remove, X}} || X <- Keys]; -normalizeOp(X, _) -> [X]. - - -% generates a random operation -op() -> ?SIZED(Size, op(Size)). -op(Size) -> - oneof([ - {update, nestedOp(Size)}, - {update, ?LET(L, list(nestedOp(Size div 2)), removeDuplicateKeys(L, []))}, - {remove, typed_key()}, - {remove, ?LET(L, list(typed_key()), lists:usort(L))}, - ?LET({Updates,Removes}, - {list(nestedOp(Size div 2)),list(typed_key())}, - begin - Removes2 = lists:usort(Removes), - Updates2 = removeDuplicateKeys(Updates, Removes2), - {batch, {Updates2, Removes2}} - end), - {reset, {}} - ]). - -removeDuplicateKeys([], _) -> []; -removeDuplicateKeys([{Key,Op}|Rest], Keys) -> - case lists:member(Key, Keys) of - true -> removeDuplicateKeys(Rest, Keys); - false -> [{Key, Op}|removeDuplicateKeys(Rest, [Key|Keys])] - end. - -nestedOp(Size) -> - oneof( - [ - {{key(), antidote_crdt_integer}, prop_crdt_integer:op()}, - {{key(), antidote_crdt_orset}, prop_crdt_orset:set_op()} - ] - ++ - if - Size > 1 -> - [{{key(), antidote_crdt_map_aw}, ?LAZY(op(Size div 2))}]; - true -> [] - end - ). - -typed_key() -> {key(), crdt_type()}. - -crdt_type() -> - oneof([antidote_crdt_integer, antidote_crdt_orset, antidote_crdt_map_aw]). - -key() -> - oneof([a,b,c,d]). - - - - - diff --git a/test/prop_crdt_flag_dw.erl b/test/prop_flag_dw.erl similarity index 98% rename from test/prop_crdt_flag_dw.erl rename to test/prop_flag_dw.erl index 0420cbf..13b544c 100644 --- a/test/prop_crdt_flag_dw.erl +++ b/test/prop_flag_dw.erl @@ -18,7 +18,7 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_flag_dw). +-module(prop_flag_dw). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). diff --git a/test/prop_crdt_flag_ew.erl b/test/prop_flag_ew.erl similarity index 98% rename from test/prop_crdt_flag_ew.erl rename to test/prop_flag_ew.erl index a6a68bf..a786d80 100644 --- a/test/prop_crdt_flag_ew.erl +++ b/test/prop_flag_ew.erl @@ -18,7 +18,7 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_flag_ew). +-module(prop_flag_ew). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). diff --git a/test/prop_crdt_gmap.erl b/test/prop_map_go.erl similarity index 66% rename from test/prop_crdt_gmap.erl rename to test/prop_map_go.erl index 5c3d5fe..e29b2f9 100644 --- a/test/prop_crdt_gmap.erl +++ b/test/prop_map_go.erl @@ -18,17 +18,17 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_gmap). +-module(prop_map_go). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_gmap_spec/0]). +-export([prop_map_go_spec/0]). -prop_gmap_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_gmap, fun op/0, fun spec/1). +prop_map_go_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_map_go, fun op/0, fun spec/1). spec(Operations1) -> @@ -38,18 +38,12 @@ spec(Operations1) -> NestedSpec = [{{Key,Type}, nestedSpec(Type, Ops)} || {{Key,Type}, Ops} <- GroupedByKey], lists:sort(NestedSpec). -nestedOps(Operations, {_,Type}=Key) -> - Resets = - case Type:is_operation({reset, {}}) of - true -> - [{Clock, {reset, {}}} || {Clock, {reset, {}}} <- Operations]; - false -> [] - end, - Resets ++ [{Clock, NestedOp} || {Clock, {update, {Key2, NestedOp}}} <- Operations, Key == Key2]. +nestedOps(Operations, Key) -> + [{Clock, NestedOp} || {Clock, {update, {Key2, NestedOp}}} <- Operations, Key == Key2]. -nestedSpec(antidote_crdt_gmap, Ops) -> spec(Ops); -nestedSpec(antidote_crdt_orset, Ops) -> prop_crdt_orset:add_wins_set_spec(Ops); -nestedSpec(antidote_crdt_counter, Ops) -> prop_crdt_counter:counter_spec(Ops). +nestedSpec(antidote_crdt_map_go, Ops) -> spec(Ops); +nestedSpec(antidote_crdt_set_aw, Ops) -> prop_set_aw:spec(Ops); +nestedSpec(antidote_crdt_counter_pn, Ops) -> prop_counter_pn:spec(Ops). normalizeOp({Clock, {update, List}}) when is_list(List) -> @@ -60,14 +54,10 @@ normalizeOp(X) -> [X]. % generates a random operation op() -> ?SIZED(Size, op(Size)). op(Size) -> - frequency([ - % nested updates - {10, {update, oneof([ + {update, oneof([ nestedOp(Size), ?LET(L, list(nestedOp(Size div 2)), removeDuplicateKeys(L, [])) - ])}}, - {1, {reset, {}}} - ]). + ])}. removeDuplicateKeys([], _) -> []; removeDuplicateKeys([{Key,Op}|Rest], Keys) -> @@ -81,22 +71,17 @@ nestedOp(Size) -> [ % TODO add other type (orset) and recursive maps % TODO make sure that keys are unique here and in the is_operation check - {{key(), antidote_crdt_orset}, prop_crdt_orset:set_op()}, - {{key(), antidote_crdt_counter}, prop_crdt_counter:counter_op()} + {{key(), antidote_crdt_set_aw}, prop_set_aw:op()}, + {{key(), antidote_crdt_counter_pn}, prop_counter_pn:op()} ] ++ if Size > 1 -> - [{{key(), antidote_crdt_gmap}, ?LAZY(op(Size div 2))}]; + [{{key(), antidote_crdt_map_go}, ?LAZY(op(Size div 2))}]; true -> [] end ). key() -> - oneof([a,b,c,d]). - - - - - + oneof([a,b,c,d]). \ No newline at end of file diff --git a/test/prop_crdt_map_rr.erl b/test/prop_map_rr.erl similarity index 87% rename from test/prop_crdt_map_rr.erl rename to test/prop_map_rr.erl index 6b0929a..290c9c4 100644 --- a/test/prop_crdt_map_rr.erl +++ b/test/prop_map_rr.erl @@ -18,15 +18,15 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_map_rr). +-module(prop_map_rr). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_map_spec/0]). +-export([prop_map_rr_spec/0]). -prop_map_spec() -> +prop_map_rr_spec() -> crdt_properties:crdt_satisfies_partial_spec(antidote_crdt_map_rr, fun op/0, fun spec/2). @@ -66,10 +66,10 @@ nestedOps(Operations, {_,Type}=Key) -> Resets ++ [{Clock, NestedOp} || {Clock, {update, {Key2, NestedOp}}} <- Operations, Key == Key2]. nestedSpec(antidote_crdt_map_rr, Ops, Value) -> spec(Ops, Value); -% nestedSpec(antidote_crdt_orset, Ops) -> prop_crdt_orset:add_wins_set_spec(Ops); -% nestedSpec(antidote_crdt_big_counter, Ops) -> prop_crdt_big_counter:big_counter_spec(Ops); +% nestedSpec(antidote_crdt_set_aw, Ops) -> prop_set_aw:spec(Ops); +% nestedSpec(antidote_crdt_counter_fat, Ops) -> prop_counter_fat:spec(Ops); nestedSpec(antidote_crdt_set_rw, Ops, Value) -> - (crdt_properties:spec_to_partial(fun prop_crdt_set_rw:rem_wins_set_spec/1))(Ops, Value). + (crdt_properties:spec_to_partial(fun prop_set_rw:spec/1))(Ops, Value). % normalizes operations (update-lists into single update-operations) normalizeOp({Clock, {update, List}}, _) when is_list(List) -> @@ -114,9 +114,9 @@ removeDuplicateKeys([{Key,Op}|Rest], Keys) -> nestedOp(Size) -> oneof( [ - % {{key(), prop_crdt_big_counter}, prop_crdt_big_counter:big_counter_op()}, - % {{key(), antidote_crdt_orset}, prop_crdt_orset:set_op()}, - {{key(), antidote_crdt_set_rw}, prop_crdt_set_rw:set_op()} + % {{key(), antidote_crdt_counter_fat}, prop_counter_fat:op()}, + % {{key(), antidote_crdt_set_aw}, prop_set_aw:op()}, + {{key(), antidote_crdt_set_rw}, prop_set_rw:op()} ] ++ if diff --git a/test/prop_crdt_lwwreg.erl b/test/prop_register_lww.erl similarity index 91% rename from test/prop_crdt_lwwreg.erl rename to test/prop_register_lww.erl index 514eb48..904e3c2 100644 --- a/test/prop_crdt_lwwreg.erl +++ b/test/prop_register_lww.erl @@ -18,17 +18,17 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_lwwreg). +-module(prop_register_lww). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_lwwreg_spec/0]). +-export([prop_register_lww_spec/0]). -prop_lwwreg_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_lwwreg, fun op/0, fun spec/1). +prop_register_lww_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_register_lww, fun op/0, fun spec/1). spec(Operations) -> diff --git a/test/prop_crdt_mvreg.erl b/test/prop_register_mv.erl similarity index 84% rename from test/prop_crdt_mvreg.erl rename to test/prop_register_mv.erl index e9699cd..4aba5e8 100644 --- a/test/prop_crdt_mvreg.erl +++ b/test/prop_register_mv.erl @@ -18,17 +18,17 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_mvreg). +-module(prop_register_mv). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_mvreg_spec/0]). +-export([prop_register_mv_spec/0]). -prop_mvreg_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_mvreg, fun op/0, fun spec/1). +prop_register_mv_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_register_mv, fun op/0, fun spec/1). spec(Operations) -> @@ -36,7 +36,7 @@ spec(Operations) -> -% generates a random counter operation +% generates a random operation op() -> frequency([ {5, {assign, oneof([a,b,c,d,e,f,g,h,i])}}, diff --git a/test/prop_crdt_orset.erl b/test/prop_set_aw.erl similarity index 86% rename from test/prop_crdt_orset.erl rename to test/prop_set_aw.erl index 2099403..dcc16eb 100644 --- a/test/prop_crdt_orset.erl +++ b/test/prop_set_aw.erl @@ -18,20 +18,20 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_orset). +-module(prop_set_aw). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_orset_spec/0, set_op/0, add_wins_set_spec/1]). +-export([prop_set_aw_spec/0, op/0, spec/1]). -prop_orset_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_orset, fun set_op/0, fun add_wins_set_spec/1). +prop_set_aw_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_set_aw, fun op/0, fun spec/1). -add_wins_set_spec(Operations1) -> +spec(Operations1) -> Operations = lists:flatmap(fun normalizeOperation/1, Operations1), lists:usort( % all X, @@ -52,8 +52,8 @@ normalizeOperation({Clock, {remove_all, Elems}}) -> normalizeOperation(X) -> [X]. -% generates a random counter operation -set_op() -> +% generates a random operation +op() -> oneof([ {add, set_element()}, {add_all, list(set_element())}, @@ -63,5 +63,4 @@ set_op() -> ]). set_element() -> - oneof([a,b]). - + oneof([a,b]). \ No newline at end of file diff --git a/test/prop_crdt_gset.erl b/test/prop_set_go.erl similarity index 84% rename from test/prop_crdt_gset.erl rename to test/prop_set_go.erl index ac50d69..a1c110f 100644 --- a/test/prop_crdt_gset.erl +++ b/test/prop_set_go.erl @@ -18,27 +18,27 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_gset). +-module(prop_set_go). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_gset_spec/0]). +-export([prop_set_go_spec/0]). -prop_gset_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_gset, fun set_op/0, fun add_wins_set_spec/1). +prop_set_go_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_set_go, fun op/0, fun spec/1). -add_wins_set_spec(Operations) -> +spec(Operations) -> lists:usort( [X || {_, {add, X}} <- Operations] ++ [X || {_, {add_all, Xs}} <- Operations, X <- Xs] ). % generates a random counter operation -set_op() -> +op() -> oneof([ {add, set_element()}, {add_all, list(set_element())} diff --git a/test/prop_crdt_set_rw.erl b/test/prop_set_rw.erl similarity index 88% rename from test/prop_crdt_set_rw.erl rename to test/prop_set_rw.erl index eb5680a..dc3ea1c 100644 --- a/test/prop_crdt_set_rw.erl +++ b/test/prop_set_rw.erl @@ -18,20 +18,20 @@ %% %% ------------------------------------------------------------------- --module(prop_crdt_set_rw). +-module(prop_set_rw). -define(PROPER_NO_TRANS, true). -include_lib("proper/include/proper.hrl"). %% API --export([prop_orset_spec/0, set_op/0, rem_wins_set_spec/1]). +-export([prop_set_rw_spec/0, op/0, spec/1]). -prop_orset_spec() -> - crdt_properties:crdt_satisfies_spec(antidote_crdt_set_rw, fun set_op/0, fun rem_wins_set_spec/1). +prop_set_rw_spec() -> + crdt_properties:crdt_satisfies_spec(antidote_crdt_set_rw, fun op/0, fun spec/1). -rem_wins_set_spec(Operations1) -> +spec(Operations1) -> Operations2 = crdt_properties:filter_resets(Operations1), Operations = lists:flatmap(fun normalizeOperation/1, Operations2), RemoveClocks = @@ -55,7 +55,7 @@ normalizeOperation(X) -> [X]. % generates a random counter operation -set_op() -> +op() -> oneof([ {add, set_element()}, {add_all, list(set_element())},