<commit_before>%% -------------------------------------------------------------------
%%
%% 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(vectorclock).

-include("antidote.hrl").

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

-export([get_clock/1, update_clock/3, get_clock_by_key/1,
         is_greater_than/2,
         get_clock_of_dc/2,
         set_clock_of_dc/3,
         get_stable_snapshot/0,
         get_scalar_stable_time/0,
         from_list/1,
         eq/2,lt/2,gt/2,le/2,ge/2, strict_ge/2, strict_le/2]).

-export_type([vectorclock/0]).


-spec get_clock_by_key(Key :: key()) -> {ok, vectorclock:vectorclock()} | {error, term()}.
get_clock_by_key(Key) ->
    Preflist = log_utilities:get_preflist_from_key(Key),
    Indexnode = hd(Preflist),
    try
        riak_core_vnode_master:sync_command(
          Indexnode, get_clock, vectorclock_vnode_master)
    catch
        _:Reason ->
            lager:error("Exception caught: ~p", [Reason]),
            {error, Reason}
    end.

-spec get_clock(Partition :: non_neg_integer())
               -> {ok, vectorclock()} | {error, term()}.
get_clock(Partition) ->
    Indexnode = {Partition, node()},
    try
        riak_core_vnode_master:sync_command(
           Indexnode, get_clock, vectorclock_vnode_master)
    catch
        _:Reason ->
            lager:error("Exception caught: ~p", [Reason]),
            {error, Reason}
    end.

%% @doc get_stable_snapshot: Returns stable snapshot time
%% in the current DC. stable snapshot time is the snapshot available at
%% in all partitions
-spec get_stable_snapshot() -> {ok, vectorclock:vectorclock()}.
-ifdef (USE_CLOCKSI).
get_stable_snapshot() ->
    %% This is fine if transactions coordinators exists on the ring (i.e. they have access
    %% to riak core meta-data) otherwise will have to change this
    {ok, vectorclock_vnode:get_stable_snapshot()}.
-else.
-ifdef (USE_GR).
get_stable_snapshot() ->
    %% For gentlerain use the same format as clocksi
    %% But, replicate GST to all entries in the dict
    StableSnapshot = vectorclock_vnode:get_stable_snapshot(),
    case dict:is_empty(StableSnapshot) of
        true -> 
            {ok, StableSnapshot};
        false ->
            ListTime = dict:fold( 
                         fun(_Key, Value, Acc) ->
                                 [Value | Acc ]
                         end, [], StableSnapshot),
            GST = lists:min(ListTime),
            {ok, dict:map( 
                   fun(_K, _V) ->
                           GST
                   end,
                   StableSnapshot)}
    end.
-endif.
-endif.

%% Returns the minimum value in the stable vector snapshot time
%% Useful for gentlerain protocol.
-spec get_scalar_stable_time() -> {ok, non_neg_integer()}.
get_scalar_stable_time() ->   
    StableSnapshot = vectorclock_vnode:get_stable_snapshot(),
    %% dict:is_empty/1 is not available, hence using dict:size/1
    %% to check whether it is empty
    case dict:size(StableSnapshot) of
        0 -> 
            {ok, 0};
        _ ->
            %% This is correct only if stablesnapshot has entries for
            %% all DCs. Inorder to check that we need to configure the 
            %% number of DCs in advance, which is not possible now.
            ListTime = dict:fold( <commit_msg>use dict size instead of is_empty<commit_after>%% -------------------------------------------------------------------
%%
%% 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(vectorclock).

-include("antidote.hrl").

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

-export([get_clock/1, update_clock/3, get_clock_by_key/1,
         is_greater_than/2,
         get_clock_of_dc/2,
         set_clock_of_dc/3,
         get_stable_snapshot/0,
         get_scalar_stable_time/0,
         from_list/1,
         eq/2,lt/2,gt/2,le/2,ge/2, strict_ge/2, strict_le/2]).

-export_type([vectorclock/0]).


-spec get_clock_by_key(Key :: key()) -> {ok, vectorclock:vectorclock()} | {error, term()}.
get_clock_by_key(Key) ->
    Preflist = log_utilities:get_preflist_from_key(Key),
    Indexnode = hd(Preflist),
    try
        riak_core_vnode_master:sync_command(
          Indexnode, get_clock, vectorclock_vnode_master)
    catch
        _:Reason ->
            lager:error("Exception caught: ~p", [Reason]),
            {error, Reason}
    end.

-spec get_clock(Partition :: non_neg_integer())
               -> {ok, vectorclock()} | {error, term()}.
get_clock(Partition) ->
    Indexnode = {Partition, node()},
    try
        riak_core_vnode_master:sync_command(
           Indexnode, get_clock, vectorclock_vnode_master)
    catch
        _:Reason ->
            lager:error("Exception caught: ~p", [Reason]),
            {error, Reason}
    end.

%% @doc get_stable_snapshot: Returns stable snapshot time
%% in the current DC. stable snapshot time is the snapshot available at
%% in all partitions
-spec get_stable_snapshot() -> {ok, vectorclock:vectorclock()}.
-ifdef (USE_CLOCKSI).
get_stable_snapshot() ->
    %% This is fine if transactions coordinators exists on the ring (i.e. they have access
    %% to riak core meta-data) otherwise will have to change this
    {ok, vectorclock_vnode:get_stable_snapshot()}.
-else.
-ifdef (USE_GR).
get_stable_snapshot() ->
    %% For gentlerain use the same format as clocksi
    %% But, replicate GST to all entries in the dict
    StableSnapshot = vectorclock_vnode:get_stable_snapshot(),
    case dict:size(StableSnapshot) of
        0 -> 
            {ok, StableSnapshot};
        _ ->
            ListTime = dict:fold( 
                         fun(_Key, Value, Acc) ->
                                 [Value | Acc ]
                         end, [], StableSnapshot),
            GST = lists:min(ListTime),
            {ok, dict:map( 
                   fun(_K, _V) ->
                           GST
                   end,
                   StableSnapshot)}
    end.
-endif.
-endif.

%% Returns the minimum value in the stable vector snapshot time
%% Useful for gentlerain protocol.
-spec get_scalar_stable_time() -> {ok, non_neg_integer()}.
get_scalar_stable_time() ->   
    StableSnapshot = vectorclock_vnode:get_stable_snapshot(),
    %% dict:is_empty/1 is not available, hence using dict:size/1
    %% to check whether it is empty
    case dict:size(StableSnapshot) of
        0 -> 
            %% This case occur when updates from remote replicas has not yet received
            %% or when there are no remote replicas
            %% Since with current setup there is no mechanism
            %% to distinguish these, we assume the second case
            Now = clocksi_vnode:now_microsec(erlang:now()) - ?OLD_SS_MICROSEC,
            {ok, Now};
        _ ->
            %% This is correct only if stablesnapshot has entries for
            %% all DCs. Inorder to check that we need to configure the 
            %% number of DCs in advance, which is not possible now.
            ListTime = dict:fold( 
{'fraction_non_alphanumeric': 0.13634728384698405, 'fraction_numerical': 0.0069552704161170405, 'mean_word_length': 2.2489287105570703, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2715500', 'n_tokens_mistral': 2448, 'n_tokens_neox': 2380, 'n_words': 718}