Current section
Files
Jump to
Current section
Files
src/mclock.erl
%% -------------------------------------------------------------------
%%
%%
%% Copyright (c) 2016 Georges R. Younes, Inc. 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 A simple Erlang implementation of matrix clocks not used exactly as Matrix clock
%% but as Recent Timestamp Matrix RTM as in referenced below:
%%
%% @reference Carlos Baquero, Paulo Sérgio Almeida, and Ali Shoker
%% Making Operation-based CRDTs Operation-based (2014)
%% [http://haslab.uminho.pt/ashoker/files/opbaseddais14.pdf]
-module(mclock).
-include("ishikawa.hrl").
-export([fresh/0, update_rtm/3, update_stablevv/1]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
-export_type([mclock/0, mclock_node/0]).
-type mclock() :: [mc_entry()].
% The timestamp is present but not used, in case a client wishes to inspect it.
-type mc_entry() :: {mclock_node(), vclock:vclock()}.
% Nodes can have any term() as a name, but they must differ from each other.
-type mclock_node() :: term().
% @doc Create a brand new mclock.
-spec fresh() -> mclock().
fresh() ->
[].
-spec update_rtm(mclock(), actor(), vclock:vclock()) -> mclock().
update_rtm(RTM, MsgActor, MsgVV) ->
case lists:keymember(MsgActor, 1, RTM) of
true ->
lists:keyreplace(MsgActor, 1, RTM, {MsgActor, MsgVV});
false ->
RTM ++ [{MsgActor, MsgVV}]
end.
-spec update_stablevv(mclock()) -> vclock:vclock().
update_stablevv(RTM0) ->
[{_, Min0} | RTM1] = RTM0,
lists:foldl(
fun({_, VV}, Acc) ->
lists:foldl(
fun({Actor, Count}, Acc2) ->
case Count < vclock:get_counter(Actor, Acc2) of
true ->
lists:keyreplace(Actor, 1, Acc2, {Actor, Count});
false ->
Acc2
end
end,
Acc,
VV
)
end,
Min0,
RTM1
).
%% ===================================================================
%% EUnit tests
%% ===================================================================
-ifdef(TEST).
% doc Serves as both a trivial test and some example code.
update_rtm_test() ->
RTM = [{<<"1">>, [{<<"1">>, 1}, {<<"2">>, 2}]},
{<<"2">>, [{<<"1">>, 3}, {<<"2">>, 4}]}],
VV = [{<<"1">>, 5},
{<<"2">>, 6}],
?assertEqual([{<<"1">>, [{<<"1">>, 5}, {<<"2">>, 6}]},
{<<"2">>, [{<<"1">>, 3}, {<<"2">>, 4}]}], update_rtm(RTM, <<"1">>, VV)),
?assertEqual([{<<"1">>, [{<<"1">>, 1}, {<<"2">>, 2}]},
{<<"2">>, [{<<"1">>, 5}, {<<"2">>, 6}]}], update_rtm(RTM, <<"2">>, VV)).
update_stablevv_test() ->
RTM0 = [{<<"1">>, [{<<"1">>, 1}, {<<"2">>, 2}]},
{<<"2">>, [{<<"1">>, 3}, {<<"2">>, 2}]}],
RTM1 = [{<<"1">>, [{<<"1">>, 4}, {<<"2">>, 6}]},
{<<"2">>, [{<<"1">>, 2}, {<<"2">>, 5}]}],
?assertEqual([{<<"1">>, 1}, {<<"2">>, 2}], update_stablevv(RTM0)),
?assertEqual([{<<"1">>, 2}, {<<"2">>, 5}], update_stablevv(RTM1)).
-endif.