You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2017/07/10 18:29:32 UTC

[couchdb] 09/17: FIXUP: Evict unused entries

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 <pa...@gmail.com>
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
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.