/apps/rts/src/rts_stat_vnode.erl

https://github.com/rzezeski/rts · Erlang · 227 lines · 183 code · 33 blank · 11 comment · 0 complexity · 45b0ef0b73c3c0a81c7cd57fb4d0f863 MD5 · raw file

  1. %% @doc A vnode to handle get & put commands for stat data. The vnode
  2. %% requests will be hashed on Client and StatName and will use a
  3. %% coordinator to enforce N/R/W values.
  4. -module(rts_stat_vnode).
  5. -behaviour(riak_core_vnode).
  6. -include("rts.hrl").
  7. -include_lib("riak_core/include/riak_core_vnode.hrl").
  8. -export([start_vnode/1,
  9. init/1,
  10. terminate/2,
  11. handle_command/3,
  12. is_empty/1,
  13. delete/1,
  14. handle_handoff_command/3,
  15. handoff_starting/2,
  16. handoff_cancelled/1,
  17. handoff_finished/2,
  18. handle_handoff_data/2,
  19. encode_handoff_item/2,
  20. handle_coverage/4,
  21. handle_exit/3]).
  22. -export([
  23. get/3,
  24. set/4,
  25. repair/3,
  26. incr/3,
  27. incrby/4,
  28. append/4,
  29. sadd/4,
  30. srem/4
  31. ]).
  32. -record(state, {partition, stats, node}).
  33. -define(MASTER, rts_stat_vnode_master).
  34. -define(sync(PrefList, Command, Master),
  35. riak_core_vnode_master:sync_command(PrefList, Command, Master)).
  36. %%%===================================================================
  37. %%% API
  38. %%%===================================================================
  39. start_vnode(I) ->
  40. riak_core_vnode_master:get_vnode_pid(I, ?MODULE).
  41. get(Preflist, ReqID, StatName) ->
  42. riak_core_vnode_master:command(Preflist,
  43. {get, ReqID, StatName},
  44. {fsm, undefined, self()},
  45. ?MASTER).
  46. set(Preflist, Identity, StatName, Val) ->
  47. riak_core_vnode_master:command(Preflist,
  48. {set, Identity, StatName, Val},
  49. ?MASTER).
  50. %% @doc Attempt to repair -- fire and forget.
  51. repair(IdxNode, StatName, Obj) ->
  52. riak_core_vnode_master:command(IdxNode,
  53. {repair, undefined, StatName, Obj},
  54. ignore,
  55. ?MASTER).
  56. %% TODO: I have to look at the Sender stuff more closely again
  57. incr(Preflist, Identity, StatName) ->
  58. riak_core_vnode_master:command(Preflist,
  59. {incrby, Identity, StatName, 1},
  60. {fsm, undefined, self()},
  61. ?MASTER).
  62. incrby(Preflist, Identity, StatName, Val) ->
  63. riak_core_vnode_master:command(Preflist,
  64. {incrby, Identity, StatName, Val},
  65. {fsm, undefined, self()},
  66. ?MASTER).
  67. append(Preflist, Identity, StatName, Val) ->
  68. riak_core_vnode_master:command(Preflist,
  69. {append, Identity, StatName, Val},
  70. {fsm, undefined, self()},
  71. ?MASTER).
  72. sadd(Preflist, Identity, StatName, Val) ->
  73. riak_core_vnode_master:command(Preflist,
  74. {sadd, Identity, StatName, Val},
  75. {fsm, undefined, self()},
  76. ?MASTER).
  77. srem(Preflist, Identity, StatName, Val) ->
  78. riak_core_vnode_master:command(Preflist,
  79. {srem, Identity, StatName, Val},
  80. {fsm, undefined, self()},
  81. ?MASTER).
  82. %%%===================================================================
  83. %%% Callbacks
  84. %%%===================================================================
  85. init([Partition]) ->
  86. {ok, #state { partition=Partition, stats=dict:new(), node=node() }}.
  87. handle_command({get, ReqID, StatName}, _Sender,
  88. #state{stats=Stats, partition=Partition, node=Node}=State) ->
  89. Reply =
  90. case dict:find(StatName, Stats) of
  91. error ->
  92. not_found;
  93. {ok, Found} ->
  94. Found
  95. end,
  96. {reply, {ok, ReqID, {Partition,Node}, Reply}, State};
  97. handle_command({set, {ReqID, _}, StatName, Val}, _Sender, #state{stats=Stats0}=State) ->
  98. Stats = dict:store(StatName, Val, Stats0),
  99. {reply, {ok, ReqID}, State#state{stats=Stats}};
  100. handle_command({repair, undefined, StatName, Obj}, _Sender, #state{stats=Stats0}=State) ->
  101. error_logger:error_msg("repair performed ~p~n", [Obj]),
  102. Stats = dict:store(StatName, Obj, Stats0),
  103. {noreply, State#state{stats=Stats}};
  104. handle_command({incrby, {ReqID, Coordinator}, StatName, IncrBy}, _Sender, #state{stats=Stats0}=State) ->
  105. Obj =
  106. case dict:find(StatName, Stats0) of
  107. {ok, #rts_obj{val=#incr{total=T0, counts=C0}}=O} ->
  108. T = T0 + IncrBy,
  109. C = dict:update_counter(Coordinator, IncrBy, C0),
  110. Val = #incr{total=T, counts=C},
  111. rts_obj:update(Val, Coordinator, O);
  112. error ->
  113. Val = #incr{total=IncrBy,
  114. counts=dict:from_list([{Coordinator, IncrBy}])},
  115. VC0 = vclock:fresh(),
  116. VC = vclock:increment(Coordinator, VC0),
  117. #rts_obj{val=Val, vclock=VC}
  118. end,
  119. Stats = dict:store(StatName, Obj, Stats0),
  120. {reply, {ok, ReqID}, State#state{stats=Stats}};
  121. handle_command({append, {ReqID, _}, StatName, Val}, _Sender, #state{stats=Stats0}=State) ->
  122. Stats = try dict:append(StatName, Val, Stats0)
  123. catch _:_ -> dict:store(StatName, [Val], Stats0)
  124. end,
  125. {reply, {ok, ReqID}, State#state{stats=Stats}};
  126. handle_command({sadd, {ReqID, Coordinator}, StatName, Val},
  127. _Sender, #state{stats=Stats0}=State) ->
  128. SB =
  129. case dict:find(StatName, Stats0) of
  130. {ok, #rts_obj{val=SB0}=O} ->
  131. SB1 = statebox:modify({sets, add_element, [Val]}, SB0),
  132. SB2 = statebox:expire(?STATEBOX_EXPIRE, SB1),
  133. rts_obj:update(SB2, Coordinator, O);
  134. error ->
  135. SB0 = statebox:new(fun sets:new/0),
  136. SB1 = statebox:modify({sets, add_element, [Val]}, SB0),
  137. VC0 = vclock:fresh(),
  138. VC = vclock:increment(Coordinator, VC0),
  139. #rts_obj{val=SB1, vclock=VC}
  140. end,
  141. Stats = dict:store(StatName, SB, Stats0),
  142. {reply, {ok, ReqID}, State#state{stats=Stats}};
  143. handle_command({srem, {ReqID, Coordinator}, StatName, Val},
  144. _Sender, #state{stats=Stats0}=State) ->
  145. SB =
  146. case dict:find(StatName, Stats0) of
  147. {ok, #rts_obj{val=SB0}=O} ->
  148. SB1 = statebox:modify({sets, del_element, [Val]}, SB0),
  149. SB2 = statebox:expire(?STATEBOX_EXPIRE, SB1),
  150. rts_obj:update(SB2, Coordinator, O);
  151. error ->
  152. SB0 = statebox:new(fun sets:new/0),
  153. SB1 = statebox:modify({sets, del_element, [Val]}, SB0),
  154. VC0 = vclock:fresh(),
  155. VC = vclock:increment(Coordinator, VC0),
  156. #rts_obj{val=SB1, vclock=VC}
  157. end,
  158. Stats = dict:store(StatName, SB, Stats0),
  159. {reply, {ok, ReqID}, State#state{stats=Stats}}.
  160. handle_handoff_command(?FOLD_REQ{foldfun=Fun, acc0=Acc0}, _Sender, State) ->
  161. Acc = dict:fold(Fun, Acc0, State#state.stats),
  162. {reply, Acc, State};
  163. handle_handoff_command(Req, _Sender, State) ->
  164. handle_command(Req, _Sender, State).
  165. handoff_starting(_TargetNode, _State) ->
  166. {true, _State}.
  167. handoff_cancelled(State) ->
  168. {ok, State}.
  169. handoff_finished(_TargetNode, State) ->
  170. {ok, State}.
  171. handle_handoff_data(Data, #state{stats=Stats0}=State) ->
  172. {StatName, HObj} = binary_to_term(Data),
  173. MObj =
  174. case dict:find(StatName, Stats0) of
  175. {ok, Obj} -> rts_obj:merge([Obj,HObj]);
  176. error -> HObj
  177. end,
  178. Stats = dict:store(StatName, MObj, Stats0),
  179. {reply, ok, State#state{stats=Stats}}.
  180. encode_handoff_item(StatName, Val) ->
  181. term_to_binary({StatName,Val}).
  182. is_empty(State) ->
  183. case dict:size(State#state.stats) of
  184. 0 -> {true, State};
  185. _ -> {false, State}
  186. end.
  187. delete(State) ->
  188. {ok, State}.
  189. handle_coverage(_Req, _KeySpaces, _Sender, State) ->
  190. {stop, not_implemented, State}.
  191. handle_exit(_Pid, _Reason, _State) ->
  192. {noreply, _State}.
  193. terminate(_Reason, _State) ->
  194. ok.