summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorncshaw <ncshaw@ibm.com>2022-05-20 16:21:44 -0500
committerNoah Shaw <ncshaw@ibm.com>2022-07-22 15:05:03 -0500
commit963daf6ca9df5815973bb6934c3fdc02b905f6f9 (patch)
tree4b42b4994fb25943d52614960eea872f10fd91b8
parentfff03ef8e8491e609bd9159e0272fcebeeab1387 (diff)
downloadcouchdb-963daf6ca9df5815973bb6934c3fdc02b905f6f9.tar.gz
Implement view_report function
-rw-r--r--src/couch/src/couch_debug.erl80
-rw-r--r--src/couch_mrview/src/couch_mrview_debug.erl391
2 files changed, 459 insertions, 12 deletions
diff --git a/src/couch/src/couch_debug.erl b/src/couch/src/couch_debug.erl
index 9dc5053e6..93e11bf30 100644
--- a/src/couch/src/couch_debug.erl
+++ b/src/couch/src/couch_debug.erl
@@ -47,7 +47,9 @@
]).
-export([
- print_table/2
+ print_table/2,
+ print_report/1,
+ print_report_with_info_width/2
]).
-type throw(_Reason) :: no_return().
@@ -80,6 +82,8 @@ help() ->
print_linked_processes,
memory_info,
print_table,
+ print_report,
+ print_report_with_info_width,
restart,
restart_busy
].
@@ -287,15 +291,16 @@ help(print_linked_processes) ->
Print cluster of linked processes. This function receives the
initial Pid to start from. The function doesn't recurse to pids
- older than initial one. The output would look like similar to:
- ```
-couch_debug:print_linked_processes(whereis(couch_index_server)).
-name | reductions | message_queue_len | memory
-couch_index_server[<0.288.0>] | 478240 | 0 | 109696
- couch_index:init/1[<0.3520.22>] | 4899 | 0 | 109456
- couch_file:init/1[<0.886.22>] | 11973 | 0 | 67984
- couch_index:init/1[<0.3520.22>] | 4899 | 0 | 109456
- ```
+ older than initial one.
+
+ The output will look like similar to:
+
+ couch_debug:print_linked_processes(whereis(couch_index_server)).
+ name | reductions | message_queue_len | memory
+ couch_index_server[<0.288.0>] | 478240 | 0 | 109696
+ couch_index:init/1[<0.3520.22>] | 4899 | 0 | 109456
+ couch_file:init/1[<0.886.22>] | 11973 | 0 | 67984
+ couch_index:init/1[<0.3520.22>] | 4899 | 0 | 109456
---
", []);
@@ -328,6 +333,41 @@ help(print_table) ->
---
", []);
+help(print_report) ->
+ io:format("
+ print_report(Report)
+ --------------------------------
+
+ Print a report in table form.
+ - Report: List of {InfoKey, InfoVal} where each InfoKey is unique
+ (unlike print_table/2).
+
+ The output will look similar to:
+
+ |info | value
+ | btree_size | 51
+ | def | function(doc){emit(doc.id, 1);}
+ | id_num | 0
+ | options |
+ | purge_seq | 0
+ | reduce_funs |
+ | update_seq | 3
+
+ ---
+ ", []);
+help(print_report_with_info_width) ->
+ io:format("
+ print_report_with_info_width(Report, Width)
+ --------------------------------
+
+ Print a report in table form. Same as print_report/1 but with a custom
+ width for the InfoKey column.
+ - Report: List of {InfoKey, InfoVal} where each InfoKey is unique
+ (unlike print_table/2).
+ - Width: Width of InfoKey column in TableSpec. Default is 50.
+
+ ---
+ ", []);
help(print_tree) ->
io:format("
print_tree(Tree, TableSpec)
@@ -891,6 +931,26 @@ print_table(Rows, TableSpec) ->
),
ok.
+print_report(Report) ->
+ print_report_with_info_width(Report, 50).
+
+print_report_with_info_width(Report, Width) ->
+ TableSpec = [
+ {Width, left, info},
+ {100, right, value}
+ ],
+ io:format("~s~n", [format(TableSpec)]),
+ lists:map(
+ fun({InfoKey, Value}) ->
+ TableSpec1 = [
+ {Width, left, info},
+ {100, right, InfoKey}
+ ],
+ io:format("~s~n", [table_row(InfoKey, 2, [{InfoKey, Value}], TableSpec1)])
+ end,
+ Report
+ ).
+
print_tree(Tree, TableSpec) ->
io:format("~s~n", [format(TableSpec)]),
map_tree(Tree, fun(_, {Id, Props}, Pos) ->
diff --git a/src/couch_mrview/src/couch_mrview_debug.erl b/src/couch_mrview/src/couch_mrview_debug.erl
index a4203d49d..66f5d79d9 100644
--- a/src/couch_mrview/src/couch_mrview_debug.erl
+++ b/src/couch_mrview/src/couch_mrview_debug.erl
@@ -18,14 +18,30 @@
]).
-export([
- view_signature/2
+ view_signature/2,
+ index_state/1,
+ view_state/1,
+ view_state/2,
+ index_view_state/1,
+ index_view_state/2,
+ index_report/1,
+ view_report/1,
+ view_report/2,
+ index_view_report/1,
+ index_view_report/2
]).
-include_lib("couch_mrview/include/couch_mrview.hrl").
help() ->
[
- view_signature
+ view_signature,
+ index_state,
+ view_state,
+ index_view_state,
+ index_report,
+ view_report,
+ index_view_report
].
-spec help(Function :: atom()) -> ok.
@@ -34,7 +50,223 @@ help(view_signature) ->
io:format("
view_signature(ShardName, DDocName)
--------------
+
Returns a view signature for given ddoc for a given (non clustered) database.
+
+ ---
+ ", []);
+help(index_state) ->
+ io:format("
+ index_state(Pid)
+ --------------
+
+ Pid: Pid of couch_index:init/1, specifically an mrview index.
+
+ Returns a state map for an index that includes the following fields:
+ - collator_versions
+ - compact_running
+ - db_name
+ - idx_name
+ - language
+ - pending_updates
+ - purge_seq
+ - signature
+ - sizes
+ - update_options
+ - update_seq
+ - updater_running
+ - view_file_path
+ - waiting_clients
+ - waiting_commit
+
+ ---
+ ", []);
+help(view_state) ->
+ io:format("
+ view_state(PidOrIdxState)
+ view_state(Pid, ViewName)
+ view_state(IdxState, ViewName)
+ --------------
+
+ PidOrIdxState: Pid of couch_index:init/1, specifically an mrview index, or the state
+ of an mrview index.
+ Pid: Pid of couch_index:init/1, specifically an mrview index.
+ IdxState: State of an mrview index (#mrst{} record).
+ ViewName: Name of the view to be queried or undefined.
+
+ Returns a state map for a ViewName if specified or all views if not that includes the
+ following fields:
+ - btree_size
+ - def
+ - id_num
+ - options
+ - purge_seq
+ - reduce_funs
+ - update_seq
+
+ ---
+ ", []);
+help(index_view_state) ->
+ io:format("
+ index_view_state(Pid)
+ index_view_state(Pid, ViewName)
+ --------------
+
+ Pid: Pid of couch_index:init/1, specifically an mrview index.
+ ViewName: Name of the view to be queried or undefined.
+
+ Returns a state map that includes the index state returned by index_state/1 and the view
+ state returned by view_state/2. Like view_state/2, a ViewName can be specified or not.
+
+ ---
+ ", []);
+help(index_report) ->
+ io:format("
+ index_report(Pid)
+ --------------
+
+ Pid: Pid of couch_index:init/1, specifically an mrview index.
+
+ Prints a report for the index state of an mrview index that includes the following fields:
+ - signature
+ - db_name
+ - idx_name
+ - update_seq
+ - purge_seq
+ - view_file_path
+ - pending_updates
+
+ The output will look similar to:
+
+ |info | value
+ | collator_versions | 153.112
+ | compact_running | false
+ | db_name | shards/00000000-ffffffff/dbv1.1658179540
+ | idx_name | _design/dbv1ddoc
+ | language | javascript
+ | pending_updates | 0
+ | purge_seq | 0
+ | signature | a967fb72089e71e870f790f32bcc6a55
+ | sizes | {[{file,4264},{active,163},{external,51}]}
+ | update_options |
+ | update_seq | 3
+ | updater_running | false
+ | view_file_path | .shards/00000000-ffffffff/dbv1.1658179540_design/mrview/a967fb72089e71e870f790f32bcc6a55.view
+ | waiting_clients | 0
+ | waiting_commit | false
+
+ ---
+ ", []);
+help(view_report) ->
+ io:format("
+ view_report(PidOrIdxState)
+ view_report(Pid, ViewName)
+ view_report(IdxState, ViewName)
+ --------------
+
+ PidOrIdxState: Pid of couch_index:init/1, specifically an mrview index, or the state
+ of an mrview index.
+ Pid: Pid of couch_index:init/1, specifically an mrview index.
+ IdxState: State of an mrview index (#mrst{} record).
+ ViewName: Name of the view to be queried or undefined.
+
+ Prints a report for a ViewName if specified or all views if not that includes the following
+ fields:
+ - id_num
+ - update_seq
+ - purge_seq
+ - reduce_funs
+ - def
+ - btree_size
+ - options
+
+ The output will look similar to:
+
+ dbv1view
+ |info | value
+ | btree_size | 51
+ | def | function(doc){emit(doc.id, 1);}
+ | id_num | 0
+ | options |
+ | purge_seq | 0
+ | reduce_funs |
+ | update_seq | 3
+ dbv2view
+ |info | value
+ | btree_size | 50
+ | def | function(doc){emit(doc.id, 2);}
+ | id_num | 1
+ | options |
+ | purge_seq | 0
+ | reduce_funs | _sum
+ | update_seq | 3
+
+ ---
+ ", []);
+help(index_view_report) ->
+ io:format("
+ index_view_report(Pid)
+ index_view_report(Pid, ViewName)
+ --------------
+
+ Pid: Pid of couch_index:init/1, specifically an mrview index.
+ ViewName: Name of the view to be queried or undefined.
+
+ Prints a report for the index state and views of an mrview index. The report includes the following
+ fields for an index state:
+ - signature
+ - db_name
+ - idx_name
+ - update_seq
+ - purge_seq
+ - view_file_path
+ - pending_updates
+ The report also includes the following fields for a ViewName if specified or all views if not:
+ - id_num
+ - update_seq
+ - purge_seq
+ - reduce_funs
+ - def
+ - btree_size
+ - options
+
+ The output will look similar to:
+
+ |info | value
+ | collator_versions | 153.112
+ | compact_running | false
+ | db_name | shards/00000000-ffffffff/dbv1.1658179540
+ | idx_name | _design/dbv1ddoc
+ | language | javascript
+ | pending_updates | 0
+ | purge_seq | 0
+ | signature | a967fb72089e71e870f790f32bcc6a55
+ | sizes | {[{file,4264},{active,163},{external,51}]}
+ | update_options |
+ | update_seq | 3
+ | updater_running | false
+ | view_file_path | .shards/00000000-ffffffff/dbv1.1658179540_design/mrview/a967fb72089e71e870f790f32bcc6a55.view
+ | waiting_clients | 0
+ | waiting_commit | false
+ dbv1view
+ |info | value
+ | btree_size | 51
+ | def | function(doc){emit(doc.id, 1);}
+ | id_num | 0
+ | options |
+ | purge_seq | 0
+ | reduce_funs |
+ | update_seq | 3
+ dbv2view
+ |info | value
+ | btree_size | 50
+ | def | function(doc){emit(doc.id, 2);}
+ | id_num | 1
+ | options |
+ | purge_seq | 0
+ | reduce_funs | _sum
+ | update_seq | 3
+
---
", []);
help(Unknown) ->
@@ -48,3 +280,158 @@ view_signature(DbName, DDocName) ->
{ok, DDoc} = couch_db:open_doc_int(Db, <<"_design/", DDocName/binary>>, []),
{ok, IdxState} = couch_mrview_util:ddoc_to_mrst(DDocName, DDoc),
couch_util:to_hex(IdxState#mrst.sig).
+
+index_state(Pid) when is_pid(Pid) ->
+ {ok, IdxState} = couch_index:get_state(Pid, 0),
+ case IdxState of
+ #mrst{} ->
+ {ok, Info} = couch_index:get_info(Pid),
+ Sig = IdxState#mrst.sig,
+ DbName = IdxState#mrst.db_name,
+ State =
+ Info ++
+ [
+ {signature, Sig},
+ {db_name, DbName},
+ {idx_name, IdxState#mrst.idx_name},
+ {view_file_path, couch_mrview_util:index_file(DbName, Sig)}
+ ],
+ {ok, maps:from_list(State)};
+ _ ->
+ {error, not_mrview_index}
+ end.
+
+view_state(PidOrIdxState) ->
+ view_state(PidOrIdxState, undefined).
+
+view_state(Pid, ViewName) when is_pid(Pid) ->
+ {ok, IdxState} = couch_index:get_state(Pid, 0),
+ view_state(IdxState, ViewName);
+view_state(IdxState, ViewName) ->
+ case IdxState of
+ #mrst{} ->
+ MrViews = lists:foldl(
+ fun(MrView, Acc) ->
+ {Name, ReduceFuns} =
+ case MrView#mrview.reduce_funs of
+ [] ->
+ {hd(MrView#mrview.map_names), []};
+ _ ->
+ % reduce_funs contains tuples of {Name, ReduceFuns}
+ hd(MrView#mrview.reduce_funs)
+ end,
+ View = #{
+ id_num => MrView#mrview.id_num,
+ update_seq => MrView#mrview.update_seq,
+ purge_seq => MrView#mrview.purge_seq,
+ reduce_funs => ReduceFuns,
+ def => MrView#mrview.def,
+ btree_size => couch_btree:size(MrView#mrview.btree),
+ options => MrView#mrview.options
+ },
+ maps:put(Name, View, Acc)
+ end,
+ #{},
+ IdxState#mrst.views
+ ),
+ case ViewName of
+ undefined ->
+ {ok, MrViews};
+ _ ->
+ case maps:get(ViewName, MrViews) of
+ {badkey, Key} ->
+ io:format("No view named ~p was found.", [Key]),
+ {error, {badkey, Key}};
+ Value ->
+ {ok, #{ViewName => Value}}
+ end
+ end;
+ _ ->
+ {error, not_mrview_index}
+ end.
+
+index_view_state(Pid) when is_pid(Pid) ->
+ index_view_state(Pid, undefined).
+
+index_view_state(Pid, ViewName) when is_pid(Pid) ->
+ {ok, IdxState} = index_state(Pid),
+ {ok, ViewState} = view_state(Pid, ViewName),
+ {ok, maps:put(views, ViewState, IdxState)}.
+
+index_report(Pid) when is_pid(Pid) ->
+ case index_state(Pid) of
+ {ok, IdxState} ->
+ Sig = maps:get(signature, IdxState),
+ IdxState2 = maps:put(signature, couch_util:to_hex(Sig), IdxState),
+ % Convert collator versions to strings to print pretty
+ IdxState3 = convert_collator_versions_to_strings(IdxState2),
+ IdxState4 = maps:update_with(view_file_path, fun format_view_path/1, IdxState3),
+ couch_debug:print_report_with_info_width(maps:to_list(IdxState4), 21);
+ Error ->
+ Error
+ end.
+
+view_report(PidOrIdxState) ->
+ view_report(PidOrIdxState, undefined).
+
+view_report(Pid, ViewName) when is_pid(Pid) ->
+ {ok, IdxState} = couch_index:get_state(Pid, 0),
+ view_report(IdxState, ViewName);
+view_report(IdxState, ViewName) ->
+ case view_state(IdxState, ViewName) of
+ {ok, ViewState} ->
+ lists:foreach(
+ fun({Name, Info}) ->
+ io:format("~s~n", [binary_to_list(Name)]),
+ couch_debug:print_report_with_info_width(maps:to_list(Info), 15)
+ end,
+ maps:to_list(ViewState)
+ );
+ Error ->
+ Error
+ end.
+
+index_view_report(Pid) when is_pid(Pid) ->
+ index_view_report(Pid, undefined).
+index_view_report(Pid, ViewName) when is_pid(Pid) ->
+ case index_view_state(Pid) of
+ {ok, State} ->
+ AllViews = maps:get(views, State),
+ Views =
+ case ViewName of
+ undefined ->
+ AllViews;
+ _ ->
+ #{ViewName => maps:get(ViewName, AllViews)}
+ end,
+ Sig = maps:get(signature, State),
+ State2 = maps:put(signature, couch_util:to_hex(Sig), State),
+ % Convert collator versions to strings to print pretty
+ State3 = convert_collator_versions_to_strings(State2),
+ State4 = maps:update_with(view_file_path, fun format_view_path/1, State3),
+ couch_debug:print_report_with_info_width(
+ maps:to_list(maps:without([views], State4)), 21
+ ),
+ lists:foreach(
+ fun({Name, Info}) ->
+ io:format("~s~n", [binary_to_list(Name)]),
+ couch_debug:print_report_with_info_width(maps:to_list(Info), 15)
+ end,
+ maps:to_list(Views)
+ );
+ Error ->
+ Error
+ end.
+
+convert_collator_versions_to_strings(State) ->
+ CollatorVersions = lists:map(
+ fun(Version) ->
+ binary_to_list(Version)
+ end,
+ maps:get(collator_versions, State)
+ ),
+ maps:put(collator_versions, CollatorVersions, State).
+
+format_view_path(ViewFilePath) ->
+ BaseDir = config:get("couchdb", "view_index_dir"),
+ lists:flatten(string:replace(ViewFilePath, BaseDir ++ "/", "")).