This is an automated email from the ASF dual-hosted git repository. nickva pushed a commit to branch fix-bulk-get-not-found-all-revs in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit dad7dfbb295c9057379f69cf9a065638aa16421a Author: Nick Vatamaniuc <[email protected]> AuthorDate: Mon May 18 11:06:55 2026 -0400 Fix bulk_get not_found response handling when revs=all Since revs=all when doc is not found we return `{ok, []}` we didn't count that response as a not_found response and success_possible would return false too early. If a node was in mm mode and that was the last response we could end up emitting that as an internal error instead of the proper "not found" result. --- src/fabric/src/fabric_open_revs.erl | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/fabric/src/fabric_open_revs.erl b/src/fabric/src/fabric_open_revs.erl index 30ab7c74b..1f6bbb9ad 100644 --- a/src/fabric/src/fabric_open_revs.erl +++ b/src/fabric/src/fabric_open_revs.erl @@ -23,6 +23,10 @@ idrevs, wcnt = 0, rcnt = 0, + % count of non-error responses regardless if they added anything to `response` list + % for example a worker might returtn {ok,[]} when we get Revs=all and we have make sure + % we count that towards sucess_possible/1 returning true. + received = 0, responses = [] }). @@ -95,7 +99,7 @@ init_state(DbName, IdsRevsOpts, Options) -> responses_fold({ArgRef, NewResp}, #{} = Reqs) -> #{ArgRef := Req} = Reqs, - #req{rcnt = R, wcnt = W, responses = Resps} = Req, + #req{rcnt = R, wcnt = W, received = Recv, responses = Resps} = Req, Resps1 = merge_responses(Resps, NewResp), % If responses don't match or are "not found", don't bump rcnt so we can % wait for more workers. @@ -112,6 +116,7 @@ responses_fold({ArgRef, NewResp}, #{} = Reqs) -> ArgRef => Req#req{ rcnt = NewR, wcnt = W - 1, + received = Recv + 1, responses = Resps1 } }. @@ -231,8 +236,8 @@ success_possible(#{} = Reqs) -> success_possible_fold(_Key, #req{}, _Acc = false) -> false; -success_possible_fold(_Key, #req{wcnt = W, responses = Resps}, _Acc) -> - W > 0 orelse Resps =/= []. +success_possible_fold(_Key, #req{wcnt = W, received = Recv}, _Acc) -> + W > 0 orelse Recv > 0. r_met(#{} = Reqs, ExpectedR) -> Fun = fun(_, #req{rcnt = R}, Acc) -> min(R, Acc) end, @@ -346,6 +351,7 @@ open_revs_quorum_test_() -> ?TDEF_FE(t_stemmed_merge_correctly), ?TDEF_FE(t_not_found_counted_as_descendant), ?TDEF_FE(t_all_not_found), + ?TDEF_FE(t_all_not_found_then_maintenance_mode), ?TDEF_FE(t_rev_not_found_returned), ?TDEF_FE(t_rexi_errors_progress), ?TDEF_FE(t_generic_errors_progress), @@ -491,6 +497,15 @@ t_all_not_found(_) -> {ok, S2} = handle_message([[]], W2, S1), ?assertEqual({stop, [[]]}, handle_message([[]], W3, S2)). +t_all_not_found_then_maintenance_mode(_) -> + % two revs=all not_founds + one last mm mode + S0 = #st{workers = Workers0} = st0(), + [W1, W2, W3] = lists:sort(maps:keys(Workers0)), + {ok, S1} = handle_message([[]], W1, S0), + {ok, S2} = handle_message([[]], W2, S1), + Res = handle_message({rexi_EXIT, {maintenance_mode, foo}}, W3, S2), + ?assertEqual({stop, [[]]}, Res). + t_rev_not_found_returned(_) -> % If a specific rev is not found that is returned S0 = #st{workers = Workers0} = st0(),
