summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Avdey <eiri@eiri.ca>2017-06-05 13:21:28 -0300
committerEric Avdey <eiri@eiri.ca>2017-06-05 15:04:55 -0300
commit05d2e1e6a7db3e2526530cdbb446034557b94e57 (patch)
tree6240975bfcd68fe675ea521b6635c7b3da8222ba
parentb09273f8ff07f22dd5a3b0df34352390fb364fea (diff)
downloadcouchdb-05d2e1e6a7db3e2526530cdbb446034557b94e57.tar.gz
Add retry to get_view_state
There are a race condition in `get_view/4`, between aquiring index's Pid and getting its state, that surface when the same signature DDoc got rapidly deleted and re-created. This patch addresses this by adding retry logic to get_view_index_state.
-rw-r--r--src/couch_mrview/src/couch_mrview_util.erl51
1 files changed, 36 insertions, 15 deletions
diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index 27f8737d4..3e6a86b3c 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -34,28 +34,16 @@
-export([to_key_seq/1]).
-define(MOD, couch_mrview_index).
+-define(GET_VIEW_RETRY_COUNT, 1).
+-define(GET_VIEW_RETRY_DELAY, 50).
-include_lib("couch/include/couch_db.hrl").
-include_lib("couch_mrview/include/couch_mrview.hrl").
get_view(Db, DDoc, ViewName, Args0) ->
- {ok, Pid, Args2} = get_view_index_pid(Db, DDoc, ViewName, Args0),
- DbUpdateSeq = couch_util:with_db(Db, fun(WDb) ->
- couch_db:get_update_seq(WDb)
- end),
- MinSeq = case Args2#mrargs.update of
- false -> 0; lazy -> 0; _ -> DbUpdateSeq
- end,
- {ok, State} = case couch_index:get_state(Pid, MinSeq) of
- {ok, _} = Resp -> Resp;
- Error -> throw(Error)
- end,
+ {ok, State, Args2} = get_view_index_state(Db, DDoc, ViewName, Args0),
Ref = erlang:monitor(process, State#mrst.fd),
- if Args2#mrargs.update == lazy ->
- spawn(fun() -> catch couch_index:get_state(Pid, DbUpdateSeq) end);
- true -> ok
- end,
#mrst{language=Lang, views=Views} = State,
{Type, View, Args3} = extract_view(Lang, Args2, ViewName, Views),
check_range(Args3, view_cmp(View)),
@@ -71,6 +59,39 @@ get_view_index_pid(Db, DDoc, ViewName, Args0) ->
couch_index_server:get_index(?MOD, Db, DDoc, ArgCheck).
+get_view_index_state(Db, DDoc, ViewName, Args0) ->
+ get_view_index_state(Db, DDoc, ViewName, Args0, ?GET_VIEW_RETRY_COUNT).
+
+get_view_index_state(_, DDoc, _, _, RetryCount) when RetryCount < 0 ->
+ couch_log:warning("DDoc '~s' recreated too frequently", [DDoc#doc.id]),
+ throw({get_view_state, exceeded_retry_count});
+get_view_index_state(Db, DDoc, ViewName, Args0, RetryCount) ->
+ try
+ {ok, Pid, Args} = get_view_index_pid(Db, DDoc, ViewName, Args0),
+ UpdateSeq = couch_util:with_db(Db, fun(WDb) ->
+ couch_db:get_update_seq(WDb)
+ end),
+ {ok, State} = case Args#mrargs.update of
+ lazy ->
+ spawn(fun() ->
+ catch couch_index:get_state(Pid, UpdateSeq)
+ end),
+ couch_index:get_state(Pid, 0);
+ false ->
+ couch_index:get_state(Pid, 0);
+ _ ->
+ couch_index:get_state(Pid, UpdateSeq)
+ end,
+ {ok, State, Args}
+ catch
+ exit:{Reason, _} when Reason == noproc; Reason == normal ->
+ timer:sleep(?GET_VIEW_RETRY_DELAY),
+ get_view_index_state(Db, DDoc, ViewName, Args0, RetryCount - 1);
+ Error ->
+ throw(Error)
+ end.
+
+
ddoc_to_mrst(DbName, #doc{id=Id, body={Fields}}) ->
MakeDict = fun({Name, {MRFuns}}, DictBySrcAcc) ->
case couch_util:get_value(<<"map">>, MRFuns) of