Skip to content

Instantly share code, notes, and snippets.

@russelldb
Created November 20, 2011 20:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save russelldb/1380896 to your computer and use it in GitHub Desktop.
Save russelldb/1380896 to your computer and use it in GitHub Desktop.
State based PN-Counter, using riak's vclocks
%%% @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).
@eriksoe
Copy link

eriksoe commented Mar 25, 2012

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.)

@russelldb
Copy link
Author

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