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/13 17:59:40 UTC
[couchdb] 09/24: 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 8af7f28470dbf8ce722c61f2403834c0fbf63333
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>.