diff options
author | Robert Newson <rnewson@apache.org> | 2023-03-22 18:20:04 +0000 |
---|---|---|
committer | Robert Newson <rnewson@apache.org> | 2023-03-22 18:20:04 +0000 |
commit | f82d9617324d9098d143cdd22d47872bfbb6ae32 (patch) | |
tree | 3bdf93ecf7e64a8ef66f87e813ee0a856a021e67 | |
parent | 451d4bdd6844a692cad2ab547e0a513daf1e88b0 (diff) | |
download | couchdb-f82d9617324d9098d143cdd22d47872bfbb6ae32.tar.gz |
don't crash in handle_db_event
-rw-r--r-- | src/couch_index/src/couch_index_server.erl | 106 |
1 files changed, 68 insertions, 38 deletions
diff --git a/src/couch_index/src/couch_index_server.erl b/src/couch_index/src/couch_index_server.erl index 2e368bfc2..36273ad68 100644 --- a/src/couch_index/src/couch_index_server.erl +++ b/src/couch_index/src/couch_index_server.erl @@ -192,6 +192,9 @@ handle_cast({add_to_ets, [Pid, DbName, DDocId, Sig]}, State) -> {noreply, State}; handle_cast({rem_from_ets, [DbName, DDocId, Sig]}, State) -> ets:delete_object(State#st.by_db, {DbName, {DDocId, Sig}}), + {noreply, State}; +handle_cast({rem_from_ets, [DbName]}, State) -> + rem_from_ets(DbName, State), {noreply, State}. handle_info({'EXIT', Pid, Reason}, Server) -> @@ -298,6 +301,27 @@ rem_from_ets(DbName, Sig, DDocIds, Pid, #st{} = St) -> DDocIds ). +rem_from_ets(DbName, #st{} = State) -> + SigDDocIds = lists:foldl( + fun({_, {DDocId, Sig}}, DDict) -> + dict:append(Sig, DDocId, DDict) + end, + dict:new(), + ets:lookup(State#st.by_db, DbName) + ), + Fun = fun({Sig, DDocIds}) -> + [{_, Pid}] = ets:lookup(State#st.by_sig, {DbName, Sig}), + unlink(Pid), + receive + {'EXIT', Pid, _} -> + ok + after 0 -> + ok + end, + rem_from_ets(DbName, Sig, DDocIds, Pid, State) + end, + lists:foreach(Fun, dict:to_list(SigDDocIds)). + handle_db_event(DbName, created, St) -> gen_server:cast(St#st.server_name, {reset_indexes, DbName}), {ok, St}; @@ -305,44 +329,50 @@ handle_db_event(DbName, deleted, St) -> gen_server:cast(St#st.server_name, {reset_indexes, DbName}), {ok, St}; handle_db_event(<<"shards/", _/binary>> = DbName, {ddoc_updated, DDocId}, St) -> - DDocResult = couch_util:with_db(DbName, fun(Db) -> - couch_db:open_doc(Db, DDocId, [ejson_body, ?ADMIN_CTX]) - end), - LocalShards = - try - mem3:local_shards(mem3:dbname(DbName)) - catch - error:database_does_not_exist -> - [] - end, - DbShards = [mem3:name(Sh) || Sh <- LocalShards], - lists:foreach( - fun(DbShard) -> - lists:foreach( - fun({_DbShard, {_DDocId, Sig}}) -> - % check if there are other ddocs with the same Sig for the same db - SigDDocs = ets:match_object(St#st.by_db, {DbShard, {'$1', Sig}}), - if - length(SigDDocs) > 1 -> - % remove records from by_db for this DDoc - Args = [DbShard, DDocId, Sig], - gen_server:cast(St#st.server_name, {rem_from_ets, Args}); - true -> - % single DDoc with this Sig - close couch_index processes - case ets:lookup(St#st.by_sig, {DbShard, Sig}) of - [{_, IndexPid}] -> - (catch gen_server:cast(IndexPid, {ddoc_updated, DDocResult})); - [] -> - [] - end - end - end, - ets:match_object(St#st.by_db, {DbShard, {DDocId, '$1'}}) - ) - end, - DbShards - ), - {ok, St}; + %% this handle_db_event function must not crash (or it takes down the couch_index_server) + try + DDocResult = couch_util:with_db(DbName, fun(Db) -> + couch_db:open_doc(Db, DDocId, [ejson_body, ?ADMIN_CTX]) + end), + LocalShards = mem3:local_shards(mem3:dbname(DbName)), + DbShards = [mem3:name(Sh) || Sh <- LocalShards], + lists:foreach( + fun(DbShard) -> + lists:foreach( + fun({_DbShard, {_DDocId, Sig}}) -> + % check if there are other ddocs with the same Sig for the same db + SigDDocs = ets:match_object(St#st.by_db, {DbShard, {'$1', Sig}}), + if + length(SigDDocs) > 1 -> + % remove records from by_db for this DDoc + Args = [DbShard, DDocId, Sig], + gen_server:cast(St#st.server_name, {rem_from_ets, Args}); + true -> + % single DDoc with this Sig - close couch_index processes + case ets:lookup(St#st.by_sig, {DbShard, Sig}) of + [{_, IndexPid}] -> + (catch gen_server:cast( + IndexPid, {ddoc_updated, DDocResult} + )); + [] -> + [] + end + end + end, + ets:match_object(St#st.by_db, {DbShard, {DDocId, '$1'}}) + ) + end, + DbShards + ), + {ok, St} + catch + Class:Reason:Stack -> + couch_log:warning("~p: handle_db_event ~p for db ~p, reason ~p, stack ~p", [ + ?MODULE, Class, DbName, Reason, Stack + ]), + gen_server:cast(St#st.server_name, {rem_from_ets, [DbName]}), + {ok, St} + end; handle_db_event(DbName, {ddoc_updated, DDocId}, St) -> lists:foreach( fun({_DbName, {_DDocId, Sig}}) -> |