Optionally indicate if "r" was met
COUCHDB-2655
diff --git a/src/fabric_doc_open.erl b/src/fabric_doc_open.erl
index c7d90a4..bcb6198 100644
--- a/src/fabric_doc_open.erl
+++ b/src/fabric_doc_open.erl
@@ -23,6 +23,7 @@
dbname,
workers,
r,
+ n,
state,
replies,
q_reply
@@ -34,19 +35,21 @@
[Id, [deleted|Options]]),
SuppressDeletedDoc = not lists:member(deleted, Options),
N = mem3:n(DbName),
- R = couch_util:get_value(r, Options, integer_to_list(mem3:quorum(DbName))),
+ R = list_to_integer(
+ couch_util:get_value(r, Options, integer_to_list(mem3:quorum(DbName)))),
Acc0 = #acc{
dbname = DbName,
workers = Workers,
- r = erlang:min(N, list_to_integer(R)),
- state = r_not_met,
+ r = R,
+ n = N,
+ state = threshold_not_met,
replies = []
},
RexiMon = fabric_util:create_monitors(Workers),
try fabric_util:recv(Workers, #shard.ref, fun handle_message/3, Acc0) of
{ok, #acc{}=Acc} ->
Reply = handle_response(Acc),
- format_reply(Reply, SuppressDeletedDoc);
+ format_reply(Reply, SuppressDeletedDoc, Acc#acc.state, Options);
{timeout, #acc{workers=DefunctWorkers}} ->
fabric_util:log_timeout(DefunctWorkers, "open_doc"),
{error, timeout};
@@ -75,18 +78,23 @@
handle_message(Reply, Worker, Acc) ->
NewReplies = fabric_util:update_counter(Reply, 1, Acc#acc.replies),
NewAcc = Acc#acc{replies = NewReplies},
- case is_r_met(Acc#acc.workers, NewReplies, Acc#acc.r) of
- {true, QuorumReply} ->
+ case {is_threshold_met(Acc#acc.workers, NewReplies, Acc#acc.r),
+ is_threshold_met(Acc#acc.workers, NewReplies, Acc#acc.n)} of
+ {{true, QuorumReply}, _} ->
fabric_util:cleanup(lists:delete(Worker, Acc#acc.workers)),
{stop, NewAcc#acc{workers=[], state=r_met, q_reply=QuorumReply}};
- wait_for_more ->
+ {_, {true, QuorumReply}} ->
+ fabric_util:cleanup(lists:delete(Worker, Acc#acc.workers)),
+ {stop, NewAcc#acc{workers=[], state=n_met, q_reply=QuorumReply}};
+ {W1, W2} when W1 == wait_for_more; W2 == wait_for_more ->
NewWorkers = lists:delete(Worker, Acc#acc.workers),
{ok, NewAcc#acc{workers=NewWorkers}};
- no_more_workers ->
+ {N1, N2} when N1 == no_more_workers; N2 == no_more_workers ->
{stop, NewAcc#acc{workers=[]}}
end.
-handle_response(#acc{state=r_met, replies=Replies, q_reply=QuorumReply}=Acc) ->
+handle_response(#acc{state=State, replies=Replies, q_reply=QuorumReply}=Acc)
+ when State == r_met; State == n_met ->
case {Replies, fabric_util:remove_ancestors(Replies, [])} of
{[_], [_]} ->
% Complete agreement amongst all copies
@@ -103,8 +111,8 @@
handle_response(Acc) ->
read_repair(Acc).
-is_r_met(Workers, Replies, R) ->
- case lists:dropwhile(fun({_,{_, Count}}) -> Count < R end, Replies) of
+is_threshold_met(Workers, Replies, Threshold) ->
+ case lists:dropwhile(fun({_,{_, Count}}) -> Count < Threshold end, Replies) of
[{_,{QuorumReply, _}} | _] ->
{true, QuorumReply};
[] when length(Workers) > 1 ->
@@ -156,13 +164,20 @@
end, Docs),
{ok, Winner}.
-format_reply({ok, #doc{deleted=true}}, true) ->
+
+format_reply({ok, #doc{deleted=true}}, true, _, _) ->
{not_found, deleted};
-format_reply(Else, _) ->
+format_reply({ok, Doc}, _, RMet, Options) ->
+ Meta = case lists:member(is_r_met, Options) of
+ true -> [{r_met, RMet == r_met} | Doc#doc.meta];
+ false -> Doc#doc.meta
+ end,
+ {ok, Doc#doc{meta=Meta}};
+format_reply(Else, _, _, _) ->
Else.
-is_r_met_test() ->
+is_threshold_met_test() ->
Workers0 = [],
Workers1 = [nil],
Workers2 = [nil,nil],
@@ -171,71 +186,71 @@
?assertEqual(
{true, foo},
- is_r_met([], [fabric_util:kv(foo,2)], 2)
+ is_threshold_met([], [fabric_util:kv(foo,2)], 2)
),
?assertEqual(
{true, foo},
- is_r_met([], [fabric_util:kv(foo,3)], 2)
+ is_threshold_met([], [fabric_util:kv(foo,3)], 2)
),
?assertEqual(
{true, foo},
- is_r_met([], [fabric_util:kv(foo,1)], 1)
+ is_threshold_met([], [fabric_util:kv(foo,1)], 1)
),
?assertEqual(
{true, foo},
- is_r_met([], [fabric_util:kv(foo,2), fabric_util:kv(bar,1)], 2)
+ is_threshold_met([], [fabric_util:kv(foo,2), fabric_util:kv(bar,1)], 2)
),
?assertEqual(
{true, bar},
- is_r_met([], [fabric_util:kv(bar,1), fabric_util:kv(bar,2)], 2)
+ is_threshold_met([], [fabric_util:kv(bar,1), fabric_util:kv(bar,2)], 2)
),
?assertEqual(
{true, bar},
- is_r_met([], [fabric_util:kv(bar,2), fabric_util:kv(foo,1)], 2)
+ is_threshold_met([], [fabric_util:kv(bar,2), fabric_util:kv(foo,1)], 2)
),
% Not met, but wait for more messages
?assertEqual(
wait_for_more,
- is_r_met(Workers2, [fabric_util:kv(foo,1)], 2)
+ is_threshold_met(Workers2, [fabric_util:kv(foo,1)], 2)
),
?assertEqual(
wait_for_more,
- is_r_met(Workers2, [fabric_util:kv(foo,2)], 3)
+ is_threshold_met(Workers2, [fabric_util:kv(foo,2)], 3)
),
?assertEqual(
wait_for_more,
- is_r_met(Workers2, [fabric_util:kv(foo,1), fabric_util:kv(bar,1)], 2)
+ is_threshold_met(Workers2, [fabric_util:kv(foo,1), fabric_util:kv(bar,1)], 2)
),
% Not met, bail out
?assertEqual(
no_more_workers,
- is_r_met(Workers0, [fabric_util:kv(foo,1)], 2)
+ is_threshold_met(Workers0, [fabric_util:kv(foo,1)], 2)
),
?assertEqual(
no_more_workers,
- is_r_met(Workers1, [fabric_util:kv(foo,1)], 2)
+ is_threshold_met(Workers1, [fabric_util:kv(foo,1)], 2)
),
?assertEqual(
no_more_workers,
- is_r_met(Workers1, [fabric_util:kv(foo,1), fabric_util:kv(bar,1)], 2)
+ is_threshold_met(Workers1, [fabric_util:kv(foo,1), fabric_util:kv(bar,1)], 2)
),
?assertEqual(
no_more_workers,
- is_r_met(Workers1, [fabric_util:kv(foo,2)], 3)
+ is_threshold_met(Workers1, [fabric_util:kv(foo,2)], 3)
),
ok.