diff options
author | Eric Avdey <eiri@eiri.ca> | 2017-06-05 13:21:28 -0300 |
---|---|---|
committer | Eric Avdey <eiri@eiri.ca> | 2017-06-05 15:04:55 -0300 |
commit | 05d2e1e6a7db3e2526530cdbb446034557b94e57 (patch) | |
tree | 6240975bfcd68fe675ea521b6635c7b3da8222ba | |
parent | b09273f8ff07f22dd5a3b0df34352390fb364fea (diff) | |
download | couchdb-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.erl | 51 |
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 |