You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2019/11/11 23:42:29 UTC

[couchdb] 02/05: Improve efficiency and observability of expiration

This is an automated email from the ASF dual-hosted git repository.

jaydoane pushed a commit to branch expiring-cache
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit f8b4b775db2dba2f3856ac5ec11646c83d278256
Author: Jay Doane <ja...@apache.org>
AuthorDate: Tue Oct 29 00:03:19 2019 -0700

    Improve efficiency and observability of expiration
---
 .../src/couch_expiring_cache_fdb.erl               | 55 ++++++++++++++------
 .../src/couch_expiring_cache_server.erl            | 58 ++++++++++++----------
 2 files changed, 72 insertions(+), 41 deletions(-)

diff --git a/src/couch_expiring_cache/src/couch_expiring_cache_fdb.erl b/src/couch_expiring_cache/src/couch_expiring_cache_fdb.erl
index f432813..283a92b 100644
--- a/src/couch_expiring_cache/src/couch_expiring_cache_fdb.erl
+++ b/src/couch_expiring_cache/src/couch_expiring_cache_fdb.erl
@@ -13,6 +13,7 @@
 -module(couch_expiring_cache_fdb).
 
 -export([
+    clear_expired_range/2,
     get_range/2,
     cache_prefix/1,
     layer_prefix/1,
@@ -22,11 +23,11 @@
     %% delete_all/1,
     insert/5,
     lookup/2,
-    remove/2
+    remove_primary/2
 ]).
 
 
--define(DEFAULT_LIMIT, 100000).
+-define(DEFAULT_LIMIT, 100000). % How to enforce?
 
 -define(XC, 53). % coordinate with fabric2.hrl
 -define(PK, 1).
@@ -78,12 +79,32 @@ get_range(EndTS, Limit) when Limit > 0 ->
 
 remove_exp(ExpiresTS, Name, Key) ->
     fabric2_fdb:transactional(fun(Tx) ->
-        Prefix = layer_prefix(Tx),
+        clear_expired(Tx, ExpiresTS, Name, Key, layer_prefix(Tx))
+    end).
+
 
-        PK = erlfdb_tuple:pack({?XC, ?PK, Name, Key}, Prefix),
-        XK = erlfdb_tuple:pack({?XC, ?EXP, ExpiresTS, Name, Key}, Prefix),
-        ok = erlfdb:clear(Tx, PK),
-        ok = erlfdb:clear(Tx, XK)
+clear_expired(Tx, ExpiresTS, Name, Key, Prefix) ->
+    PK = primary_key(Name, Key, Prefix),
+    XK = expiry_key(ExpiresTS, Name, Key, Prefix),
+    ok = erlfdb:clear(Tx, PK),
+    ok = erlfdb:clear(Tx, XK).
+
+
+clear_expired_range(EndTS, Limit) when Limit > 0 ->
+    Callback = fun
+        (TS, 0) when TS > 0 -> TS;
+        (TS, Acc) -> min(TS, Acc)
+    end,
+    fabric2_fdb:transactional(fun(Tx) ->
+        LayerPrefix = layer_prefix(Tx),
+        ExpiresPrefix = erlfdb_tuple:pack({?XC, ?EXP}, LayerPrefix),
+        fabric2_fdb:fold_range({tx, Tx}, ExpiresPrefix, fun({K, _V}, Acc) ->
+            Unpacked = erlfdb_tuple:unpack(K, ExpiresPrefix),
+            couch_log:info("~p clearing ~p", [?MODULE, Unpacked]),
+            {ExpiresTS, Name, Key} = Unpacked,
+            clear_expired(Tx, ExpiresTS, Name, Key, LayerPrefix),
+            Callback(ExpiresTS, Acc)
+        end, 0, [{end_key, EndTS}, {limit, Limit}])
     end).
 
 
@@ -91,11 +112,11 @@ insert(Name, Key, Val, StaleTS, ExpiresTS) ->
     fabric2_fdb:transactional(fun(Tx) ->
         Prefix = layer_prefix(Tx),
 
-        PK = erlfdb_tuple:pack({?XC, ?PK, Name, Key}, Prefix),
+        PK = primary_key(Name, Key, Prefix),
         PV = erlfdb_tuple:pack({Val, StaleTS, ExpiresTS}),
         ok = erlfdb:set(Tx, PK, PV),
 
-        XK = erlfdb_tuple:pack({?XC, ?EXP, ExpiresTS, Name, Key}, Prefix),
+        XK = expiry_key(ExpiresTS, Name, Key, Prefix),
         XV = erlfdb_tuple:pack({}),
         ok = erlfdb:set(Tx, XK, XV)
     end).
@@ -105,7 +126,7 @@ lookup(Name, Key) ->
     fabric2_fdb:transactional(fun(Tx) ->
         Prefix = layer_prefix(Tx),
 
-        PK = erlfdb_tuple:pack({?XC, ?PK, Name, Key}, Prefix),
+        PK = primary_key(Name, Key, Prefix),
         case erlfdb:wait(erlfdb:get(Tx, PK)) of
             not_found ->
                 not_found;
@@ -121,15 +142,21 @@ lookup(Name, Key) ->
     end).
 
 
-remove(Name, Key) ->
+remove_primary(Name, Key) ->
     fabric2_fdb:transactional(fun(Tx) ->
-        Prefix = layer_prefix(Tx),
-
-        PK = erlfdb_tuple:pack({?XC, ?PK, Name, Key}, Prefix),
+        PK = primary_key(Name, Key, layer_prefix(Tx)),
         erlfdb:clear(Tx, PK)
     end).
 
 
+primary_key(Name, Key, Prefix) ->
+    erlfdb_tuple:pack({?XC, ?PK, Name, Key}, Prefix).
+
+
+expiry_key(ExpiresTS, Name, Key, Prefix) ->
+    erlfdb_tuple:pack({?XC, ?EXP, ExpiresTS, Name, Key}, Prefix).
+
+
 layer_prefix(Tx) ->
     fabric2_fdb:get_dir(Tx).
 
diff --git a/src/couch_expiring_cache/src/couch_expiring_cache_server.erl b/src/couch_expiring_cache/src/couch_expiring_cache_server.erl
index 0072c38..f67e7be 100644
--- a/src/couch_expiring_cache/src/couch_expiring_cache_server.erl
+++ b/src/couch_expiring_cache/src/couch_expiring_cache_server.erl
@@ -28,8 +28,9 @@
 ]).
 
 
--define(PERIOD_DEFAULT, 10).
--define(MAX_JITTER_DEFAULT, 1).
+-define(DEFAULT_BATCH, 1000).
+-define(DEFAULT_PERIOD_MS, 5000).
+-define(DEFAULT_MAX_JITTER_MS, 1000).
 
 
 start_link() ->
@@ -38,7 +39,11 @@ start_link() ->
 
 init(_) ->
     Ref = schedule_remove_expired(),
-    {ok, #{timer_ref => Ref}}.
+    {ok, #{
+        timer_ref => Ref,
+        lag => 0,
+        last_expiration => 0,
+        min_ts => 0}}.
 
 
 terminate(_, _) ->
@@ -54,9 +59,14 @@ handle_cast(Msg, St) ->
 
 
 handle_info(remove_expired, St) ->
-    ok = remove_expired(),
+    Now = erlang:system_time(second),
+    MinTS = remove_expired(Now),
     Ref = schedule_remove_expired(),
-    {noreply, St#{timer_ref => Ref}};
+    {noreply, St#{
+        timer_ref => Ref,
+        lag => Now - MinTS,
+        last_expiration => Now,
+        min_ts => MinTS}};
 
 handle_info(Msg, St) ->
     {stop, {bad_info, Msg}, St}.
@@ -66,33 +76,27 @@ code_change(_OldVsn, St, _Extra) ->
     {ok, St}.
 
 
-remove_expired() ->
-    Now = erlang:system_time(second),
-    Limit = 10,
-    Expired = couch_expiring_cache_fdb:get_range(Now, Limit),
-    case Expired of
-        [] ->
-            ok;
-        _ ->
-            lists:foreach(fun({TS, Name, Key} = Exp) ->
-                couch_log:info("~p remove_expired ~p", [?MODULE, Exp]),
-                couch_expiring_cache_fdb:remove_exp(TS, Name, Key)
-            end, Expired)
-    end.
+remove_expired(EndTS) ->
+    couch_expiring_cache_fdb:clear_expired_range(EndTS, batch_size()).
 
 
 schedule_remove_expired() ->
-    Timeout = get_period_sec(),
-    MaxJitter = max(Timeout div 2, get_max_jitter_sec()),
+    Timeout = period_ms(),
+    MaxJitter = max(Timeout div 2, max_jitter_ms()),
     Wait = Timeout + rand:uniform(max(1, MaxJitter)),
-    erlang:send_after(Wait * 1000, self(), remove_expired).
+    erlang:send_after(Wait, self(), remove_expired).
+
+
+period_ms() ->
+    config:get_integer("couch_expiring_cache", "period_ms",
+        ?DEFAULT_PERIOD_MS).
 
 
-get_period_sec() ->
-    config:get_integer("couch_expiring_cache", "period_sec",
-        ?PERIOD_DEFAULT).
+max_jitter_ms() ->
+    config:get_integer("couch_expiring_cache", "max_jitter_ms",
+        ?DEFAULT_MAX_JITTER_MS).
 
 
-get_max_jitter_sec() ->
-    config:get_integer("couch_expiring_cache", "max_jitter_sec",
-        ?MAX_JITTER_DEFAULT).
+batch_size() ->
+    config:get_integer("couch_expiring_cache", "batch_size",
+        ?DEFAULT_BATCH).