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:02:35 UTC

[couchdb] branch allow-using-cached-security-docs updated (03e82a1 -> 811f564)

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

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


 discard 03e82a1  Allow using cached db handle when reading security and revs_limit properties
     new 811f564  Can use cached db handles when getting security and revs_limit

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (03e82a1)
            \
             N -- N -- N   refs/heads/allow-using-cached-security-docs (811f564)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:


[couchdb] 01/01: Can use cached db handles when getting security and revs_limit

Posted by va...@apache.org.
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 811f56417681ecb07a2659373f5fa329997ca548
Author: Nick Vatamaniuc <va...@apache.org>
AuthorDate: Tue Apr 14 17:43:04 2020 -0400

    Can use cached db handles when getting security and revs_limit
    
    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")),