This is an automated email from the ASF dual-hosted git repository. iilyak pushed a commit to branch couch-stats-resource-tracker-v3-rebase-http-2 in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 9264f5af8629c6e082fe3264765bc78866406c8d Author: ILYA Khlopotov <iil...@apache.org> AuthorDate: Wed Jun 25 07:14:27 2025 -0700 Factor out csrt_entry.erl --- src/couch_stats/src/csrt_entry.erl | 60 ++++++++++++++++++++++++ src/couch_stats/src/csrt_httpd.erl | 23 +-------- src/couch_stats/src/csrt_query.erl | 28 +---------- src/couch_stats/src/csrt_util.erl | 41 +--------------- src/couch_stats/test/eunit/csrt_logger_tests.erl | 14 +++--- src/couch_stats/test/eunit/csrt_server_tests.erl | 4 +- 6 files changed, 75 insertions(+), 95 deletions(-) diff --git a/src/couch_stats/src/csrt_entry.erl b/src/couch_stats/src/csrt_entry.erl new file mode 100644 index 000000000..3b1cf3f50 --- /dev/null +++ b/src/couch_stats/src/csrt_entry.erl @@ -0,0 +1,60 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(csrt_entry). + +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("couch_stats_resource_tracker.hrl"). + +-export([ + value/2, + key/1 +]). + +-spec value(#rctx{}, rctx_field()) -> any(). + +value(#rctx{pid_ref = Val}, pid_ref) -> Val; +value(#rctx{nonce = Val}, nonce) -> Val; +value(#rctx{type = Val}, type) -> csrt_util:convert_type(Val); +value(#rctx{dbname = Val}, dbname) -> Val; +value(#rctx{username = Val}, username) -> Val; +value(#rctx{db_open = Val}, db_open) -> Val; +value(#rctx{docs_read = Val}, docs_read) -> Val; +value(#rctx{docs_written = Val}, docs_written) -> Val; +value(#rctx{rows_read = Val}, rows_read) -> Val; +value(#rctx{changes_returned = Val}, changes_returned) -> Val; +value(#rctx{ioq_calls = Val}, ioq_calls) -> Val; +value(#rctx{js_filter = Val}, js_filter) -> Val; +value(#rctx{js_filtered_docs = Val}, js_filtered_docs) -> Val; +value(#rctx{get_kv_node = Val}, get_kv_node) -> Val; +value(#rctx{get_kp_node = Val}, get_kp_node) -> Val; +value(#rctx{started_at = Val}, started_at) -> Val; +value(#rctx{updated_at = Val}, updated_at) -> Val. + +-spec key(BinKey :: binary() | string()) -> Key :: rctx_field() + | throw({bad_request, Reason :: binary()}). + +key(<<"pid_ref">>) -> pid_ref; +key(<<"nonce">>) -> nonce; +key(<<"type">>) -> type; +key(<<"dbname">>) -> dbname; +key(<<"username">>) -> username; +key(<<"db_open">>) -> db_open; +key(<<"docs_read">>) -> docs_read; +key(<<"rows_read">>) -> rows_read; +key(<<"changes_returned">>) -> changes_returned; +key(<<"ioq_calls">>) -> ioq_calls; +key(<<"js_filter">>) -> js_filter; +key(<<"js_filtered_docs">>) -> js_filtered_docs; +key(<<"get_kv_node">>) -> get_kv_node; +key(<<"get_kp_node">>) -> get_kp_node; +key(Other) when is_binary(Other) -> throw({bad_request, <<"Invalid key '", Other/binary, "'">>}). diff --git a/src/couch_stats/src/csrt_httpd.erl b/src/couch_stats/src/csrt_httpd.erl index e4eb13fd2..5c260d596 100644 --- a/src/couch_stats/src/csrt_httpd.erl +++ b/src/couch_stats/src/csrt_httpd.erl @@ -129,37 +129,18 @@ query_matcher(MatcherName, AggregationKey, CounterKey) -> csrt_query:query_matcher(Matcher, AggregationKey, CounterKey) end. --spec to_key(BinKey :: binary() | string()) -> Key :: rctx_field() - | throw({bad_request, Reason :: binary()}). - -to_key(<<"pid_ref">>) -> pid_ref; -to_key(<<"nonce">>) -> nonce; -to_key(<<"type">>) -> type; -to_key(<<"dbname">>) -> dbname; -to_key(<<"username">>) -> username; -to_key(<<"db_open">>) -> db_open; -to_key(<<"docs_read">>) -> docs_read; -to_key(<<"rows_read">>) -> rows_read; -to_key(<<"changes_returned">>) -> changes_returned; -to_key(<<"ioq_calls">>) -> ioq_calls; -to_key(<<"js_filter">>) -> js_filter; -to_key(<<"js_filtered_docs">>) -> js_filtered_docs; -to_key(<<"get_kv_node">>) -> get_kv_node; -to_key(<<"get_kp_node">>) -> get_kp_node; -to_key(Other) when is_binary(Other) -> throw({bad_request, <<"Invalid key '", Other/binary, "'">>}). - -spec parse_key(Keys :: binary() | [binary()]) -> [rctx_field()] | throw({bad_request, Reason :: binary()}). parse_key(Keys) when is_list(Keys) -> parse_key(Keys, []); parse_key(BinKey) when is_binary(BinKey) -> - to_key(BinKey); + csrt_entry:key(BinKey); parse_key(undefined) -> undefined. parse_key([BinKey | Rest], Keys) -> - parse_key(Rest, [to_key(BinKey) | Keys]); + parse_key(Rest, [csrt_entry:key(BinKey) | Keys]); parse_key([], Keys) -> lists:reverse(Keys). diff --git a/src/couch_stats/src/csrt_query.erl b/src/couch_stats/src/csrt_query.erl index 97a0be751..a512366a9 100644 --- a/src/couch_stats/src/csrt_query.erl +++ b/src/couch_stats/src/csrt_query.erl @@ -92,32 +92,8 @@ find_by_pidref(PidRef) -> find_workers_by_pidref(PidRef) -> csrt_server:match_resource(#rctx{type = #rpc_worker{from = PidRef}}). -field(#rctx{pid_ref = Val}, pid_ref) -> Val; -%% NOTE: Pros and cons to doing these convert functions here -%% Ideally, this would be done later so as to prefer the core data structures -%% as long as possible, but we currently need the output of this function to -%% be jiffy:encode'able. The tricky bit is dynamically encoding the group_by -%% structure provided by the caller of *_by aggregator functions below. -%% For now, we just always return jiffy:encode'able data types. -field(#rctx{nonce = Val}, nonce) -> Val; -field(#rctx{type = Val}, type) -> csrt_util:convert_type(Val); -field(#rctx{dbname = Val}, dbname) -> Val; -field(#rctx{username = Val}, username) -> Val; -field(#rctx{db_open = Val}, db_open) -> Val; -field(#rctx{docs_read = Val}, docs_read) -> Val; -field(#rctx{docs_written = Val}, docs_written) -> Val; -field(#rctx{rows_read = Val}, rows_read) -> Val; -field(#rctx{changes_returned = Val}, changes_returned) -> Val; -field(#rctx{ioq_calls = Val}, ioq_calls) -> Val; -field(#rctx{js_filter = Val}, js_filter) -> Val; -field(#rctx{js_filtered_docs = Val}, js_filtered_docs) -> Val; -field(#rctx{get_kv_node = Val}, get_kv_node) -> Val; -field(#rctx{get_kp_node = Val}, get_kp_node) -> Val; -field(#rctx{started_at = Val}, started_at) -> Val; -field(#rctx{updated_at = Val}, updated_at) -> Val. - curry_field(Field) -> - fun(Ele) -> field(Ele, Field) end. + fun(Ele) -> csrt_entry:value(Ele, Field) end. count_by(KeyFun) -> csrt_query:count_by(all(), KeyFun). @@ -167,7 +143,7 @@ all() -> %% eg: group_by(all(), [username, dbname, mfa], ioq_calls). %% eg: group_by(all(), [username, dbname, mfa], js_filters). group_by(Matcher, KeyL, ValFun, AggFun, Limit) when is_list(KeyL) -> - KeyFun = fun(Ele) -> list_to_tuple([field(Ele, Key) || Key <- KeyL]) end, + KeyFun = fun(Ele) -> list_to_tuple([csrt_entry:value(Ele, Key) || Key <- KeyL]) end, group_by(Matcher, KeyFun, ValFun, AggFun, Limit); group_by(Matcher, Key, ValFun, AggFun, Limit) when is_atom(Key) -> group_by(Matcher, curry_field(Key), ValFun, AggFun, Limit); diff --git a/src/couch_stats/src/csrt_util.erl b/src/couch_stats/src/csrt_util.erl index c19c10d6f..4da5dc610 100644 --- a/src/couch_stats/src/csrt_util.erl +++ b/src/couch_stats/src/csrt_util.erl @@ -58,7 +58,6 @@ set_fabric_init_p/2, set_fabric_init_p/3, map_to_rctx/1, - field/2, rctx_record_info/0 ]). @@ -276,42 +275,6 @@ map_to_rctx_field(_, _, Rctx) -> %% Unknown key, could throw but just move on Rctx. --spec field(Field :: rctx_field(), Rctx :: rctx()) -> any(). -field(updated_at, #rctx{updated_at = Val}) -> - Val; -field(started_at, #rctx{started_at = Val}) -> - Val; -field(pid_ref, #rctx{pid_ref = Val}) -> - Val; -field(nonce, #rctx{nonce = Val}) -> - Val; -field(dbname, #rctx{dbname = Val}) -> - Val; -field(username, #rctx{username = Val}) -> - Val; -field(db_open, #rctx{db_open = Val}) -> - Val; -field(docs_read, #rctx{docs_read = Val}) -> - Val; -field(docs_written, #rctx{docs_written = Val}) -> - Val; -field(js_filter, #rctx{js_filter = Val}) -> - Val; -field(js_filtered_docs, #rctx{js_filtered_docs = Val}) -> - Val; -field(rows_read, #rctx{rows_read = Val}) -> - Val; -field(type, #rctx{type = Val}) -> - Val; -field(get_kp_node, #rctx{get_kp_node = Val}) -> - Val; -field(get_kv_node, #rctx{get_kv_node = Val}) -> - Val; -field(changes_returned, #rctx{changes_returned = Val}) -> - Val; -field(ioq_calls, #rctx{ioq_calls = Val}) -> - Val. - -spec add_delta(T :: term(), Delta :: maybe_delta()) -> term_delta(). add_delta(T, undefined) -> T; @@ -520,10 +483,10 @@ t_should_not_track_init_p(_) -> t_should_extract_fields_properly(_) -> Rctx = #rctx{}, #{fields := Fields} = rctx_record_info(), - %% field/2 throws on invalid fields, assert that the function succeeded + %% csrt_entry:value/2 throws on invalid fields, assert that the function succeeded TestField = fun(Field) -> try - field(Field, Rctx), + csrt_entry:value(Rctx, Field), true catch _:_ -> false diff --git a/src/couch_stats/test/eunit/csrt_logger_tests.erl b/src/couch_stats/test/eunit/csrt_logger_tests.erl index 8d781de76..74c88590b 100644 --- a/src/couch_stats/test/eunit/csrt_logger_tests.erl +++ b/src/couch_stats/test/eunit/csrt_logger_tests.erl @@ -288,8 +288,8 @@ t_matcher_on_changes_processed(#{rctxs := Rctxs0}) -> %% Make sure we have at least one match Rctxs = [rctx_gen(#{rows_read => Threshold + 10}) | Rctxs0], ChangesFilter = fun(R) -> - Ret = csrt_util:field(changes_returned, R), - Proc = csrt_util:field(rows_read, R), + Ret = csrt_entry:value(R, changes_returned), + Proc = csrt_entry:value(R, rows_read), (Proc - Ret) >= Threshold end, ?assertEqual( @@ -309,8 +309,8 @@ t_matcher_on_long_reqs(#{rctxs := Rctxs0}) -> UpdatedAt = Now - round(NativeThreshold * 1.23), Rctxs = [rctx_gen(#{started_at => Now, updated_at => UpdatedAt}) | Rctxs0], DurationFilter = fun(R) -> - Started = csrt_util:field(started_at, R), - Updated = csrt_util:field(updated_at, R), + Started = csrt_entry:value(R, started_at), + Updated = csrt_entry:value(R, updated_at), abs(Updated - Started) >= NativeThreshold end, ?assertEqual( @@ -472,7 +472,7 @@ matcher_on(Field, Value) -> matcher_for(Field, Value, fun erlang:'=:='/2). matcher_for(Field, Value, Op) -> - fun(Rctx) -> Op(csrt_util:field(Field, Rctx), Value) end. + fun(Rctx) -> Op(csrt_entry:value(Rctx, Field), Value) end. matcher_for_csrt(MatcherName) -> Matchers = #{MatcherName => {_, _} = csrt_logger:get_matcher(MatcherName)}, @@ -487,9 +487,9 @@ matcher_for_csrt(MatcherName) -> matcher_for_dbname_io(Dbname0, Threshold) -> Dbname = list_to_binary(Dbname0), fun(Rctx) -> - DbnameA = csrt_util:field(dbname, Rctx), + DbnameA = csrt_entry:value(Rctx, dbname), Fields = [ioq_calls, get_kv_node, get_kp_node, docs_read, rows_read], - Vals = [{F, csrt_util:field(F, Rctx)} || F <- Fields], + Vals = [{F, csrt_entry:value(Rctx, F)} || F <- Fields], Dbname =:= mem3:dbname(DbnameA) andalso lists:any(fun({_K, V}) -> V >= Threshold end, Vals) end. diff --git a/src/couch_stats/test/eunit/csrt_server_tests.erl b/src/couch_stats/test/eunit/csrt_server_tests.erl index 04203b6aa..732c4fa3f 100644 --- a/src/couch_stats/test/eunit/csrt_server_tests.erl +++ b/src/couch_stats/test/eunit/csrt_server_tests.erl @@ -278,8 +278,8 @@ t_updated_at({_Ctx, DbName, _View}) -> docs_written => 0, pid_ref => PidRef }), - Started = csrt_util:field(started_at, RawRctx), - Updated = csrt_util:field(updated_at, RawRctx), + Started = csrt_entry:value(RawRctx, started_at), + Updated = csrt_entry:value(RawRctx, updated_at), ?assert( csrt_util:make_dt(Started, Updated, millisecond) > TimeDelay, "updated_at gets updated with an expected TimeDelay"