This is an automated email from the ASF dual-hosted git repository.
chewbranca pushed a commit to branch couch-stats-resource-tracker-v3
in repository https://gitbox.apache.org/repos/asf/couchdb.git
The following commit(s) were added to
refs/heads/couch-stats-resource-tracker-v3 by this push:
new 64456e0ce Sort out core CSRT tests
64456e0ce is described below
commit 64456e0ce737abaccc4adae9ba6630f1a55740aa
Author: Russell Branca <[email protected]>
AuthorDate: Mon Mar 17 14:22:25 2025 -0700
Sort out core CSRT tests
---
src/couch/src/couch_query_servers.erl | 6 +-
.../src/couch_stats_resource_tracker.hrl | 1 +
src/couch_stats/src/csrt.erl | 8 +
src/couch_stats/test/eunit/csrt_server_tests.erl | 299 +++++++++++++++------
4 files changed, 235 insertions(+), 79 deletions(-)
diff --git a/src/couch/src/couch_query_servers.erl
b/src/couch/src/couch_query_servers.erl
index b5ddee312..2d5185806 100644
--- a/src/couch/src/couch_query_servers.erl
+++ b/src/couch/src/couch_query_servers.erl
@@ -555,9 +555,9 @@ filter_docs_int(Db, DDoc, FName, JsonReq, JsonDocs) ->
%% Count usage in _int version as this can be repeated for OS error
%% Pros & cons... might not have actually processed `length(JsonDocs)` docs
%% but it certainly undercounts if we count in `filter_docs/5` above
- %% TODO: wire in csrt tracking
- couch_stats:increment_counter([couchdb, query_server, js_filter]),
- couch_stats:increment_counter([couchdb, query_server, js_filtered_docs],
length(JsonDocs)),
+ %% TODO: replace with couchdb.query_server.*.ddoc_filter stats once we can
+ %% funnel back the stats used in the couchjs process to this caller process
+ csrt:js_filtered(length(JsonDocs)),
[true, Passes] = ddoc_prompt(
Db,
DDoc,
diff --git a/src/couch_stats/src/couch_stats_resource_tracker.hrl
b/src/couch_stats/src/couch_stats_resource_tracker.hrl
index 238b56a0e..998621bfb 100644
--- a/src/couch_stats/src/couch_stats_resource_tracker.hrl
+++ b/src/couch_stats/src/couch_stats_resource_tracker.hrl
@@ -48,6 +48,7 @@
[couchdb, btree, get_node, kv_node] => ?COUCH_BT_GET_KV_NODE,
[couchdb, btree, write_node, kp_node] => ?COUCH_BT_WRITE_KP_NODE,
[couchdb, btree, write_node, kv_node] => ?COUCH_BT_WRITE_KV_NODE,
+ %% NOTE: these stats are not local to the RPC worker, need forwarding
[couchdb, query_server, calls, ddoc_filter] => ?COUCH_JS_FILTER,
[couchdb, query_server, volume, ddoc_filter] => ?COUCH_JS_FILTERED_DOCS
}).
diff --git a/src/couch_stats/src/csrt.erl b/src/couch_stats/src/csrt.erl
index 932d6878b..f66755c4c 100644
--- a/src/couch_stats/src/csrt.erl
+++ b/src/couch_stats/src/csrt.erl
@@ -61,6 +61,7 @@
inc/1,
inc/2,
ioq_called/0,
+ js_filtered/1,
make_delta/0,
rctx_delta/2,
maybe_add_delta/1,
@@ -322,6 +323,13 @@ should_track(_Metric) ->
ioq_called() ->
inc(ioq_calls).
+%% we cannot yet use stats couchdb.query_server.*.ddoc_filter because those
+%% are collected in a dedicated process.
+%% TODO: funnel back stats from background worker processes to the RPC worker
+js_filtered(N) ->
+ inc(js_filter),
+ inc(js_filtered_docs, N).
+
docs_written(N) ->
inc(docs_written, N).
diff --git a/src/couch_stats/test/eunit/csrt_server_tests.erl
b/src/couch_stats/test/eunit/csrt_server_tests.erl
index fc4e6198b..f3cf07a83 100644
--- a/src/couch_stats/test/eunit/csrt_server_tests.erl
+++ b/src/couch_stats/test/eunit/csrt_server_tests.erl
@@ -17,6 +17,7 @@
-include_lib("couch_mrview/include/couch_mrview.hrl").
-define(DOCS_COUNT, 100).
+-define(DDOCS_COUNT, 1).
-define(DB_Q, 8).
-define(DEBUG_ENABLED, false).
@@ -32,130 +33,179 @@ csrt_context_test_() ->
])
}.
-csrt_fabric_test_() ->
+test_funs() ->
+ [
+ ?TDEF_FE(t_all_docs_include_false),
+ ?TDEF_FE(t_all_docs_include_true),
+ ?TDEF_FE(t_all_docs_limit_zero),
+ ?TDEF_FE(t_get_doc),
+ ?TDEF_FE(t_put_doc),
+ ?TDEF_FE(t_delete_doc),
+ ?TDEF_FE(t_update_docs),
+ ?TDEF_FE(t_changes),
+ ?TDEF_FE(t_changes_limit_zero),
+ ?TDEF_FE(t_changes_filtered),
+ ?TDEF_FE(t_view_query),
+ ?TDEF_FE(t_view_query_include_docs)
+ ].
+
+ddoc_test_funs() ->
+ [
+ ?TDEF_FE(t_changes_js_filtered)
+ | test_funs()
+ ].
+
+csrt_fabric_no_ddoc_test_() ->
{
- setup,
+ "CSRT fabric tests with no DDoc present",
+ foreach,
fun setup/0,
fun teardown/1,
- with([
- ?TDEF(t_all_docs_include_false),
- ?TDEF(t_all_docs_include_true),
- ?TDEF(t_all_docs_limit_zero),
- ?TDEF(t_get_doc),
- ?TDEF(t_put_doc),
- ?TDEF(t_delete_doc),
- ?TDEF(t_update_docs),
- ?TDEF(t_changes),
- ?TDEF(t_changes_limit_zero),
- ?TDEF(t_changes_filtered),
- ?TDEF(t_changes_js_filtered),
- ?TDEF(t_view_query)
- ])
+ test_funs()
}.
+csrt_fabric_test_() ->
+ {
+ "CSRT fabric tests with a DDoc present",
+ foreach,
+ fun() -> setup_ddoc(<<"_design/foo">>, <<"bar">>) end,
+ fun teardown/1,
+ ddoc_test_funs()
+ }.
+
+make_docs(Count) ->
+ lists:map(
+ fun(I) ->
+ #doc{
+ id = ?l2b("foo_" ++ integer_to_list(I)),
+ body={[{<<"value">>, I}]}
+ }
+ end,
+ lists:seq(1, Count)).
+
setup() ->
Ctx = test_util:start_couch([fabric, couch_stats]),
DbName = ?tempdb(),
ok = fabric:create_db(DbName, [{q, ?DB_Q}, {n, 1}]),
- Docs = [#doc{id = ?l2b("foo_" ++ integer_to_list(I))} || I <- lists:seq(1,
?DOCS_COUNT)],
+ Docs = make_docs(?DOCS_COUNT),
Opts = [],
{ok, _} = fabric:update_docs(DbName, Docs, Opts),
- {Ctx, DbName}.
+ {Ctx, DbName, undefined}.
-teardown({Ctx, DbName}) ->
+teardown({Ctx, DbName, _View}) ->
ok = fabric:delete_db(DbName, [?ADMIN_CTX]),
test_util:stop_couch(Ctx).
-t_context_setting({_Ctx, _DbName}) ->
+setup_ddoc(DDocId, ViewName) ->
+ {Ctx, DbName, undefined} = setup(),
+ DDoc = couch_doc:from_json_obj(
+ {[
+ {<<"_id">>, DDocId},
+ {<<"language">>, <<"javascript">>},
+ {
+ <<"views">>,
+ {[{
+ ViewName,
+ {[
+ {<<"map">>, <<"function(doc) { emit(doc.value, null);
}">>}
+ ]}
+ }]}
+ },
+ {
+ <<"filters">>,
+ {[{
+ <<"even">>,
+ <<"function(doc) { return (doc.value % 2 == 0); }">>
+ }]}
+ }
+ ]}
+ ),
+ {ok, _Rev} = fabric:update_doc(DbName, DDoc, [?ADMIN_CTX]),
+ {Ctx, DbName, {DDocId, ViewName}}.
+
+t_context_setting({_Ctx, _DbName, _View}) ->
false.
-t_all_docs_limit_zero({_Ctx, DbName}) ->
+t_all_docs_limit_zero({_Ctx, DbName, _View}) ->
Context = #{
method => 'GET',
path => "/" ++ ?b2l(DbName) ++ "/_all_docs"
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
MArgs = #mrargs{include_docs = false, limit = 0},
_Res = fabric:all_docs(DbName, [?ADMIN_CTX], fun view_cb/2, [], MArgs),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
- ?debugFmt("RCTX: ~p~n", [Rctx]),
+ Rctx = load_rctx(PidRef),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
db_open => ?DB_Q,
rows_read => 0,
docs_read => 0,
docs_written => 0,
+ ioq_calls => assert_gt(),
pid_ref => PidRef
}),
ok = nonzero_local_io_assert(Rctx),
- ?assert(maps:get(ioq_calls, Rctx) > 0),
- ?assert(maps:get(get_kp_node, Rctx) > 0),
- ?assert(maps:get(get_kv_node, Rctx) > 0),
ok = assert_teardown(PidRef).
-
-t_all_docs_include_false({_Ctx, DbName}) ->
+t_all_docs_include_false({_Ctx, DbName, View}) ->
Context = #{
method => 'GET',
path => "/" ++ ?b2l(DbName) ++ "/_all_docs"
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
MArgs = #mrargs{include_docs = false},
_Res = fabric:all_docs(DbName, [?ADMIN_CTX], fun view_cb/2, [], MArgs),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx = load_rctx(PidRef),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
db_open => ?DB_Q,
- rows_read => ?DOCS_COUNT,
+ rows_read => docs_count(View),
docs_read => 0,
docs_written => 0,
pid_ref => PidRef
}),
ok = nonzero_local_io_assert(Rctx),
- ?assert(maps:get(ioq_calls, Rctx) > 0),
- ?assert(maps:get(get_kp_node, Rctx) > 0),
- ?assert(maps:get(get_kv_node, Rctx) > 0),
ok = assert_teardown(PidRef).
-t_all_docs_include_true({_Ctx, DbName}) ->
+t_all_docs_include_true({_Ctx, DbName, View}) ->
pdebug(dbname, DbName),
Context = #{
method => 'GET',
path => "/" ++ ?b2l(DbName) ++ "/_all_docs"
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
MArgs = #mrargs{include_docs = true},
_Res = fabric:all_docs(DbName, [?ADMIN_CTX], fun view_cb/2, [], MArgs),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx = load_rctx(PidRef),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
db_open => ?DB_Q,
- rows_read => ?DOCS_COUNT,
- docs_read => ?DOCS_COUNT,
+ rows_read => docs_count(View),
+ docs_read => docs_count(View),
docs_written => 0,
pid_ref => PidRef
}),
ok = nonzero_local_io_assert(Rctx),
ok = assert_teardown(PidRef).
-t_update_docs({_Ctx, DbName}) ->
+t_update_docs({_Ctx, DbName, View}) ->
pdebug(dbname, DbName),
Context = #{
method => 'POST',
path => "/" ++ ?b2l(DbName)
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
Docs = [#doc{id = ?l2b("bar_" ++ integer_to_list(I))} || I <- lists:seq(1,
?DOCS_COUNT)],
_Res = fabric:update_docs(DbName, Docs, [?ADMIN_CTX]),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx = load_rctx(PidRef),
pdebug(rctx, Rctx),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
@@ -165,10 +215,10 @@ t_update_docs({_Ctx, DbName}) ->
docs_written => ?DOCS_COUNT,
pid_ref => PidRef
}),
- ok = zero_local_io_assert(Rctx),
+ ok = ddoc_dependent_local_io_assert(Rctx, View),
ok = assert_teardown(PidRef).
-t_get_doc({_Ctx, DbName}) ->
+t_get_doc({_Ctx, DbName, _View}) ->
pdebug(dbname, DbName),
DocId = "foo_17",
Context = #{
@@ -176,10 +226,10 @@ t_get_doc({_Ctx, DbName}) ->
path => "/" ++ ?b2l(DbName) ++ "/" ++ DocId
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
_Res = fabric:open_doc(DbName, DocId, [?ADMIN_CTX]),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx = load_rctx(PidRef),
pdebug(rctx, Rctx),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
@@ -193,7 +243,7 @@ t_get_doc({_Ctx, DbName}) ->
ok = assert_teardown(PidRef).
-t_put_doc({_Ctx, DbName}) ->
+t_put_doc({_Ctx, DbName, View}) ->
pdebug(dbname, DbName),
DocId = "bar_put_1919",
Context = #{
@@ -201,11 +251,11 @@ t_put_doc({_Ctx, DbName}) ->
path => "/" ++ ?b2l(DbName) ++ "/" ++ DocId
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
Doc = #doc{id = ?l2b(DocId)},
_Res = fabric:update_doc(DbName, Doc, [?ADMIN_CTX]),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx = load_rctx(PidRef),
pdebug(rctx, Rctx),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
@@ -215,10 +265,10 @@ t_put_doc({_Ctx, DbName}) ->
docs_written => 1,
pid_ref => PidRef
}),
- ok = zero_local_io_assert(Rctx),
+ ok = ddoc_dependent_local_io_assert(Rctx, View),
ok = assert_teardown(PidRef).
-t_delete_doc({_Ctx, DbName}) ->
+t_delete_doc({_Ctx, DbName, View}) ->
pdebug(dbname, DbName),
DocId = "foo_17",
{ok, Doc0} = fabric:open_doc(DbName, DocId, [?ADMIN_CTX]),
@@ -228,10 +278,10 @@ t_delete_doc({_Ctx, DbName}) ->
path => "/" ++ ?b2l(DbName) ++ "/" ++ DocId
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
_Res = fabric:update_doc(DbName, Doc, [?ADMIN_CTX]),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx = load_rctx(PidRef),
pdebug(rctx, Rctx),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
@@ -241,25 +291,25 @@ t_delete_doc({_Ctx, DbName}) ->
docs_written => 1,
pid_ref => PidRef
}),
- ok = zero_local_io_assert(Rctx),
+ ok = ddoc_dependent_local_io_assert(Rctx, View),
ok = assert_teardown(PidRef).
-t_changes({_Ctx, DbName}) ->
+t_changes({_Ctx, DbName, View}) ->
pdebug(dbname, DbName),
Context = #{
method => 'GET',
path => "/" ++ ?b2l(DbName) ++ "/_changes"
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
- _Res = fabric:changes(DbName, fun changes_cb/2, [],
#changes_args{limit=0}),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ _Res = fabric:changes(DbName, fun changes_cb/2, [], #changes_args{}),
+ Rctx = load_rctx(PidRef),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
db_open => ?DB_Q,
- rows_read => false,
- changes_returned => false,
+ rows_read => assert_gte(?DB_Q),
+ changes_returned => docs_count(View),
docs_read => 0,
docs_written => 0,
pid_ref => PidRef
@@ -269,22 +319,19 @@ t_changes({_Ctx, DbName}) ->
?assert(maps:get(rows_read, Rctx) >= ?DB_Q, rows_read),
?assert(maps:get(changes_returned, Rctx) >= ?DB_Q, changes_returned),
ok = nonzero_local_io_assert(Rctx),
- ?assert(maps:get(ioq_calls, Rctx) > 0),
- ?assert(maps:get(get_kp_node, Rctx) > 0),
- ?assert(maps:get(get_kv_node, Rctx) > 0),
ok = assert_teardown(PidRef).
-t_changes_limit_zero({_Ctx, DbName}) ->
+t_changes_limit_zero({_Ctx, DbName, _View}) ->
Context = #{
method => 'GET',
path => "/" ++ ?b2l(DbName) ++ "/_changes"
},
{PidRef, Nonce} = coordinator_context(Context),
- Rctx0 = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx0 = load_rctx(PidRef),
ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
_Res = fabric:changes(DbName, fun changes_cb/2, [],
#changes_args{limit=0}),
- Rctx = csrt_util:to_json(csrt:get_resource(PidRef)),
+ Rctx = load_rctx(PidRef),
ok = rctx_assert(Rctx, #{
nonce => Nonce,
db_open => ?DB_Q,
@@ -297,19 +344,85 @@ t_changes_limit_zero({_Ctx, DbName}) ->
?assert(maps:get(rows_read, Rctx) >= ?DB_Q, rows),
?assert(maps:get(changes_returned, Rctx) >= ?DB_Q, rows),
ok = nonzero_local_io_assert(Rctx),
- ?assert(maps:get(ioq_calls, Rctx) > 0),
- ?assert(maps:get(get_kp_node, Rctx) > 0),
- ?assert(maps:get(get_kv_node, Rctx) > 0),
ok = assert_teardown(PidRef).
-t_changes_filtered({_Ctx, DbName}) ->
- ?assert(false, DbName).
+%% TODO: stub in non JS filter with selector
+t_changes_filtered({_Ctx, _DbName, _View}) ->
+ false.
-t_changes_js_filtered({_Ctx, DbName}) ->
- ?assert(false, DbName).
+t_changes_js_filtered({_Ctx, DbName, {DDocId, _ViewName}=View}) ->
+ pdebug(dbname, DbName),
+ Method = 'GET',
+ Path = "/" ++ ?b2l(DbName) ++ "/_changes",
+ Context = #{
+ method => Method,
+ path => Path
+ },
+ {PidRef, Nonce} = coordinator_context(Context),
+ Req = {json_req, null},
+ Rctx0 = load_rctx(PidRef),
+ ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
+ Filter = configure_filter(DbName, DDocId, Req),
+ Args = #changes_args{filter_fun = Filter},
+ _Res = fabric:changes(DbName, fun changes_cb/2, [], Args),
+ Rctx = load_rctx(PidRef),
+ ok = rctx_assert(Rctx, #{
+ nonce => Nonce,
+ db_open => assert_gte(?DB_Q),
+ rows_read => assert_gte(docs_count(View)),
+ changes_returned => round(?DOCS_COUNT / 2),
+ docs_read => assert_gte(docs_count(View)),
+ docs_written => 0,
+ pid_ref => PidRef,
+ js_filter => docs_count(View),
+ js_filtered_docs => docs_count(View)
+ }),
+ ok = nonzero_local_io_assert(Rctx),
+ ok = assert_teardown(PidRef).
-t_view_query({_Ctx, DbName}) ->
- ?assert(false, DbName).
+t_view_query({_Ctx, DbName, View}) ->
+ Context = #{
+ method => 'GET',
+ path => "/" ++ ?b2l(DbName) ++ "/_design/foo/_view/bar"
+ },
+ {PidRef, Nonce} = coordinator_context(Context),
+ Rctx0 = load_rctx(PidRef),
+ ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
+ MArgs = #mrargs{include_docs = false},
+ _Res = fabric:all_docs(DbName, [?ADMIN_CTX], fun view_cb/2, [], MArgs),
+ Rctx = load_rctx(PidRef),
+ ok = rctx_assert(Rctx, #{
+ nonce => Nonce,
+ db_open => ?DB_Q,
+ rows_read => docs_count(View),
+ docs_read => 0,
+ docs_written => 0,
+ pid_ref => PidRef
+ }),
+ ok = nonzero_local_io_assert(Rctx),
+ ok = assert_teardown(PidRef).
+
+t_view_query_include_docs({_Ctx, DbName, View}) ->
+ Context = #{
+ method => 'GET',
+ path => "/" ++ ?b2l(DbName) ++ "/_design/foo/_view/bar"
+ },
+ {PidRef, Nonce} = coordinator_context(Context),
+ Rctx0 = load_rctx(PidRef),
+ ok = fresh_rctx_assert(Rctx0, PidRef, Nonce),
+ MArgs = #mrargs{include_docs = true},
+ _Res = fabric:all_docs(DbName, [?ADMIN_CTX], fun view_cb/2, [], MArgs),
+ Rctx = load_rctx(PidRef),
+ ok = rctx_assert(Rctx, #{
+ nonce => Nonce,
+ db_open => ?DB_Q,
+ rows_read => docs_count(View),
+ docs_read => docs_count(View),
+ docs_written => 0,
+ pid_ref => PidRef
+ }),
+ ok = nonzero_local_io_assert(Rctx),
+ ok = assert_teardown(PidRef).
assert_teardown(PidRef) ->
?assertEqual(ok, csrt:destroy_context(PidRef)),
@@ -371,6 +484,8 @@ rctx_assert(Rctx, Asserts0) ->
fun
(_K, false) ->
ok;
+ (K, Fun) when is_function(Fun) ->
+ Fun(K, maps:get(K, Rctx));
(K, V) ->
case maps:get(K, Rctx) of
false ->
@@ -409,6 +524,11 @@ nonzero_local_io_assert(Rctx, io_separate) ->
?assert(maps:get(get_kv_node, Rctx) > 0),
ok.
+ddoc_dependent_local_io_assert(Rctx, undefined) ->
+ zero_local_io_assert(Rctx);
+ddoc_dependent_local_io_assert(Rctx, {_DDoc, _ViewName}) ->
+ nonzero_local_io_assert(Rctx, io_sum).
+
coordinator_context(#{method := Method, path := Path}) ->
Nonce = couch_util:to_hex(crypto:strong_rand_bytes(5)),
Req = #httpd{method=Method, nonce=Nonce},
@@ -426,3 +546,30 @@ fresh_rctx_assert(Rctx, PidRef, Nonce) ->
pid_ref => PidRef
},
rctx_assert(Rctx, FreshAsserts).
+
+assert_gt() ->
+ assert_gt(0).
+
+assert_gt(N) ->
+ fun(K, RV) -> ?assert(RV > N, {K, RV, N}) end.
+
+assert_gte(N) ->
+ fun(K, RV) -> ?assert(RV >= N, {K, RV, N}) end.
+
+docs_count(undefined) ->
+ ?DOCS_COUNT;
+docs_count({_, _}) ->
+ ?DOCS_COUNT + ?DDOCS_COUNT.
+
+configure_filter(DbName, DDocId, Req) ->
+ configure_filter(DbName, DDocId, Req, <<"even">>).
+
+configure_filter(DbName, DDocId, Req, FName) ->
+ {ok, DDoc} = ddoc_cache:open_doc(DbName, DDocId),
+ DIR = fabric_util:doc_id_and_rev(DDoc),
+ Style = main_only,
+ {fetch, custom, Style, Req, DIR, FName}.
+
+load_rctx(PidRef) ->
+ timer:sleep(50), %% Add slight delay to accumulate RPC response deltas
+ csrt_util:to_json(csrt:get_resource(PidRef)).