Created
November 20, 2011 20:54
-
-
Save russelldb/1380896 to your computer and use it in GitHub Desktop.
State based PN-Counter, using riak's vclocks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%% @author Russell Brown <russelldb@basho.com> | |
%%% @copyright (C) 2011, Russell Brown | |
%%% @doc | |
%%% an attmept at using the normal riak vclocks to create a pn counter | |
%%% The idea is basically, get/put to two keys, one for incr, one for decr | |
%%% And use the vclocks as the actual counter, the payload is meaningless | |
%%% But each put to incr is a increment, and each put to decr, is a decrement | |
%%% The counters value is the sum of the incr objects vclock counts, minus the sum of the | |
%%% decr vclocks counts. | |
%%% Siblings can be handled by a merge that takes the greater of two vclcock counts where there is conflict | |
%%% (which is what Riak does for you) | |
%%% @end | |
%%% Created : 20 Nov 2011 by Russell Brown <russelldb@basho.com> | |
-module(riak_pncount). | |
-export([increment/2, decrement/2, count/2]). | |
-define(BUCKET, <<"counters">>). | |
-define(INCR(Counter), <<Counter/binary, <<"_incr">>/binary>>). | |
-define(DECR(Counter), <<Counter/binary, <<"_decr">>/binary>>). | |
increment(RC, Counter) when is_pid(RC), is_binary(Counter) -> | |
do(RC, ?INCR(Counter)). | |
decrement(RC, Counter) when is_pid(RC), is_binary(Counter) -> | |
do(RC, ?DECR(Counter)). | |
count(RC, Counter) when is_pid(RC), is_binary(Counter) -> | |
vclock_count(fetch(RC, ?INCR(Counter))) - vclock_count(fetch(RC, ?DECR(Counter))). | |
%% Private | |
vclock_count({ok, Obj}) -> | |
VC = riakc_obj:vclock(Obj), | |
Vclock = binary_to_term(zlib:unzip(VC)), | |
lists:sum([vclock:get_counter(Node, Vclock) || Node <- vclock:all_nodes(Vclock)]); | |
vclock_count({error, notfound}) -> | |
0. | |
do(RC, Key) -> | |
O = case fetch(RC, Key) of | |
{ok, Obj} -> Obj; | |
{error, notfound} -> | |
riakc_obj:new(?BUCKET, Key, <<"tada">>) | |
end, | |
riakc_pb_socket:put(RC, O). | |
fetch(RC, Key) -> | |
riakc_pb_socket:get(RC, ?BUCKET, Key). |
Yeah, this is just an example of one way you could bend existing riak into a CRDT, vclock pruning would truncate the count.
I have a working riak_core app for CRDTs, now, and I'm hoping to work into RIak in some way this year, since I think counters alone make it useful, but the sets are handy too.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This approach breaks on vclock pruning, if that ever happens...
(I've considered suggesting that pruned counts get collected in a special node-ID entry - that change to pruning would protect this counter from pruning effects.)