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")),