You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2020/04/14 22:00:35 UTC

[couchdb] 01/01: Allow using cached db handle when reading security and revs_limit properties

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

vatamane pushed a commit to branch allow-using-cached-security-docs
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 03e82a1beaa59180293bb228c9df70ae7e8dea3b
Author: Nick Vatamaniuc <va...@apache.org>
AuthorDate: Tue Apr 14 17:43:04 2020 -0400

    Allow using cached db handle when reading security and revs_limit properties
    
    By default transactions are started every time to check metadata and possibly
    reopen the db, but if a `max_age` option is provided then the handle is allowed
    to be up to `max_age` milliseconds old.
    
    The main use of this would be in pluggable authorization handlers where it
    might be necessary to inspect the security doc multiple times for the same
    request before a final decision is made.
    
    `revs_limit/1` was updated as well, mainly for consistency since it is almost
    identical to `get_security/1`.
---
 src/fabric/src/fabric2_db.erl             | 36 ++++++++++++++++++++++++-------
 src/fabric/src/fabric2_fdb.erl            |  2 ++
 src/fabric/test/fabric2_db_misc_tests.erl | 25 +++++++++++++++++++++
 3 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index 9b9efda..6157c64 100644
--- a/src/fabric/src/fabric2_db.erl
+++ b/src/fabric/src/fabric2_db.erl
@@ -50,7 +50,9 @@
     get_instance_start_time/1,
     get_pid/1,
     get_revs_limit/1,
+    get_revs_limit/2,
     get_security/1,
+    get_security/2,
     get_update_seq/1,
     get_user_ctx/1,
     get_uuid/1,
@@ -500,17 +502,21 @@ get_pid(#{}) ->
 
 
 get_revs_limit(#{} = Db) ->
-    #{revs_limit := RevsLimit} = fabric2_fdb:transactional(Db, fun(TxDb) ->
-        fabric2_fdb:ensure_current(TxDb)
-    end),
-    RevsLimit.
+    get_revs_limit(#{} = Db, []).
+
+
+get_revs_limit(#{} = Db, Opts) ->
+    CurrentDb = get_cached_db(Db, Opts),
+    maps:get(revs_limit, CurrentDb).
 
 
 get_security(#{} = Db) ->
-    #{security_doc := SecDoc} = fabric2_fdb:transactional(Db, fun(TxDb) ->
-        fabric2_fdb:ensure_current(TxDb)
-    end),
-    SecDoc.
+    get_security(Db, []).
+
+
+get_security(#{} = Db, Opts) ->
+    CurrentDb = get_cached_db(Db, Opts),
+    maps:get(security_doc, CurrentDb).
 
 
 get_update_seq(#{} = Db) ->
@@ -2037,3 +2043,17 @@ open_json_doc(Db, DocId, OpenOpts, DocOpts) ->
         {ok, #doc{} = Doc} ->
             [{doc, couch_doc:to_json_obj(Doc, DocOpts)}]
     end.
+
+
+get_cached_db(#{} = Db, Opts) when is_list(Opts) ->
+    MaxAge = fabric2_util:get_value(max_age, Opts, 0),
+    Now = erlang:monotonic_time(millisecond),
+    Age = Now - maps:get(open_ts, Db),
+    case Age < MaxAge of
+        true ->
+            Db;
+        false ->
+            fabric2_fdb:transactional(Db, fun(TxDb) ->
+                fabric2_fdb:ensure_current(TxDb)
+            end)
+    end.
diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl
index d96c3ae..fc77816 100644
--- a/src/fabric/src/fabric2_fdb.erl
+++ b/src/fabric/src/fabric2_fdb.erl
@@ -231,6 +231,7 @@ create(#{} = Db0, Options) ->
         revs_limit => 1000,
         security_doc => {[]},
         user_ctx => UserCtx,
+        open_ts => erlang:monotonic_time(millisecond),
 
         validate_doc_update_funs => [],
         before_doc_update => undefined,
@@ -272,6 +273,7 @@ open(#{} = Db0, Options) ->
         security_doc => {[]},
 
         user_ctx => UserCtx,
+        open_ts => erlang:monotonic_time(millisecond),
 
         % Place holders until we implement these
         % bits.
diff --git a/src/fabric/test/fabric2_db_misc_tests.erl b/src/fabric/test/fabric2_db_misc_tests.erl
index 1959982..b41c7b0 100644
--- a/src/fabric/test/fabric2_db_misc_tests.erl
+++ b/src/fabric/test/fabric2_db_misc_tests.erl
@@ -38,6 +38,7 @@ misc_test_() ->
                 ?TDEF(accessors),
                 ?TDEF(set_revs_limit),
                 ?TDEF(set_security),
+                ?TDEF(get_security_cached),
                 ?TDEF(is_system_db),
                 ?TDEF(validate_dbname),
                 ?TDEF(validate_doc_ids),
@@ -113,6 +114,30 @@ set_security({DbName, Db, _}) ->
     ?assertEqual(SecObj, fabric2_db:get_security(Db2)).
 
 
+get_security_cached({DbName, Db, _}) ->
+    OldSecObj = fabric2_db:get_security(Db),
+    SecObj = {[
+        {<<"admins">>, {[
+            {<<"names">>, [<<"foo1">>]},
+            {<<"roles">>, []}
+        ]}}
+    ]},
+
+    % Set directly so we don't auto-update the local cache
+    {ok, Db1} = fabric2_db:open(DbName, [?ADMIN_CTX]),
+    ?assertMatch({ok, #{}}, fabric2_fdb:transactional(Db1, fun(TxDb) ->
+        fabric2_fdb:set_config(TxDb, security_doc, SecObj)
+    end)),
+
+    {ok, Db2} = fabric2_db:open(DbName, [?ADMIN_CTX]),
+    ?assertEqual(OldSecObj, fabric2_db:get_security(Db2, [{max_age, 1000}])),
+
+    timer:sleep(100),
+    ?assertEqual(SecObj, fabric2_db:get_security(Db2, [{max_age, 50}])),
+
+    ?assertEqual(ok, fabric2_db:set_security(Db2, OldSecObj)).
+
+
 is_system_db({DbName, Db, _}) ->
     ?assertEqual(false, fabric2_db:is_system_db(Db)),
     ?assertEqual(false, fabric2_db:is_system_db_name("foo")),