summaryrefslogtreecommitdiff
path: root/src/couch_index
diff options
context:
space:
mode:
authorFilipe David Borba Manana <fdmanana@apache.org>2011-11-13 18:22:42 +0000
committerFilipe David Borba Manana <fdmanana@apache.org>2011-11-15 13:00:11 +0000
commitee942109e39d88947e82672c526d8277f9824b65 (patch)
treea6c340614853cf2c98f15114c951c71dd2d95966 /src/couch_index
parent34516a8b45708b4917eff27a9363b476fe40e26a (diff)
downloadcouchdb-ee942109e39d88947e82672c526d8277f9824b65.tar.gz
Shutdown view group on ddoc update
If a design document is updated (or deleted), its associated view groups that are open will be shutdown as soon as they finish serving all their requests. Not doing this prevented a proper cleanup of outdated view files (file descriptor leaks) and unncessary processes in the system (old view groups and index file ref counters, for e.g.). COUCHDB-1309
Diffstat (limited to 'src/couch_index')
-rw-r--r--src/couch_index/src/couch_index.erl36
-rw-r--r--src/couch_index/src/couch_index_server.erl45
2 files changed, 64 insertions, 17 deletions
diff --git a/src/couch_index/src/couch_index.erl b/src/couch_index/src/couch_index.erl
index 3b27b0f56..508604829 100644
--- a/src/couch_index/src/couch_index.erl
+++ b/src/couch_index/src/couch_index.erl
@@ -34,7 +34,8 @@
compactor,
waiters=[],
commit_delay,
- committed=true
+ committed=true,
+ shutdown=false
}).
@@ -201,8 +202,13 @@ handle_cast({config_change, NewDelay}, State) ->
{noreply, State#st{commit_delay=MsDelay}};
handle_cast({updated, NewIdxState}, State) ->
{noreply, NewState} = handle_cast({new_state, NewIdxState}, State),
- maybe_restart_updater(NewState),
- {noreply, NewState};
+ case NewState#st.shutdown andalso (NewState#st.waiters =:= []) of
+ true ->
+ {stop, normal, NewState};
+ false ->
+ maybe_restart_updater(NewState),
+ {noreply, NewState}
+ end;
handle_cast({new_state, NewIdxState}, State) ->
#st{mod=Mod, commit_delay=Delay} = State,
CurrSeq = Mod:get(update_seq, NewIdxState),
@@ -231,6 +237,30 @@ handle_cast(delete, State) ->
#st{mod=Mod, idx_state=IdxState} = State,
ok = Mod:delete(IdxState),
{stop, normal, State};
+handle_cast(ddoc_updated, State) ->
+ #st{mod = Mod, idx_state = IdxState, waiters = Waiters} = State,
+ DbName = Mod:get(db_name, IdxState),
+ DDocId = Mod:get(idx_name, IdxState),
+ Shutdown = couch_util:with_db(DbName, fun(Db) ->
+ case couch_db:open_doc(Db, DDocId, [ejson_body]) of
+ {not_found, deleted} ->
+ true;
+ {ok, DDoc} ->
+ {ok, NewIdxState} = Mod:init(Db, DDoc),
+ Mod:get(signature, NewIdxState) =/= Mod:get(signature, IdxState)
+ end
+ end),
+ case Shutdown of
+ true ->
+ case Waiters of
+ [] ->
+ {stop, normal, State};
+ _ ->
+ {noreply, State#st{shutdown = true}}
+ end;
+ false ->
+ {noreply, State#st{shutdown = false}}
+ end;
handle_cast(_Mesg, State) ->
{stop, unhandled_cast, State}.
diff --git a/src/couch_index/src/couch_index_server.erl b/src/couch_index/src/couch_index_server.erl
index e5f4571f9..975f44dab 100644
--- a/src/couch_index/src/couch_index_server.erl
+++ b/src/couch_index/src/couch_index_server.erl
@@ -70,10 +70,10 @@ get_index(Module, IdxState) ->
init([]) ->
process_flag(trap_exit, true),
couch_config:register(fun ?MODULE:config_change/2),
- couch_db_update_notifier:start_link(fun ?MODULE:update_notify/1),
ets:new(?BY_SIG, [protected, set, named_table]),
ets:new(?BY_PID, [private, set, named_table]),
- ets:new(?BY_DB, [private, bag, named_table]),
+ ets:new(?BY_DB, [protected, bag, named_table]),
+ couch_db_update_notifier:start_link(fun ?MODULE:update_notify/1),
RootDir = couch_index_util:root_dir(),
% Deprecation warning if it wasn't index_dir
case couch_config:get("couchdb", "index_dir") of
@@ -104,13 +104,13 @@ handle_call({get_index, {_Mod, _IdxState, DbName, Sig}=Args}, From, State) ->
[{_, Pid}] when is_pid(Pid) ->
{reply, {ok, Pid}, State}
end;
-handle_call({async_open, {DbName, Sig}, {ok, Pid}}, _From, State) ->
+handle_call({async_open, {DbName, DDocId, Sig}, {ok, Pid}}, _From, State) ->
[{_, Waiters}] = ets:lookup(?BY_SIG, {DbName, Sig}),
[gen_server:reply(From, {ok, Pid}) || From <- Waiters],
link(Pid),
- add_to_ets(DbName, Sig, Pid),
+ add_to_ets(DbName, Sig, DDocId, Pid),
{reply, ok, State};
-handle_call({async_error, {DbName, Sig}, Error}, _From, State) ->
+handle_call({async_error, {DbName, _DDocId, Sig}, Error}, _From, State) ->
[{_, Waiters}] = ets:lookup(?BY_SIG, {DbName, Sig}),
[gen_server:reply(From, Error) || From <- Waiters],
ets:delete(?BY_SIG, {DbName, Sig}),
@@ -128,7 +128,9 @@ handle_cast({reset_indexes, DbName}, State) ->
handle_info({'EXIT', Pid, Reason}, Server) ->
case ets:lookup(?BY_PID, Pid) of
[{Pid, DbName, Sig}] ->
- rem_from_ets(DbName, Sig, Pid);
+ [{DbName, {DDocId, Sig}}] =
+ ets:match_object(?BY_DB, {DbName, {'$1', Sig}}),
+ rem_from_ets(DbName, Sig, DDocId, Pid);
[] when Reason /= normal ->
exit(Reason);
_Else ->
@@ -142,37 +144,40 @@ code_change(_OldVsn, State, _Extra) ->
new_index({Mod, IdxState, DbName, Sig}) ->
+ DDocId = Mod:get(idx_name, IdxState),
case couch_index:start_link({Mod, IdxState}) of
{ok, Pid} ->
- gen_server:call(?MODULE, {async_open, {DbName, Sig}, {ok, Pid}}),
+ ok = gen_server:call(
+ ?MODULE, {async_open, {DbName, DDocId, Sig}, {ok, Pid}}),
unlink(Pid);
Error ->
- gen_server:call(?MODULE, {async_error, {DbName, Sig}, Error})
+ ok = gen_server:call(
+ ?MODULE, {async_error, {DbName, DDocId, Sig}, Error})
end.
reset_indexes(DbName, Root) ->
% shutdown all the updaters and clear the files, the db got changed
- Fun = fun({_, Sig}) ->
+ Fun = fun({_, {DDocId, Sig}}) ->
[{_, Pid}] = ets:lookup(?BY_SIG, {DbName, Sig}),
couch_util:shutdown_sync(Pid),
- rem_from_ets(DbName, Sig, Pid)
+ rem_from_ets(DbName, Sig, DDocId, Pid)
end,
lists:foreach(Fun, ets:lookup(?BY_DB, DbName)),
Path = Root ++ "/." ++ binary_to_list(DbName) ++ "_design",
couch_file:nuke_dir(Root, Path).
-add_to_ets(DbName, Sig, Pid) ->
+add_to_ets(DbName, Sig, DDocId, Pid) ->
ets:insert(?BY_SIG, {{DbName, Sig}, Pid}),
ets:insert(?BY_PID, {Pid, {DbName, Sig}}),
- ets:insert(?BY_DB, {DbName, Sig}).
+ ets:insert(?BY_DB, {DbName, {DDocId, Sig}}).
-rem_from_ets(DbName, Sig, Pid) ->
+rem_from_ets(DbName, Sig, DDocId, Pid) ->
ets:delete(?BY_SIG, {DbName, Sig}),
ets:delete(?BY_PID, Pid),
- ets:delete_object(?BY_DB, {DbName, Sig}).
+ ets:delete_object(?BY_DB, {DbName, {DDocId, Sig}}).
config_change("couchdb", "view_index_dir") ->
@@ -185,6 +190,18 @@ update_notify({deleted, DbName}) ->
gen_server:cast(?MODULE, {reset_indexes, DbName});
update_notify({created, DbName}) ->
gen_server:cast(?MODULE, {reset_indexes, DbName});
+update_notify({ddoc_updated, {DbName, DDocId}}) ->
+ case ets:match_object(?BY_DB, {DbName, {DDocId, '$1'}}) of
+ [] ->
+ ok;
+ [{DbName, {DDocId, Sig}}] ->
+ case ets:lookup(?BY_SIG, {DbName, Sig}) of
+ [{_, IndexPid}] ->
+ (catch gen_server:cast(IndexPid, ddoc_updated));
+ [] ->
+ ok
+ end
+ end;
update_notify(_) ->
ok.