summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Holley <willholley@gmail.com>2020-01-06 11:49:41 +0000
committerWill Holley <willholley@gmail.com>2020-01-17 13:54:34 +0000
commit1cacc76ef1166fdb9e60fae173fd310ffb09139b (patch)
treed2e7d70ee38914e8d81d4c79a02311421ddb4ec4
parent5d55e289760959eb2fc228acb2ed00d57ddd4dee (diff)
downloadcouchdb-1cacc76ef1166fdb9e60fae173fd310ffb09139b.tar.gz
Instrument Mango execution stats
Adds metrics for mango execution statistics: * total docs examined (counter). Note that for non-quorum doc reads this needs to be counted at the shard level and not be the mango query coordinator * total results returned (counter) * query time (histogram) * mango:selector evaluations (may be queries or _changes feeds) For mrview-based queries which don't use quorum reads (the default), total docs examined should equal mango:selector evaluations. However, there are cases where mango selectors are evaluated outside of query time e.g. partial indexing, selector-based _changes which make it useful to maintain a distinct counter.
-rw-r--r--src/couch/priv/stats_descriptions.cfg20
-rw-r--r--src/mango/src/mango_cursor_text.erl1
-rw-r--r--src/mango/src/mango_cursor_view.erl2
-rw-r--r--src/mango/src/mango_execution_stats.erl9
-rw-r--r--src/mango/src/mango_selector.erl18
5 files changed, 40 insertions, 10 deletions
diff --git a/src/couch/priv/stats_descriptions.cfg b/src/couch/priv/stats_descriptions.cfg
index 557ac36b8..7c8fd94cb 100644
--- a/src/couch/priv/stats_descriptions.cfg
+++ b/src/couch/priv/stats_descriptions.cfg
@@ -310,3 +310,23 @@
{type, counter},
{desc, <<"number of mango queries that generated an index scan warning">>}
]}.
+{[mango, docs_examined], [
+ {type, counter},
+ {desc, <<"number of documents examined by mango queries coordinated by this node">>}
+]}.
+{[mango, quorum_docs_examined], [
+ {type, counter},
+ {desc, <<"number of documents examined by mango queries, using cluster quorum">>}
+]}.
+{[mango, results_returned], [
+ {type, counter},
+ {desc, <<"number of rows returned by mango queries">>}
+]}.
+{[mango, query_time], [
+ {type, histogram},
+ {desc, <<"length of time processing a mango query">>}
+]}.
+{[mango, evaluate_selector], [
+ {type, counter},
+ {desc, <<"number of mango selector evaluations">>}
+]}.
diff --git a/src/mango/src/mango_cursor_text.erl b/src/mango/src/mango_cursor_text.erl
index 6b202daab..2b42c3995 100644
--- a/src/mango/src/mango_cursor_text.erl
+++ b/src/mango/src/mango_cursor_text.erl
@@ -184,6 +184,7 @@ handle_hit(CAcc0, Sort, Doc) ->
} = CAcc0,
CAcc1 = update_bookmark(CAcc0, Sort),
Stats1 = mango_execution_stats:incr_docs_examined(Stats),
+ couch_stats:increment_counter([mango, docs_examined]),
CAcc2 = CAcc1#cacc{execution_stats = Stats1},
case mango_selector:match(CAcc2#cacc.selector, Doc) of
true when Skip > 0 ->
diff --git a/src/mango/src/mango_cursor_view.erl b/src/mango/src/mango_cursor_view.erl
index 256946900..240ef501d 100644
--- a/src/mango/src/mango_cursor_view.erl
+++ b/src/mango/src/mango_cursor_view.erl
@@ -249,6 +249,7 @@ view_cb({row, Row}, #mrargs{extra = Options} = Acc) ->
Doc ->
put(mango_docs_examined, get(mango_docs_examined) + 1),
Selector = couch_util:get_value(selector, Options),
+ couch_stats:increment_counter([mango, docs_examined]),
case mango_selector:match(Selector, Doc) of
true ->
ok = rexi:stream2(ViewRow),
@@ -433,6 +434,7 @@ doc_member(Cursor, RowProps) ->
% an undefined doc was returned, indicating we should
% perform a quorum fetch
ExecutionStats1 = mango_execution_stats:incr_quorum_docs_examined(ExecutionStats),
+ couch_stats:increment_counter([mango, quorum_docs_examined]),
Id = couch_util:get_value(id, RowProps),
case mango_util:defer(fabric, open_doc, [Db, Id, Opts]) of
{ok, #doc{}=DocProps} ->
diff --git a/src/mango/src/mango_execution_stats.erl b/src/mango/src/mango_execution_stats.erl
index 7e8afd782..5878a3190 100644
--- a/src/mango/src/mango_execution_stats.erl
+++ b/src/mango/src/mango_execution_stats.erl
@@ -62,6 +62,7 @@ incr_quorum_docs_examined(Stats) ->
incr_results_returned(Stats) ->
+ couch_stats:increment_counter([mango, results_returned]),
Stats#execution_stats {
resultsReturned = Stats#execution_stats.resultsReturned + 1
}.
@@ -81,11 +82,13 @@ log_end(Stats) ->
}.
-maybe_add_stats(Opts, UserFun, Stats, UserAcc) ->
+maybe_add_stats(Opts, UserFun, Stats0, UserAcc) ->
+ Stats1 = log_end(Stats0),
+ couch_stats:update_histogram([mango, query_time], Stats1#execution_stats.executionTimeMs),
+
case couch_util:get_value(execution_stats, Opts) of
true ->
- Stats0 = log_end(Stats),
- JSONValue = to_json(Stats0),
+ JSONValue = to_json(Stats1),
Arg = {add_key, execution_stats, JSONValue},
{_Go, FinalUserAcc} = UserFun(Arg, UserAcc),
FinalUserAcc;
diff --git a/src/mango/src/mango_selector.erl b/src/mango/src/mango_selector.erl
index 005a6afb3..3ea83c220 100644
--- a/src/mango/src/mango_selector.erl
+++ b/src/mango/src/mango_selector.erl
@@ -52,15 +52,19 @@ normalize(Selector) ->
% Match a selector against a #doc{} or EJSON value.
% This assumes that the Selector has been normalized.
% Returns true or false.
+match(Selector, D) ->
+ couch_stats:increment_counter([mango, evaluate_selector]),
+ match_int(Selector, D).
+
% An empty selector matches any value.
-match({[]}, _) ->
+match_int({[]}, _) ->
true;
-match(Selector, #doc{body=Body}) ->
+match_int(Selector, #doc{body=Body}) ->
match(Selector, Body, fun mango_json:cmp/2);
-match(Selector, {Props}) ->
+match_int(Selector, {Props}) ->
match(Selector, {Props}, fun mango_json:cmp/2).
% Convert each operator into a normalized version as well
@@ -582,7 +586,7 @@ match({[_, _ | _] = _Props} = Sel, _Value, _Cmp) ->
erlang:error({unnormalized_selector, Sel}).
-% Returns true if Selector requires all
+% Returns true if Selector requires all
% fields in RequiredFields to exist in any matching documents.
% For each condition in the selector, check
@@ -612,13 +616,13 @@ has_required_fields_int(Selector, RequiredFields) when not is_list(Selector) ->
% We can "see" through $and operator. Iterate
% through the list of child operators.
-has_required_fields_int([{[{<<"$and">>, Args}]}], RequiredFields)
+has_required_fields_int([{[{<<"$and">>, Args}]}], RequiredFields)
when is_list(Args) ->
has_required_fields_int(Args, RequiredFields);
% We can "see" through $or operator. Required fields
% must be covered by all children.
-has_required_fields_int([{[{<<"$or">>, Args}]} | Rest], RequiredFields)
+has_required_fields_int([{[{<<"$or">>, Args}]} | Rest], RequiredFields)
when is_list(Args) ->
Remainder0 = lists:foldl(fun(Arg, Acc) ->
% for each child test coverage against the full
@@ -635,7 +639,7 @@ has_required_fields_int([{[{<<"$or">>, Args}]} | Rest], RequiredFields)
% Handle $and operator where it has peers. Required fields
% can be covered by any child.
-has_required_fields_int([{[{<<"$and">>, Args}]} | Rest], RequiredFields)
+has_required_fields_int([{[{<<"$and">>, Args}]} | Rest], RequiredFields)
when is_list(Args) ->
Remainder = has_required_fields_int(Args, RequiredFields),
has_required_fields_int(Rest, Remainder);