You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ii...@apache.org on 2022/05/19 21:21:27 UTC
[couchdb] branch 3.x updated: Implement resource hoggers
This is an automated email from the ASF dual-hosted git repository.
iilyak pushed a commit to branch 3.x
in repository https://gitbox.apache.org/repos/asf/couchdb.git
The following commit(s) were added to refs/heads/3.x by this push:
new cf7a2275d Implement resource hoggers
new cc47dcd70 Merge pull request #3985 from noahshaw11/implement-resource_hoggers
cf7a2275d is described below
commit cf7a2275dbd361465779cbfba4b350c6cad66e4c
Author: ncshaw <nc...@ibm.com>
AuthorDate: Mon Apr 4 15:58:11 2022 -0500
Implement resource hoggers
---
src/couch/src/couch_debug.erl | 157 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 157 insertions(+)
diff --git a/src/couch/src/couch_debug.erl b/src/couch/src/couch_debug.erl
index 32549b922..7e90b9b07 100644
--- a/src/couch/src/couch_debug.erl
+++ b/src/couch/src/couch_debug.erl
@@ -35,6 +35,9 @@
print_linked_processes/1,
memory_info/1,
memory_info/2,
+ resource_hoggers/2,
+ resource_hoggers_snapshot/1,
+ analyze_resource_hoggers/2,
busy/2,
busy/3,
restart/1,
@@ -335,6 +338,63 @@ help(print_tree) ->
- TableSpec: List of either {Value} or {Width, Align, Value}
where Align is either left/center/right.
+ ---
+ ", []);
+help(resource_hoggers) ->
+ io:format("
+ resource_hoggers(MemoryInfo, InfoKey)
+ --------------------------------
+
+ Prints the top processes hogging resources along with the value associated with InfoKey.
+ - MemoryInfo: Data map containing values for a set of InfoKeys
+ (same structure returned by memory_info)
+ - InfoKey: Desired key to obtain value for. The supported keys are
+ binary, dictionary, heap_size, links, memory, message_queue_len, monitored_by,
+ monitors, stack_size, and total_heap_size
+
+ ---
+ ", []);
+help(resource_hoggers_snapshot) ->
+ io:format("
+ resource_hoggers_snapshot(MemoryInfo)
+ resource_hoggers_snapshot(PreviousSnapshot)
+ --------------------------------
+
+ Prints a snapshot of the top processes hogging resources.
+ - MemoryInfo: Data map containing values for a set of InfoKeys
+ (same structure returned by memory_info)
+ - PreviousSnapshot: Previous snapshot of resource hoggers
+
+ An example workflow is to call `memory_info(Pids)` and pass it as a first snapshot into
+ `resource_hoggers_snapshot/1`. Then, periodically call `resource_hoggers_snapshot/1` and pass in
+ the previous snapshot.
+
+ Here is an example use case:
+ ```
+ S0 = couch_debug:memory_info(erlang:processes()).
+ Summary = lists:foldl(fun(I, S) ->
+ timer:sleep(1000),
+ io:format(\"Snapshot ~~p/10~~n\", [I]),
+ couch_debug:resource_hoggers_snapshot(S)
+ end, S0, lists:seq(1, 10)).
+ couch_debug:analyze_resource_hoggers(Summary, 10).
+ ```
+
+ ---
+ ", []);
+help(analyze_resource_hoggers) ->
+ io:format("
+ analyze_resource_hoggers(Snapshot, TopN)
+ --------------------------------
+
+ Analyzes the TopN processes hogging resources along with the values associated with InfoKeys.
+ - Snapshot: Snapshot of resource hoggers
+ - TopN: Number of top processes to include in result
+
+ An example workflow is to call `resource_hoggers_snapshot(memory_info(Pids))` and pass this to `analyze_resource_hoggers/2`
+ along with the number of top processes to include in result, TopN. See `couch_debug:help(resource_hoggers_snapshot)` for an
+ example and more info.
+
---
", []);
help(Unknown) ->
@@ -620,6 +680,103 @@ info_size(InfoKV) ->
{binary, BinInfos} -> lists:sum([S || {_, S, _} <- BinInfos]);
{_, V} -> V
end.
+resource_hoggers(MemoryInfo, InfoKey) ->
+ KeyFun = fun
+ ({_Pid, _Id, undefined}) -> undefined;
+ ({_Pid, Id, DataMap}) -> {Id, [{InfoKey, maps:get(InfoKey, DataMap)}]}
+ end,
+ resource_hoggers(MemoryInfo, InfoKey, KeyFun).
+
+resource_hoggers(MemoryInfo, InfoKey, KeyFun) ->
+ HoggersData = resource_hoggers_data(MemoryInfo, InfoKey, KeyFun),
+ TableSpec = [
+ {50, centre, id},
+ {20, centre, InfoKey}
+ ],
+ print_table(HoggersData, TableSpec).
+
+resource_hoggers_data(MemoryInfo, InfoKey, KeyFun) when is_atom(InfoKey) ->
+ resource_hoggers_data(MemoryInfo, InfoKey, KeyFun, 20).
+
+resource_hoggers_data(MemoryInfo, InfoKey, KeyFun, N) when is_atom(InfoKey) and is_integer(N) ->
+ SortedTuples = resource_hoggers_data(MemoryInfo, InfoKey, KeyFun, undefined),
+ {TopN, _} = lists:split(N, SortedTuples),
+ TopN;
+resource_hoggers_data(MemoryInfo, InfoKey, KeyFun, undefined) when is_atom(InfoKey) ->
+ Tuples = lists:filtermap(
+ fun(Tuple) ->
+ case KeyFun(Tuple) of
+ undefined ->
+ false;
+ Value ->
+ {true, Value}
+ end
+ end,
+ MemoryInfo
+ ),
+ lists:reverse(
+ lists:sort(
+ fun({_, A}, {_, B}) ->
+ lists:keyfind(InfoKey, 1, A) < lists:keyfind(InfoKey, 1, B)
+ end,
+ Tuples
+ )
+ ).
+
+resource_hoggers_snapshot({N, MemoryInfo, InfoKeys} = _Snapshot) ->
+ Data = lists:filtermap(
+ fun({Pid, Id, Data}) ->
+ case memory_info(Pid, InfoKeys) of
+ {Pid, undefined, undefined} ->
+ false;
+ {_, _, DataMap} ->
+ {true, {Pid, Id, update_delta(Data, DataMap)}}
+ end
+ end,
+ MemoryInfo
+ ),
+ {N + 1, Data, InfoKeys};
+resource_hoggers_snapshot([]) ->
+ [];
+resource_hoggers_snapshot([{_Pid, _Id, Data} | _] = MemoryInfo) ->
+ resource_hoggers_snapshot({0, MemoryInfo, maps:keys(Data)}).
+
+update_delta({_, InitialDataMap}, DataMap) ->
+ update_delta(InitialDataMap, DataMap);
+update_delta(InitialDataMap, DataMap) ->
+ Delta = maps:fold(
+ fun(Key, Value, AccIn) ->
+ maps:put(Key, maps:get(Key, DataMap, Value) - Value, AccIn)
+ end,
+ maps:new(),
+ InitialDataMap
+ ),
+ {Delta, InitialDataMap}.
+
+analyze_resource_hoggers({N, Data, InfoKeys}, TopN) ->
+ io:format("Number of snapshots: ~p~n", [N]),
+ lists:map(
+ fun(InfoKey) ->
+ KeyFun = fun
+ ({_Pid, _Id, undefined}) ->
+ undefined;
+ ({_Pid, Id, {Delta, DataMap}}) ->
+ {Id, [
+ {InfoKey, maps:get(InfoKey, DataMap)},
+ {delta, maps:get(InfoKey, Delta)}
+ ]}
+ end,
+ io:format("Top ~p by change in ~p~n", [TopN, InfoKey]),
+ HoggersData = resource_hoggers_data(Data, delta, KeyFun, TopN),
+ TableSpec = [
+ {50, centre, id},
+ {20, right, InfoKey},
+ {20, right, delta}
+ ],
+ print_table(HoggersData, TableSpec)
+ end,
+ InfoKeys
+ ).
id("couch_file:init" ++ _, Pid, _Props) ->
case couch_file:process_info(Pid) of