Skip to content

Commit

Permalink
Only start changes listener if auth db exists
Browse files Browse the repository at this point in the history
In situations where the chttpd authenticaion db (usually named "_users")
does not get created, or gets deleted, the auth cache still attempts
to start the change listener, which results in notice and error level
logs in the system like:

[notice] 2018-12-03T22:04:44.299392Z node1@127.0.0.1 <0.374.0> -------- chttpd_auth_cache changes listener died database_does_not_exist at mem3_shards:load_shards_from_db/6(line:395) <= mem3_shards:load_shards_from_disk/1(line:370) <= mem3_shards:load_shards_from_disk/2(line:399) <= mem3_shards:for_docid/3(line:86) <= fabric_doc_open:go/3(line:39) <= chttpd_auth_cache:ensure_auth_ddoc_exists/2(line:195) <= chttpd_auth_cache:listen_for_changes/1(line:142)

[error] 2018-12-03T22:04:44.299585Z node1@127.0.0.1 emulator -------- Error in process <0.3180.0> on node 'node1@127.0.0.1' with exit value:
{database_does_not_exist,[{mem3_shards,load_shards_from_db,"_users",[{file,"src/mem3_shards.erl"},{line,395}]},{mem3_shards,load_shards_from_disk,1,[{file,"src/mem3_shards.erl"},{line,370}]},{mem3_shards,load_shards_from_disk,2,[{file,"src/mem3_shards.erl"},{line,399}]},{mem3_shards,for_docid,3,[{file,"src/mem3_shards.erl"},{line,86}]},{fabric_doc_open,go,3,[{file,"src/fabric_doc_open.erl"},{line,39}]},{chttpd_auth_cache,ensure_auth_ddoc_exists,2,[{file,"src/chttpd_auth_cache.erl"},{line,195}]},{chttpd_auth_cache,listen_for_changes,1,[{file,"src/chttpd_auth_cache.erl"},{line,142}]}]}

This changes the auth cache so that it

• Only starts change listener if auth db exists
• Only retains EndSeq if auth db still exists

Fixes apache#1354
Fixes apache#1949
  • Loading branch information
jaydoane committed Mar 2, 2019
1 parent 92c004b commit 8f2f436
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 7 deletions.
32 changes: 25 additions & 7 deletions src/chttpd/src/chttpd_auth_cache.erl
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ maybe_increment_auth_cache_miss(UserName) ->
%% gen_server callbacks

init([]) ->
self() ! {start_listener, 0},
self() ! {maybe_start_listener, 0},
{ok, #state{}}.

handle_call(_Call, _From, State) ->
Expand All @@ -110,17 +110,24 @@ handle_cast(_Msg, State) ->
{noreply, State}.

handle_info({'DOWN', _, _, Pid, Reason}, #state{changes_pid=Pid} = State) ->
Seq = case Reason of
{seq, EndSeq} ->
Seq = case {Reason, db_exists(dbname())} of
{{seq, EndSeq}, true} ->
EndSeq;
_ ->
couch_log:notice("~p changes listener died ~r", [?MODULE, Reason]),
0
end,
erlang:send_after(5000, self(), {start_listener, Seq}),
{noreply, State#state{last_seq=Seq}};
handle_info({start_listener, Seq}, State) ->
{noreply, State#state{changes_pid = spawn_changes(Seq)}};
erlang:send_after(5000, self(), {maybe_start_listener, Seq}),
{noreply, State#state{last_seq = Seq, changes_pid = undefined}};
handle_info({maybe_start_listener, Seq}, State) ->
State1 = case may_spawn_listener(State) of
true ->
State#state{changes_pid = spawn_changes(Seq)};
false ->
erlang:send_after(5000, self(), {maybe_start_listener, Seq}),
State
end,
{noreply, State1};
handle_info(_Msg, State) ->
{noreply, State}.

Expand All @@ -134,6 +141,17 @@ code_change(_OldVsn, #state{}=State, _Extra) ->

%% private functions

may_spawn_listener(#state{changes_pid = Pid}) ->
case is_pid(Pid) andalso is_process_alive(Pid) of
true ->
false;
false ->
db_exists(dbname())
end.

db_exists(DbName) ->
is_list(catch mem3:shards(DbName)).

spawn_changes(Since) ->
{Pid, _} = spawn_monitor(?MODULE, listen_for_changes, [Since]),
Pid.
Expand Down
80 changes: 80 additions & 0 deletions src/chttpd/test/chttpd_auth_cache_test.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
% License for the specific language governing permissions and limitations under
% the License.

-module(chttpd_auth_cache_test).

-include_lib("couch/include/couch_eunit.hrl").
-include_lib("couch/include/couch_db.hrl").


setup() ->
DbName = ?tempdb(),
ok = fabric:create_db(DbName),
DbName.


teardown(DbName) ->
fabric:delete_db(DbName).


auth_db_change_listener_lifecycle_test_() ->
{
"auth db and change listener lifecycle tests",
{
setup,
fun chttpd_test_util:start_couch/0,
fun chttpd_test_util:stop_couch/1,
{
foreach,
fun setup/0, fun teardown/1,
[
fun change_listener_should_not_start_if_auth_db_missing/1,
fun change_listener_should_start_if_auth_db_exists/1
]
}
}
}.



change_listener_should_not_start_if_auth_db_missing(_DbName) ->
?_test(begin
%% ok = restart_app(chttpd),
AuthCachePid = whereis(chttpd_auth_cache),
?assert(is_pid(AuthCachePid)),
Expected = {state, undefined, "0"},
?assertEqual(Expected, sys:get_state(AuthCachePid)),
ok
end).


change_listener_should_start_if_auth_db_exists(DbName) ->
?_test(begin
config:set("chttpd_auth", "authentication_db", ?b2l(DbName), false),
ok = restart_app(chttpd),
AuthCachePid = whereis(chttpd_auth_cache),
?assert(is_pid(AuthCachePid)),
ListenerPid = test_util:wait(fun() ->
case sys:get_state(AuthCachePid) of
{state, undefined, "0"} -> wait;
{state, Pid, "0"} when is_pid(Pid) -> Pid
end
end, 1000, 10),
?assert(is_pid(ListenerPid) andalso is_process_alive(ListenerPid)),
config:set("chttpd_auth", "authentication_db", "_users", false),
ok
end).


restart_app(App) ->
ok = application:stop(App),
ok = application:start(App).

0 comments on commit 8f2f436

Please # to comment.