summaryrefslogtreecommitdiff
path: root/src/couchdb/couch_stats_collector.erl
diff options
context:
space:
mode:
authorPaul Joseph Davis <davisp@apache.org>2009-09-17 04:04:46 +0000
committerPaul Joseph Davis <davisp@apache.org>2009-09-17 04:04:46 +0000
commit4ddb75549c6565b5a2ca9edec0d965bdf53eed11 (patch)
tree3e4b30f1429857f08a399e14f0cd1848e2d1cd4e /src/couchdb/couch_stats_collector.erl
parentda4f4a1b69f40c8847131f6103d8faca2ef4602c (diff)
Fixes COUCHDB-396
Makes the stats calculated over a moving window isntead of calculated for non-overlapping timeframes. This should make trend monitoring more robust. Thanks once again to Bob Dionne for double checking this. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@816043 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/couchdb/couch_stats_collector.erl')
-rw-r--r--src/couchdb/couch_stats_collector.erl143
1 files changed, 66 insertions, 77 deletions
diff --git a/src/couchdb/couch_stats_collector.erl b/src/couchdb/couch_stats_collector.erl
index 327a37c1..59d62a6e 100644
--- a/src/couchdb/couch_stats_collector.erl
+++ b/src/couchdb/couch_stats_collector.erl
@@ -18,23 +18,15 @@
-behaviour(gen_server).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
+-export([start/0, stop/0]).
+-export([all/0, all/1, get/1, increment/1, decrement/1, record/2, clear/1]).
+-export([track_process_count/1, track_process_count/2]).
+-export([init/1, terminate/2, code_change/3]).
+-export([handle_call/3, handle_cast/2, handle_info/2]).
--export([start/0, stop/0, get/1,
- increment/1, decrement/1,
- track_process_count/1, track_process_count/2,
- record/2, clear/1,
- all/0, all/1]).
-
--record(state, {}).
-
--define(ABSOLUTE_VALUE_COUNTER_TABLE, abs_table).
--define(HIT_COUNTER_TABLE, hit_table).
-
-
-% PUBLIC API
+-define(HIT_TABLE, stats_hit_table).
+-define(ABS_TABLE, stats_abs_table).
start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
@@ -42,106 +34,103 @@ start() ->
stop() ->
gen_server:call(?MODULE, stop).
+all() ->
+ ets:tab2list(?HIT_TABLE) ++ abs_to_list().
+
+all(Type) ->
+ case Type of
+ incremental -> ets:tab2list(?HIT_TABLE);
+ absolute -> abs_to_list()
+ end.
+
get(Key) ->
- case ets:lookup(?HIT_COUNTER_TABLE, Key) of
+ case ets:lookup(?HIT_TABLE, Key) of
[] ->
- case ets:lookup(?ABSOLUTE_VALUE_COUNTER_TABLE, Key) of
+ case ets:lookup(?ABS_TABLE, Key) of
[] ->
- 0;
- Result2 -> extract_value_from_ets_result(Key, Result2)
+ nil;
+ AbsVals ->
+ lists:map(fun({_, Value}) -> Value end, AbsVals)
end;
- [{_,Result1}] -> Result1
+ [{_, Counter}] ->
+ Counter
end.
-increment({Module, Key}) when is_integer(Key) ->
- increment({Module, list_to_atom(integer_to_list(Key))});
increment(Key) ->
- case catch ets:update_counter(?HIT_COUNTER_TABLE, Key, 1) of
+ Key2 = make_key(Key),
+ case catch ets:update_counter(?HIT_TABLE, Key2, 1) of
{'EXIT', {badarg, _}} ->
- true = ets:insert(?HIT_COUNTER_TABLE, {Key, 1}),
+ true = ets:insert(?HIT_TABLE, {Key2, 1}),
ok;
- _ -> ok
+ _ ->
+ ok
end.
decrement(Key) ->
- case catch ets:update_counter(?HIT_COUNTER_TABLE, Key, -1) of
+ Key2 = make_key(Key),
+ case catch ets:update_counter(?HIT_TABLE, Key2, -1) of
{'EXIT', {badarg, _}} ->
- true = ets:insert(?HIT_COUNTER_TABLE, {Key, -1}),
+ true = ets:insert(?HIT_TABLE, {Key2, -1}),
ok;
_ -> ok
end.
record(Key, Value) ->
- ets:insert(?ABSOLUTE_VALUE_COUNTER_TABLE, {Key, Value}).
+ true = ets:insert(?ABS_TABLE, {make_key(Key), Value}).
clear(Key) ->
- true = ets:delete(?ABSOLUTE_VALUE_COUNTER_TABLE, Key).
-
-all() ->
- lists:append(ets:tab2list(?HIT_COUNTER_TABLE),
- ets:tab2list(?ABSOLUTE_VALUE_COUNTER_TABLE)).
-
-all(Type) ->
- case Type of
- incremental -> ets:tab2list(?HIT_COUNTER_TABLE);
- absolute -> ets:tab2list(?ABSOLUTE_VALUE_COUNTER_TABLE)
- end.
+ true = ets:delete(?ABS_TABLE, make_key(Key)).
track_process_count(Stat) ->
track_process_count(self(), Stat).
track_process_count(Pid, Stat) ->
+ MonitorFun = fun() ->
+ Ref = erlang:monitor(process, Pid),
+ receive {'DOWN', Ref, _, _, _} -> ok end,
+ couch_stats_collector:decrement(Stat)
+ end,
case (catch couch_stats_collector:increment(Stat)) of
- ok ->
- spawn(
- fun() ->
- erlang:monitor(process, Pid),
- receive {'DOWN', _, _, _, _} -> ok end,
- couch_stats_collector:decrement(Stat)
- end);
- _ -> ok
+ ok -> spawn(MonitorFun);
+ _ -> ok
end.
-% GEN_SERVER
-
-
init(_) ->
- ets:new(?HIT_COUNTER_TABLE, [named_table, set, public]),
- ets:new(?ABSOLUTE_VALUE_COUNTER_TABLE, [named_table, duplicate_bag, public]),
- {ok, #state{}}.
+ ets:new(?HIT_TABLE, [named_table, set, public]),
+ ets:new(?ABS_TABLE, [named_table, duplicate_bag, public]),
+ {ok, nil}.
+terminate(_Reason, _State) ->
+ ok.
handle_call(stop, _, State) ->
{stop, normal, stopped, State}.
-
-% PRIVATE API
-
-extract_value_from_ets_result(_Key, Result) ->
- lists:map(fun({_, Value}) -> Value end, Result).
-
-
-% Unused gen_server behaviour API functions that we need to declare.
-
-%% @doc Unused
handle_cast(foo, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
-%% @doc Unused
-terminate(_Reason, _State) -> ok.
-
-%% @doc Unused
-code_change(_OldVersion, State, _Extra) -> {ok, State}.
-
-
-%% Tests
-
--ifdef(TEST).
-% Internal API unit tests go here
-
-
--endif.
+code_change(_OldVersion, State, _Extra) ->
+ {ok, State}.
+
+
+make_key({Module, Key}) when is_integer(Key) ->
+ {Module, list_to_atom(integer_to_list(Key))};
+make_key(Key) ->
+ Key.
+
+abs_to_list() ->
+ SortedKVs = lists:sort(ets:tab2list(?ABS_TABLE)),
+ lists:foldl(fun({Key, Val}, Acc) ->
+ case Acc of
+ [] ->
+ [{Key, [Val]}];
+ [{Key, Prev} | Rest] ->
+ [{Key, [Val | Prev]} | Rest];
+ Others ->
+ [{Key, [Val]} | Others]
+ end
+ end, [], SortedKVs). \ No newline at end of file