This is an automated email from the ASF dual-hosted git repository. davisp pushed a commit to branch optimize-ddoc-cache in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit f1fa94eecd3128ac0b65cb22f87546f609164d59 Author: Paul J. Davis <[email protected]> AuthorDate: Thu Jul 6 11:09:58 2017 -0500 FIXUP: Evict unused entries This is to address @chewbranca's comments about the rev-specific cache entries sticking around until they're evicted from cache for size limitations. Now they'll disappear after the first ?REFRESH_TIMEOUT that they aren't accessed. --- src/ddoc_cache/src/ddoc_cache_entry.erl | 34 ++++++++++++++++++++++----- src/ddoc_cache/test/ddoc_cache_entry_test.erl | 23 ++++++++++++++++-- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/ddoc_cache/src/ddoc_cache_entry.erl b/src/ddoc_cache/src/ddoc_cache_entry.erl index 2a90077..914e32e 100644 --- a/src/ddoc_cache/src/ddoc_cache_entry.erl +++ b/src/ddoc_cache/src/ddoc_cache_entry.erl @@ -48,7 +48,8 @@ val, opener, waiters, - ts + ts, + accessed }). @@ -95,7 +96,7 @@ accessed(Pid) -> refresh(Pid) -> - gen_server:cast(Pid, refresh). + gen_server:cast(Pid, force_refresh). init(Key) -> @@ -103,7 +104,8 @@ init(Key) -> St = #st{ key = Key, opener = spawn_opener(Key), - waiters = [] + waiters = [], + accessed = 1 }, ?EVENT(started, Key), gen_server:enter_loop(?MODULE, [], St). @@ -153,7 +155,25 @@ handle_call(Msg, _From, St) -> handle_cast(accessed, St) -> ?EVENT(accessed, St#st.key), drain_accessed(), - {noreply, update_lru(St)}; + NewSt = St#st{ + accessed = St#st.accessed + 1 + }, + {noreply, update_lru(NewSt)}; + +handle_cast(force_refresh, St) -> + % If we had frequent design document updates + % they could end up racing accessed events and + % end up prematurely evicting this entry from + % cache. To prevent this we just make sure that + % accessed is set to at least 1 before we + % execute a refresh. + NewSt = if St#st.accessed > 0 -> St; true -> + St#st{accessed = 1} + end, + handle_cast(refresh, NewSt); + +handle_cast(refresh, #st{accessed = 0} = St) -> + {stop, normal, St}; handle_cast(refresh, #st{opener = Ref} = St) when is_reference(Ref) -> #st{ @@ -161,7 +181,8 @@ handle_cast(refresh, #st{opener = Ref} = St) when is_reference(Ref) -> } = St, erlang:cancel_timer(Ref), NewSt = St#st{ - opener = spawn_opener(Key) + opener = spawn_opener(Key), + accessed = 0 }, {noreply, NewSt}; @@ -171,7 +192,8 @@ handle_cast(refresh, #st{opener = Pid} = St) when is_pid(Pid) -> {'DOWN', _, _, Pid, _} -> ok end, NewSt = St#st{ - opener = spawn_opener(St#st.key) + opener = spawn_opener(St#st.key), + accessed = 0 }, {noreply, NewSt}; diff --git a/src/ddoc_cache/test/ddoc_cache_entry_test.erl b/src/ddoc_cache/test/ddoc_cache_entry_test.erl index 62afc72..e593bf7 100644 --- a/src/ddoc_cache/test/ddoc_cache_entry_test.erl +++ b/src/ddoc_cache/test/ddoc_cache_entry_test.erl @@ -50,6 +50,7 @@ check_entry_test_() -> fun cancel_and_replace_opener/1, fun condenses_access_messages/1, fun kill_opener_on_terminate/1, + fun evict_when_not_accessed/1, fun open_dead_entry/1, fun handles_bad_messages/1, fun handles_code_change/1 @@ -63,7 +64,7 @@ cancel_and_replace_opener(_) -> {ok, Entry} = ddoc_cache_entry:start_link(Key), Opener1 = element(4, sys:get_state(Entry)), Ref1 = erlang:monitor(process, Opener1), - gen_server:cast(Entry, refresh), + gen_server:cast(Entry, force_refresh), receive {'DOWN', Ref1, _, _, _} -> ok end, Opener2 = element(4, sys:get_state(Entry)), ?assert(Opener2 /= Opener1), @@ -95,11 +96,29 @@ condenses_access_messages({DbName, _}) -> kill_opener_on_terminate(_) -> Pid = spawn(fun() -> receive _ -> ok end end), ?assert(is_process_alive(Pid)), - St = {st, key, val, Pid, waiters, ts}, + St = {st, key, val, Pid, waiters, ts, accessed}, ?assertEqual(ok, ddoc_cache_entry:terminate(normal, St)), ?assert(not is_process_alive(Pid)). +evict_when_not_accessed(_) -> + meck:reset(ddoc_cache_ev), + Key = {ddoc_cache_entry_custom, {<<"bar">>, ?MODULE}}, + true = ets:insert_new(?CACHE, #entry{key = Key}), + {ok, Entry} = ddoc_cache_entry:start_link(Key), + Ref = erlang:monitor(process, Entry), + ?assertEqual(1, element(7, sys:get_state(Entry))), + ok = gen_server:cast(Entry, refresh), + + meck:wait(ddoc_cache_ev, event, [update_noop, Key], 1000), + + ?assertEqual(0, element(7, sys:get_state(Entry))), + ok = gen_server:cast(Entry, refresh), + receive {'DOWN', Ref, _, _, Reason} -> Reason end, + ?assertEqual(normal, Reason), + ?assertEqual(0, ets:info(?CACHE, size)). + + open_dead_entry({DbName, _}) -> Pid = spawn(fun() -> ok end), Key = {ddoc_cache_entry_custom, {DbName, ?MODULE}}, -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
