You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ii...@apache.org on 2019/10/01 18:08:23 UTC

[couchdb] branch opentracing created (now aa359ee)

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

iilyak pushed a change to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git.


      at aa359ee  Remove compiler warning

This branch includes the following new commits:

     new 1186b39  Remove old clause which is no longer used
     new 2e00ea8  Identify tracing span life cycle
     new 5aa38fe  Add request_ctx field to #httpd{} record to pass to fabric
     new f4def40  Set request_ctx on database operations
     new fd9331c  Add helper functions to work with contexts
     new 7cc592a  Pass contexts to fabric2_db functions
     new a269f19  Pass request_ctx in chttpd_auth_cache
     new aa359ee  Remove compiler warning

The 8 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.



[couchdb] 05/08: Add helper functions to work with contexts

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit fd9331ce1fb9bdc9def784172901a927f2f68c5c
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Tue Oct 1 15:59:01 2019 +0000

    Add helper functions to work with contexts
---
 src/chttpd/src/chttpd.erl | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index 704e203..e00088d 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -44,7 +44,9 @@
 
 -export([
     chunked_response_buffer_size/0,
-    close_delayed_json_object/4
+    close_delayed_json_object/4,
+    contexts_to_list/1,
+    set_contexts/2
 ]).
 
 -record(delayed_resp, {
@@ -1237,6 +1239,14 @@ span_ok(_Resp) ->
 span_error(_ErrorStr, _ReasonStr, []) ->
     fun(Span) -> Span end.
 
+contexts_to_list(Req) ->
+    set_contexts(Req, []).
+
+set_contexts(#httpd{user_ctx=UserCtx, request_ctx=RequestCtx}, KVList0) ->
+    KVList1 = lists:keystore(request_ctx, 1, KVList0, {request_ctx, RequestCtx}),
+    KVList2 = lists:keystore(user_ctx, 1, KVList1, {user_ctx, UserCtx}),
+    KVList2.
+
 -ifdef(TEST).
 
 -include_lib("eunit/include/eunit.hrl").


[couchdb] 06/08: Pass contexts to fabric2_db functions

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 7cc592a465fe0beb95c2dd5ea152f3584d1cda19
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Tue Oct 1 16:01:08 2019 +0000

    Pass contexts to fabric2_db functions
    
    Since the db structure returned from fabric2_db:open and fabric2_db:create
    includes `user_ctx` and `request_ctx` there is no need to pass it explicitelly
    in every `fabric2_db` call. This means we can simplify few things:
    
    - Don't pass user_ctx in `chttpd_db:db_req/2` since we pass db already
    - Don't have to use `db_open_options` in `chttpd_changes`
    - Don't have to pass `user_ctx` to `fabric2_db:open_doc` and `fabric2_db:update_doc`
---
 src/chttpd/src/chttpd_auth_request.erl |   6 +-
 src/chttpd/src/chttpd_changes.erl      |   8 +--
 src/chttpd/src/chttpd_db.erl           | 103 ++++++++++++++-------------------
 src/chttpd/src/chttpd_misc.erl         |  11 ++--
 4 files changed, 53 insertions(+), 75 deletions(-)

diff --git a/src/chttpd/src/chttpd_auth_request.erl b/src/chttpd/src/chttpd_auth_request.erl
index 7210905..938b891 100644
--- a/src/chttpd/src/chttpd_auth_request.erl
+++ b/src/chttpd/src/chttpd_auth_request.erl
@@ -102,8 +102,8 @@ server_authorization_check(#httpd{method=Method, path_parts=[<<"_utils">>|_]}=Re
 server_authorization_check(#httpd{path_parts=[<<"_", _/binary>>|_]}=Req) ->
     require_admin(Req).
 
-db_authorization_check(#httpd{path_parts=[DbName|_],user_ctx=Ctx}=Req) ->
-    {ok, Db} = fabric2_db:open(DbName, [{user_ctx, Ctx}]),
+db_authorization_check(#httpd{path_parts=[DbName|_]}=Req) ->
+    {ok, Db} = fabric2_db:open(DbName, chttpd:contexts_to_list(Req)),
     fabric2_db:check_is_member(Db),
     Req.
 
@@ -112,7 +112,7 @@ require_admin(Req) ->
     Req.
 
 require_db_admin(#httpd{path_parts=[DbName|_],user_ctx=Ctx}=Req) ->
-    {ok, Db} = fabric2_db:open(DbName, [{user_ctx, Ctx}]),
+    {ok, Db} = fabric2_db:open(DbName, chttpd:contexts_to_list(Req)),
     Sec = fabric2_db:get_security(Db),
     case is_db_admin(Ctx,Sec) of
         true -> Req;
diff --git a/src/chttpd/src/chttpd_changes.erl b/src/chttpd/src/chttpd_changes.erl
index 81fa90f..b55ae15 100644
--- a/src/chttpd/src/chttpd_changes.erl
+++ b/src/chttpd/src/chttpd_changes.erl
@@ -658,8 +658,7 @@ send_lookup_changes(FullDocInfos, StartSeq, Dir, Db, Fun, Acc0) ->
 keep_sending_changes(Args, Acc0, FirstRound) ->
     #changes_args{
         feed = ResponseType,
-        limit = Limit,
-        db_open_options = DbOptions
+        limit = Limit
     } = Args,
 
     {ok, ChangesAcc} = send_changes(Acc0, fwd, FirstRound),
@@ -678,9 +677,8 @@ keep_sending_changes(Args, Acc0, FirstRound) ->
         if Go /= ok -> end_sending_changes(Callback, UserAcc3, EndSeq); true ->
             case wait_updated(Timeout, TimeoutFun, UserAcc3) of
             {updated, UserAcc4} ->
-                UserCtx = fabric2_db:get_user_ctx(Db),
-                DbOptions1 = [{user_ctx, UserCtx} | DbOptions],
-                case fabric2_db:open(fabric2_db:name(Db), DbOptions1) of
+                DbOptions = fabric2_db:contexts_to_list(Db),
+                case fabric2_db:open(fabric2_db:name(Db), DbOptions) of
                 {ok, Db2} ->
                     ?MODULE:keep_sending_changes(
                       Args#changes_args{limit=NewLimit},
diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index 5a7f060..08ee96b 100644
--- a/src/chttpd/src/chttpd_db.erl
+++ b/src/chttpd/src/chttpd_db.erl
@@ -92,10 +92,7 @@ handle_changes_req(#httpd{path_parts=[_,<<"_changes">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "GET,POST,HEAD").
 
 handle_changes_req1(#httpd{}=Req, Db) ->
-    #changes_args{filter=Raw, style=Style} = Args0 = parse_changes_query(Req),
-    ChangesArgs = Args0#changes_args{
-        db_open_options = [{user_ctx, fabric2_db:get_user_ctx(Db)}]
-    },
+    ChangesArgs = parse_changes_query(Req),
     ChangesFun = chttpd_changes:handle_db_changes(ChangesArgs, Req, Db),
     Max = chttpd:chunked_response_buffer_size(),
     case ChangesArgs#changes_args.feed of
@@ -363,7 +360,7 @@ handle_design_info_req(Req, _Db, _DDoc) ->
 create_db_req(#httpd{}=Req, DbName) ->
     couch_httpd:verify_is_server_admin(Req),
     DocUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
-    case fabric2_db:create(DbName, []) of
+    case fabric2_db:create(DbName, chttpd:contexts_to_list(Req)) of
         {ok, _} ->
             send_json(Req, 201, [{"Location", DocUrl}], {[{ok, true}]});
         {error, file_exists} ->
@@ -374,15 +371,15 @@ create_db_req(#httpd{}=Req, DbName) ->
 
 delete_db_req(#httpd{}=Req, DbName) ->
     couch_httpd:verify_is_server_admin(Req),
-    case fabric2_db:delete(DbName, []) of
+    case fabric2_db:delete(DbName, chttpd:contexts_to_list(Req)) of
         ok ->
             send_json(Req, 200, {[{ok, true}]});
         Error ->
             throw(Error)
     end.
 
-do_db_req(#httpd{path_parts=[DbName|_], user_ctx=Ctx}=Req, Fun) ->
-    {ok, Db} = fabric2_db:open(DbName, [{user_ctx, Ctx}]),
+do_db_req(#httpd{path_parts=[DbName|_]}=Req, Fun) ->
+    {ok, Db} = fabric2_db:open(DbName, chttpd:contexts_to_list(Req)),
     Fun(Req, Db).
 
 db_req(#httpd{method='GET',path_parts=[_DbName]}=Req, Db) ->
@@ -393,11 +390,9 @@ db_req(#httpd{method='GET',path_parts=[_DbName]}=Req, Db) ->
     couch_stats:update_histogram([couchdb, dbinfo], DeltaT),
     send_json(Req, {DbInfo});
 
-db_req(#httpd{method='POST', path_parts=[DbName], user_ctx=Ctx}=Req, Db) ->
+db_req(#httpd{method='POST', path_parts=[DbName]}=Req, Db) ->
     chttpd:validate_ctype(Req, "application/json"),
 
-    Options = [{user_ctx,Ctx}],
-
     Doc0 = chttpd:json_body(Req),
     Doc1 = couch_doc:from_json_obj_validate(Doc0, fabric2_db:name(Db)),
     Doc2 = case Doc1#doc.id of
@@ -411,7 +406,7 @@ db_req(#httpd{method='POST', path_parts=[DbName], user_ctx=Ctx}=Req, Db) ->
     "ok" ->
         % async_batching
         spawn(fun() ->
-                case catch(fabric2_db:update_doc(Db, Doc2, Options)) of
+                case catch(fabric2_db:update_doc(Db, Doc2, [])) of
                 {ok, _} ->
                     chttpd_stats:incr_writes(),
                     ok;
@@ -431,7 +426,7 @@ db_req(#httpd{method='POST', path_parts=[DbName], user_ctx=Ctx}=Req, Db) ->
         % normal
         DocUrl = absolute_uri(Req, [$/, couch_util:url_encode(DbName),
             $/, couch_util:url_encode(DocId)]),
-        case fabric2_db:update_doc(Db, Doc2, Options) of
+        case fabric2_db:update_doc(Db, Doc2, []) of
         {ok, NewRev} ->
             chttpd_stats:incr_writes(),
             HttpCode = 201;
@@ -449,8 +444,8 @@ db_req(#httpd{method='POST', path_parts=[DbName], user_ctx=Ctx}=Req, Db) ->
 db_req(#httpd{path_parts=[_DbName]}=Req, _Db) ->
     send_method_not_allowed(Req, "DELETE,GET,HEAD,POST");
 
-db_req(#httpd{method='POST', path_parts=[_DbName, <<"_ensure_full_commit">>],
-        user_ctx=Ctx}=Req, Db) ->
+db_req(#httpd{method='POST', path_parts=[_DbName, <<"_ensure_full_commit">>]
+        }=Req, Db) ->
     chttpd:validate_ctype(Req, "application/json"),
     #{db_prefix := <<_/binary>>} = Db,
     send_json(Req, 201, {[
@@ -461,7 +456,7 @@ db_req(#httpd{method='POST', path_parts=[_DbName, <<"_ensure_full_commit">>],
 db_req(#httpd{path_parts=[_,<<"_ensure_full_commit">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "POST");
 
-db_req(#httpd{method='POST',path_parts=[_,<<"_bulk_docs">>], user_ctx=Ctx}=Req, Db) ->
+db_req(#httpd{method='POST',path_parts=[_,<<"_bulk_docs">>]}=Req, Db) ->
     couch_stats:increment_counter([couchdb, httpd, bulk_requests]),
     chttpd:validate_ctype(Req, "application/json"),
     {JsonProps} = chttpd:json_body_obj(Req),
@@ -474,13 +469,13 @@ db_req(#httpd{method='POST',path_parts=[_,<<"_bulk_docs">>], user_ctx=Ctx}=Req,
         DocsArray0
     end,
     couch_stats:update_histogram([couchdb, httpd, bulk_docs], length(DocsArray)),
-    case chttpd:header_value(Req, "X-Couch-Full-Commit") of
+    Options = case chttpd:header_value(Req, "X-Couch-Full-Commit") of
     "true" ->
-        Options = [full_commit, {user_ctx,Ctx}];
+        [full_commit];
     "false" ->
-        Options = [delay_commit, {user_ctx,Ctx}];
+        [delay_commit];
     _ ->
-        Options = [{user_ctx,Ctx}]
+        []
     end,
     DbName = fabric2_db:name(Db),
     Docs = lists:map(fun(JsonObj) ->
@@ -545,10 +540,8 @@ db_req(#httpd{method='POST', path_parts=[_, <<"_bulk_get">>],
             throw({bad_request, <<"Missing JSON list of 'docs'.">>});
         Docs ->
             #doc_query_args{
-                options = Options0
+                options = Options
             } = bulk_get_parse_doc_query(Req),
-            Options = [{user_ctx, Req#httpd.user_ctx} | Options0],
-
             AcceptJson =  MochiReq:accepts_content_type("application/json"),
             AcceptMixedMp = MochiReq:accepts_content_type("multipart/mixed"),
             AcceptRelatedMp = MochiReq:accepts_content_type("multipart/related"),
@@ -613,7 +606,6 @@ db_req(#httpd{path_parts=[_, <<"_bulk_get">>]}=Req, _Db) ->
 db_req(#httpd{method='POST',path_parts=[_,<<"_purge">>]}=Req, Db) ->
     couch_stats:increment_counter([couchdb, httpd, purge_requests]),
     chttpd:validate_ctype(Req, "application/json"),
-    Options = [{user_ctx, Req#httpd.user_ctx}],
     {IdsRevs} = chttpd:json_body_obj(Req),
     IdsRevs2 = [{Id, couch_doc:parse_revs(Revs)} || {Id, Revs} <- IdsRevs],
     MaxIds = config:get_integer("purge", "max_document_id_number", 100),
@@ -630,7 +622,7 @@ db_req(#httpd{method='POST',path_parts=[_,<<"_purge">>]}=Req, Db) ->
         true -> ok
     end,
     couch_stats:increment_counter([couchdb, document_purges, total], length(IdsRevs2)),
-    Results2 = case fabric:purge_docs(Db, IdsRevs2, Options) of
+    Results2 = case fabric:purge_docs(Db, IdsRevs2, []) of
         {ok, Results} ->
             chttpd_stats:incr_writes(length(Results)),
             Results;
@@ -741,8 +733,7 @@ db_req(#httpd{method='GET',path_parts=[_,<<"_security">>]}=Req, Db) ->
 db_req(#httpd{path_parts=[_,<<"_security">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "PUT,GET");
 
-db_req(#httpd{method='PUT',path_parts=[_,<<"_revs_limit">>],user_ctx=Ctx}=Req,
-        Db) ->
+db_req(#httpd{method='PUT',path_parts=[_,<<"_revs_limit">>]}=Req, Db) ->
     Limit = chttpd:json_body(Req),
     ok = fabric2_db:set_revs_limit(Db, Limit),
     send_json(Req, {[{<<"ok">>, true}]});
@@ -754,10 +745,9 @@ db_req(#httpd{path_parts=[_,<<"_revs_limit">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "PUT,GET");
 
 db_req(#httpd{method='PUT',path_parts=[_,<<"_purged_infos_limit">>]}=Req, Db) ->
-    Options = [{user_ctx, Req#httpd.user_ctx}],
     case chttpd:json_body(Req) of
         Limit when is_integer(Limit), Limit > 0 ->
-            case fabric:set_purge_infos_limit(Db, Limit, Options) of
+            case fabric:set_purge_infos_limit(Db, Limit, []) of
                 ok ->
                     send_json(Req, {[{<<"ok">>, true}]});
                 Error ->
@@ -805,7 +795,6 @@ db_req(#httpd{path_parts=[_, DocId | FileNameParts]}=Req, Db) ->
     db_attachment_req(Req, Db, DocId, FileNameParts).
 
 multi_all_docs_view(Req, Db, OP, Queries) ->
-    UserCtx = Req#httpd.user_ctx,
     Args0 = couch_mrview_http:parse_params(Req, undefined),
     Args1 = Args0#mrargs{view_type=map},
     ArgQueries = lists:map(fun({Query}) ->
@@ -826,16 +815,15 @@ multi_all_docs_view(Req, Db, OP, Queries) ->
     },
     VAcc1 = lists:foldl(fun
         (#mrargs{keys = undefined} = Args, Acc0) ->
-            send_all_docs(Db, Args, UserCtx, Acc0);
+            send_all_docs(Db, Args, Acc0);
         (#mrargs{keys = Keys} = Args, Acc0) when is_list(Keys) ->
-            send_all_docs_keys(Db, Args, UserCtx, Acc0)
+            send_all_docs_keys(Db, Args, Acc0)
     end, VAcc0, ArgQueries),
     {ok, Resp1} = chttpd:send_delayed_chunk(VAcc1#vacc.resp, "\r\n]}"),
     chttpd:end_delayed_json_response(Resp1).
 
 
 all_docs_view(Req, Db, Keys, OP) ->
-    UserCtx = Req#httpd.user_ctx,
     Args0 = couch_mrview_http:parse_params(Req, Keys),
     Args1 = Args0#mrargs{view_type=map},
     Args2 = couch_views_util:validate_args(Args1),
@@ -848,17 +836,17 @@ all_docs_view(Req, Db, Keys, OP) ->
     },
     case Args3#mrargs.keys of
         undefined ->
-            VAcc1 = send_all_docs(Db, Args3, UserCtx, VAcc0),
+            VAcc1 = send_all_docs(Db, Args3, VAcc0),
             {ok, VAcc1#vacc.resp};
         Keys when is_list(Keys) ->
-            VAcc1 = send_all_docs_keys(Db, Args3, UserCtx, VAcc0),
+            VAcc1 = send_all_docs_keys(Db, Args3, VAcc0),
             {ok, VAcc2} = view_cb(complete, VAcc1),
             {ok, VAcc2#vacc.resp}
     end.
 
 
-send_all_docs(Db, #mrargs{keys = undefined} = Args, UserCtx, VAcc0) ->
-    Opts = all_docs_view_opts(Args, UserCtx),
+send_all_docs(Db, #mrargs{keys = undefined} = Args, VAcc0) ->
+    Opts = all_docs_view_opts(Args),
     NS = couch_util:get_value(namespace, Opts),
     FoldFun = case NS of
         <<"_all_docs">> -> fold_docs;
@@ -871,7 +859,7 @@ send_all_docs(Db, #mrargs{keys = undefined} = Args, UserCtx, VAcc0) ->
     VAcc1.
 
 
-send_all_docs_keys(Db, #mrargs{} = Args, UserCtx, VAcc0) ->
+send_all_docs_keys(Db, #mrargs{} = Args, VAcc0) ->
     Keys = apply_args_to_keylist(Args, Args#mrargs.keys),
     NS = couch_util:get_value(namespace, Args#mrargs.extra),
     TotalRows = fabric2_db:get_doc_count(Db, NS),
@@ -886,7 +874,7 @@ send_all_docs_keys(Db, #mrargs{} = Args, UserCtx, VAcc0) ->
     DocOpts = case Args#mrargs.conflicts of
         true -> [conflicts | Args#mrargs.doc_options];
         _ -> Args#mrargs.doc_options
-    end ++ [{user_ctx, UserCtx}],
+    end,
     IncludeDocs = Args#mrargs.include_docs,
     lists:foldl(fun(DocId, Acc) ->
         OpenOpts = [deleted | DocOpts],
@@ -929,7 +917,7 @@ send_all_docs_keys(Db, #mrargs{} = Args, UserCtx, VAcc0) ->
     end, VAcc1, Keys).
 
 
-all_docs_view_opts(Args, UserCtx) ->
+all_docs_view_opts(Args) ->
     NS = couch_util:get_value(namespace, Args#mrargs.extra),
     StartKey = case Args#mrargs.start_key of
         undefined -> Args#mrargs.start_key_docid;
@@ -949,7 +937,6 @@ all_docs_view_opts(Args, UserCtx) ->
         {undefined, _} -> []
     end,
     [
-        {user_ctx, UserCtx},
         {dir, Args#mrargs.direction},
         {limit, Args#mrargs.limit},
         {skip, Args#mrargs.skip},
@@ -983,7 +970,7 @@ view_cb({row, Row}, {iter, Db, Args, VAcc}) ->
             DocOpts = case Args#mrargs.conflicts of
                 true -> [conflicts | Args#mrargs.doc_options];
                 _ -> Args#mrargs.doc_options
-            end ++ [{user_ctx, (VAcc#vacc.req)#httpd.user_ctx}],
+            end,
             OpenOpts = [deleted | DocOpts],
             DocMember = case fabric2_db:open_doc(Db, DocId, OpenOpts) of
                 {not_found, missing} ->
@@ -1034,10 +1021,9 @@ db_doc_req(#httpd{method='GET', mochi_req=MochiReq}=Req, Db, DocId) ->
     #doc_query_args{
         rev = Rev,
         open_revs = Revs,
-        options = Options0,
+        options = Options,
         atts_since = AttsSince
     } = parse_doc_query(Req),
-    Options = [{user_ctx, Req#httpd.user_ctx} | Options0],
     case Revs of
     [] ->
         Options2 =
@@ -1084,13 +1070,11 @@ db_doc_req(#httpd{method='GET', mochi_req=MochiReq}=Req, Db, DocId) ->
         end
     end;
 
-db_doc_req(#httpd{method='POST', user_ctx=Ctx}=Req, Db, DocId) ->
+db_doc_req(#httpd{method='POST'}=Req, Db, DocId) ->
     couch_httpd:validate_referer(Req),
     fabric2_db:validate_docid(DocId),
     chttpd:validate_ctype(Req, "multipart/form-data"),
 
-    Options = [{user_ctx,Ctx}],
-
     Form = couch_httpd:parse_form(Req),
     case proplists:is_defined("_doc", Form) of
     true ->
@@ -1127,7 +1111,7 @@ db_doc_req(#httpd{method='POST', user_ctx=Ctx}=Req, Db, DocId) ->
     NewDoc = Doc#doc{
         atts = UpdatedAtts ++ OldAtts2
     },
-    case fabric2_db:update_doc(Db, NewDoc, Options) of
+    case fabric2_db:update_doc(Db, NewDoc, []) of
     {ok, NewRev} ->
         chttpd_stats:incr_writes(),
         HttpCode = 201;
@@ -1141,15 +1125,13 @@ db_doc_req(#httpd{method='POST', user_ctx=Ctx}=Req, Db, DocId) ->
         {rev, couch_doc:rev_to_str(NewRev)}
     ]});
 
-db_doc_req(#httpd{method='PUT', user_ctx=Ctx}=Req, Db, DocId) ->
+db_doc_req(#httpd{method='PUT'}=Req, Db, DocId) ->
     #doc_query_args{
         update_type = UpdateType
     } = parse_doc_query(Req),
     DbName = fabric2_db:name(Db),
     fabric2_db:validate_docid(DocId),
 
-    Options = [{user_ctx, Ctx}],
-
     Loc = absolute_uri(Req, [$/, couch_util:url_encode(DbName),
         $/, couch_util:url_encode(DocId)]),
     RespHeaders = [{"Location", Loc}],
@@ -1176,7 +1158,7 @@ db_doc_req(#httpd{method='PUT', user_ctx=Ctx}=Req, Db, DocId) ->
             Doc = couch_doc_from_req(Req, Db, DocId, chttpd:json_body(Req)),
 
             spawn(fun() ->
-                    case catch(fabric2_db:update_doc(Db, Doc, Options)) of
+                    case catch(fabric2_db:update_doc(Db, Doc, [])) of
                     {ok, _} ->
                         chttpd_stats:incr_writes(),
                         ok;
@@ -1199,7 +1181,7 @@ db_doc_req(#httpd{method='PUT', user_ctx=Ctx}=Req, Db, DocId) ->
         end
     end;
 
-db_doc_req(#httpd{method='COPY', user_ctx=Ctx}=Req, Db, SourceDocId) ->
+db_doc_req(#httpd{method='COPY'}=Req, Db, SourceDocId) ->
     SourceRev =
     case extract_header_rev(Req, chttpd:qs_value(Req, "rev")) of
         missing_rev -> nil;
@@ -1211,7 +1193,7 @@ db_doc_req(#httpd{method='COPY', user_ctx=Ctx}=Req, Db, SourceDocId) ->
     Doc = couch_doc_open(Db, SourceDocId, SourceRev, []),
     % save new doc
     case fabric2_db:update_doc(Db,
-        Doc#doc{id=TargetDocId, revs=TargetRevs}, [{user_ctx,Ctx}]) of
+        Doc#doc{id=TargetDocId, revs=TargetRevs}, []) of
     {ok, NewTargetRev} ->
         chttpd_stats:incr_writes(),
         HttpCode = 201;
@@ -1407,16 +1389,16 @@ send_updated_doc(Req, Db, DocId, Json) ->
 send_updated_doc(Req, Db, DocId, Doc, Headers) ->
     send_updated_doc(Req, Db, DocId, Doc, Headers, interactive_edit).
 
-send_updated_doc(#httpd{user_ctx=Ctx} = Req, Db, DocId, #doc{deleted=Deleted}=Doc,
+send_updated_doc(#httpd{} = Req, Db, DocId, #doc{deleted=Deleted}=Doc,
         Headers, UpdateType) ->
     Options =
         case couch_httpd:header_value(Req, "X-Couch-Full-Commit") of
         "true" ->
-            [full_commit, UpdateType, {user_ctx,Ctx}];
+            [full_commit, UpdateType];
         "false" ->
-            [delay_commit, UpdateType, {user_ctx,Ctx}];
+            [delay_commit, UpdateType];
         _ ->
-            [UpdateType, {user_ctx,Ctx}]
+            [UpdateType]
         end,
     {Status, {etag, Etag}, Body} = update_doc(Db, DocId,
         #doc{deleted=Deleted}=Doc, Options),
@@ -1625,7 +1607,6 @@ db_attachment_req(#httpd{method='GET',mochi_req=MochiReq}=Req, Db, DocId, FileNa
 db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts)
         when (Method == 'PUT') or (Method == 'DELETE') ->
     #httpd{
-        user_ctx = Ctx,
         mochi_req = MochiReq
     } = Req,
     FileName = validate_attachment_name(
@@ -1710,7 +1691,7 @@ db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts)
             fabric2_db:validate_docid(DocId),
             #doc{id=DocId};
         Rev ->
-            case fabric2_db:open_doc_revs(Db, DocId, [Rev], [{user_ctx,Ctx}]) of
+            case fabric2_db:open_doc_revs(Db, DocId, [Rev], []) of
             {ok, [{ok, Doc0}]} ->
                 chttpd_stats:incr_reads(),
                 Doc0;
@@ -1725,7 +1706,7 @@ db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts)
     DocEdited = Doc#doc{
         atts = NewAtt ++ [A || A <- Atts, couch_att:fetch(name, A) /= FileName]
     },
-    case fabric2_db:update_doc(Db, DocEdited, [{user_ctx,Ctx}]) of
+    case fabric2_db:update_doc(Db, DocEdited, []) of
     {ok, UpdatedRev} ->
         chttpd_stats:incr_writes(),
         HttpCode = 201;
diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl
index e05b972..236b8da 100644
--- a/src/chttpd/src/chttpd_misc.erl
+++ b/src/chttpd/src/chttpd_misc.erl
@@ -107,7 +107,7 @@ maybe_add_csp_headers(Headers, "true") ->
 maybe_add_csp_headers(Headers, _) ->
     Headers.
 
-handle_all_dbs_req(#httpd{method='GET', request_ctx=RequestCtx}=Req) ->
+handle_all_dbs_req(#httpd{method='GET'}=Req) ->
     #mrargs{
         start_key = StartKey,
         end_key = EndKey,
@@ -121,8 +121,7 @@ handle_all_dbs_req(#httpd{method='GET', request_ctx=RequestCtx}=Req) ->
         {end_key, EndKey},
         {dir, Dir},
         {limit, Limit},
-        {skip, Skip},
-        {request_ctx, RequestCtx}
+        {skip, Skip}
     ],
 
     % Eventually the Etag for this request will be derived
@@ -133,7 +132,7 @@ handle_all_dbs_req(#httpd{method='GET', request_ctx=RequestCtx}=Req) ->
         {ok, Resp} = chttpd:start_delayed_json_response(Req, 200, [{"ETag",Etag}]),
         Callback = fun all_dbs_callback/2,
         Acc = #vacc{req=Req,resp=Resp},
-        fabric2_db:list_dbs(Callback, Acc, Options)
+        fabric2_db:list_dbs(Callback, Acc, chttpd:set_contexts(Req, Options))
     end),
     case is_record(Resp, vacc) of
         true -> {ok, Resp#vacc.resp};
@@ -158,7 +157,7 @@ all_dbs_callback({error, Reason}, #vacc{resp=Resp0}=Acc) ->
     {ok, Resp1} = chttpd:send_delayed_error(Resp0, Reason),
     {ok, Acc#vacc{resp=Resp1}}.
 
-handle_dbs_info_req(#httpd{method='POST', user_ctx=UserCtx}=Req) ->
+handle_dbs_info_req(#httpd{method='POST'}=Req) ->
     chttpd:validate_ctype(Req, "application/json"),
     Props = chttpd:json_body_obj(Req),
     Keys = couch_mrview_util:get_view_keys(Props),
@@ -176,7 +175,7 @@ handle_dbs_info_req(#httpd{method='POST', user_ctx=UserCtx}=Req) ->
     send_chunk(Resp, "["),
     lists:foldl(fun(DbName, AccSeparator) ->
         try
-            {ok, Db} = fabric2_db:open(DbName, [{user_ctx, UserCtx}]),
+            {ok, Db} = fabric2_db:open(DbName, chttpd:contexts_to_list(Req)),
             {ok, Info} = fabric2_db:get_db_info(Db),
             Json = ?JSON_ENCODE({[{key, DbName}, {info, {Info}}]}),
             send_chunk(Resp, AccSeparator ++ Json)


[couchdb] 03/08: Add request_ctx field to #httpd{} record to pass to fabric

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 5aa38fe38ebf426ad672c6aa6d0c789975a44ab9
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Wed Sep 25 16:47:15 2019 +0000

    Add request_ctx field to #httpd{} record to pass to fabric
    
    In order to implement end-to-end testing we need to pass some information
    through all layers. We would use a `request_ctx` map to achive that.
    The information we might want to pass is (TBD):
    - is_sampled : true | false
    - is_enabled : true | false
    - trace_id : uuid
    - parent_span_id : uuid
    
    This commit passes the `request_ctx` to `fabric2:list_dbs`.
    In subsequent commits we would pass `request_ctx` in `Options`
    to other functions in `fabric2`.
---
 src/chttpd/src/chttpd_misc.erl | 5 +++--
 src/couch/include/couch_db.hrl | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl
index 11d2c5b..e05b972 100644
--- a/src/chttpd/src/chttpd_misc.erl
+++ b/src/chttpd/src/chttpd_misc.erl
@@ -107,7 +107,7 @@ maybe_add_csp_headers(Headers, "true") ->
 maybe_add_csp_headers(Headers, _) ->
     Headers.
 
-handle_all_dbs_req(#httpd{method='GET'}=Req) ->
+handle_all_dbs_req(#httpd{method='GET', request_ctx=RequestCtx}=Req) ->
     #mrargs{
         start_key = StartKey,
         end_key = EndKey,
@@ -121,7 +121,8 @@ handle_all_dbs_req(#httpd{method='GET'}=Req) ->
         {end_key, EndKey},
         {dir, Dir},
         {limit, Limit},
-        {skip, Skip}
+        {skip, Skip},
+        {request_ctx, RequestCtx}
     ],
 
     % Eventually the Etag for this request will be derived
diff --git a/src/couch/include/couch_db.hrl b/src/couch/include/couch_db.hrl
index 830b9bc..2e831f3 100644
--- a/src/couch/include/couch_db.hrl
+++ b/src/couch/include/couch_db.hrl
@@ -98,7 +98,8 @@
     nonce,
     cors_config,
     xframe_config,
-    qs
+    qs,
+    request_ctx = #{}
 }).
 
 


[couchdb] 02/08: Identify tracing span life cycle

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 2e00ea8abbc670e29b810bdec8d9498c512ab119
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Wed Sep 25 12:17:28 2019 +0000

    Identify tracing span life cycle
    
    A note for curious observer:
      We wouldn't be finishing a span on exit because we don't
      want to spam our trace collector with client disconnects.
      The open tracing library handles this case just fine.
---
 src/chttpd/src/chttpd.erl | 46 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 36 insertions(+), 10 deletions(-)

diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index e4ddb6a..704e203 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -157,6 +157,7 @@ handle_request(MochiReq0) ->
     handle_request_int(MochiReq).
 
 handle_request_int(MochiReq) ->
+    Span = start_span(),
     Begin = os:timestamp(),
     case config:get("chttpd", "socket_options") of
     undefined ->
@@ -231,6 +232,8 @@ handle_request_int(MochiReq) ->
                 || Part <- string:tokens(RequestedPath, "/")]
     },
 
+    HttpReq1 = attach_span(HttpReq0, Span),
+
     % put small token on heap to keep requests synced to backend calls
     erlang:put(nonce, Nonce),
 
@@ -238,11 +241,11 @@ handle_request_int(MochiReq) ->
     erlang:put(dont_log_request, true),
     erlang:put(dont_log_response, true),
 
-    {HttpReq2, Response} = case before_request(HttpReq0) of
-        {ok, HttpReq1} ->
-            process_request(HttpReq1);
+    {HttpReq3, Response} = case before_request(HttpReq1) of
+        {ok, HttpReq2} ->
+            process_request(HttpReq2);
         {error, Response0} ->
-            {HttpReq0, Response0}
+            {HttpReq1, Response0}
     end,
 
     {Status, Code, Reason, Resp} = split_response(Response),
@@ -251,12 +254,13 @@ handle_request_int(MochiReq) ->
         code = Code,
         status = Status,
         response = Resp,
-        nonce = HttpReq2#httpd.nonce,
+        nonce = HttpReq3#httpd.nonce,
         reason = Reason
     },
 
-    case after_request(HttpReq2, HttpResp) of
+    case after_request(HttpReq3, HttpResp) of
         #httpd_resp{status = ok, response = Resp} ->
+            finish_span(HttpReq3, span_ok(HttpResp)),
             {ok, Resp};
         #httpd_resp{status = aborted, reason = Reason} ->
             couch_log:error("Response abnormally terminated: ~p", [Reason]),
@@ -1048,16 +1052,20 @@ send_error(#httpd{} = Req, Code, ErrorStr, ReasonStr) ->
     send_error(Req, Code, [], ErrorStr, ReasonStr, []).
 
 send_error(Req, Code, Headers, ErrorStr, ReasonStr, []) ->
-    send_json(Req, Code, Headers,
+    Return = send_json(Req, Code, Headers,
         {[{<<"error">>,  ErrorStr},
-        {<<"reason">>, ReasonStr}]});
+        {<<"reason">>, ReasonStr}]}),
+    finish_span(Req, span_error(ErrorStr, ReasonStr, [])),
+    Return;
 send_error(Req, Code, Headers, ErrorStr, ReasonStr, Stack) ->
     log_error_with_stack_trace({ErrorStr, ReasonStr, Stack}),
-    send_json(Req, Code, [stack_trace_id(Stack) | Headers],
+    Return = send_json(Req, Code, [stack_trace_id(Stack) | Headers],
         {[{<<"error">>,  ErrorStr},
         {<<"reason">>, ReasonStr} |
         case Stack of [] -> []; _ -> [{<<"ref">>, stack_hash(Stack)}] end
-    ]}).
+    ]}),
+    finish_span(Req, span_error(ErrorStr, ReasonStr, [])),
+    Return.
 
 update_timeout_stats(<<"timeout">>, #httpd{requested_path_parts = PathParts}) ->
     update_timeout_stats(PathParts);
@@ -1211,6 +1219,24 @@ get_user(#httpd{user_ctx = #user_ctx{name = User}}) ->
 get_user(#httpd{user_ctx = undefined}) ->
     "undefined".
 
+start_span() ->
+    span.
+
+attach_span(Req, _Span) ->
+    Req.
+
+trace(Req, _Fun) ->
+    Req.
+
+finish_span(_Req, _Fun) ->
+    ok.
+
+span_ok(_Resp) ->
+    fun(Span) -> Span end.
+
+span_error(_ErrorStr, _ReasonStr, []) ->
+    fun(Span) -> Span end.
+
 -ifdef(TEST).
 
 -include_lib("eunit/include/eunit.hrl").


[couchdb] 07/08: Pass request_ctx in chttpd_auth_cache

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit a269f192d788969fda2d2849706fdd3f95c94a36
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Tue Oct 1 16:10:09 2019 +0000

    Pass request_ctx in chttpd_auth_cache
---
 src/chttpd/src/chttpd_auth_cache.erl | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/chttpd/src/chttpd_auth_cache.erl b/src/chttpd/src/chttpd_auth_cache.erl
index 9eee196..5c99354 100644
--- a/src/chttpd/src/chttpd_auth_cache.erl
+++ b/src/chttpd/src/chttpd_auth_cache.erl
@@ -38,12 +38,12 @@ start_link() ->
 
 get_user_creds(Req, UserName) when is_list(UserName) ->
     get_user_creds(Req, ?l2b(UserName));
-get_user_creds(_Req, UserName) when is_binary(UserName) ->
+get_user_creds(Req, UserName) when is_binary(UserName) ->
     Resp = case couch_auth_cache:get_admin(UserName) of
     nil ->
-        get_from_cache(UserName);
+        get_from_cache(Req, UserName);
     Props ->
-        case get_from_cache(UserName) of
+        case get_from_cache(Req, UserName) of
         nil ->
             Props;
         UserProps when is_list(UserProps) ->
@@ -53,9 +53,10 @@ get_user_creds(_Req, UserName) when is_binary(UserName) ->
     end,
     maybe_validate_user_creds(Resp).
 
-update_user_creds(_Req, UserDoc, _Ctx) ->
+update_user_creds(#httpd{request_ctx = RequestCtx}, UserDoc, _Ctx) ->
+    Options = [?ADMIN_CTX, {request_ctx, RequestCtx}],
     {_, Ref} = spawn_monitor(fun() ->
-        {ok, Db} = fabric2_db:open(dbname(), [?ADMIN_CTX]),
+        {ok, Db} = fabric2_db:open(dbname(), Options),
         case fabric2_db:update_doc(Db, UserDoc) of
             {ok, _} ->
                 exit(ok);
@@ -70,7 +71,7 @@ update_user_creds(_Req, UserDoc, _Ctx) ->
             Else
     end.
 
-get_from_cache(UserName) ->
+get_from_cache(Req, UserName) ->
     try ets_lru:lookup_d(?CACHE, UserName) of
         {ok, Props} ->
             couch_stats:increment_counter([couchdb, auth_cache_hits]),
@@ -78,7 +79,7 @@ get_from_cache(UserName) ->
             Props;
         _ ->
             maybe_increment_auth_cache_miss(UserName),
-            case load_user_from_db(UserName) of
+            case load_user_from_db(Req, UserName) of
                 nil ->
                     nil;
                 Props ->
@@ -88,7 +89,7 @@ get_from_cache(UserName) ->
     catch
         error:badarg ->
             maybe_increment_auth_cache_miss(UserName),
-            load_user_from_db(UserName)
+            load_user_from_db(Req, UserName)
     end.
 
 maybe_increment_auth_cache_miss(UserName) ->
@@ -211,8 +212,9 @@ handle_config_terminate(_Server, _Reason, _State) ->
     erlang:send_after(?RELISTEN_DELAY, Dst, restart_config_listener).
 
 
-load_user_from_db(UserName) ->
-    {ok, Db} = fabric2_db:open(dbname(), [?ADMIN_CTX]),
+load_user_from_db(#httpd{request_ctx = RequestCtx}, UserName) ->
+    Options = [?ADMIN_CTX, {request_ctx, RequestCtx}],
+    {ok, Db} = fabric2_db:open(dbname(), Options),
     try fabric2_db:open_doc(Db, docid(UserName), [conflicts]) of
     {ok, Doc} ->
         {Props} = couch_doc:to_json_obj(Doc, []),


[couchdb] 01/08: Remove old clause which is no longer used

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 1186b3906b9c4611a2d1310bba7b70db20420585
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Wed Sep 25 11:03:45 2019 +0000

    Remove old clause which is no longer used
    
    The history of `send_error(_Req, {already_sent, Resp, _Error})`
    clause is bellow:
    
    - it was added on [2009/04/18](https://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?r1=762574&r2=765819&diff_format=h)
    - we triggered that clause [in couch_httpd:do](https://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?revision=642432&view=markup#l88)
    - at that time we were using inets webserver [see use of `httpd_response/3`](https://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?revision=642432&view=markup#l170)
    - The inets OTP codebase uses `already_sent` messages [here](https://github.com/erlang/otp/blob/50214f02501926fee6ec286efa68a57a47c2e531/lib/inets/src/http_server/httpd_response.erl#L220)
    
    It should be safe to remove this clause because we are not using inets anymore
    and search of `already_sent` term across all dependencies doesn't return any results.
---
 src/chttpd/src/chttpd.erl     | 3 ---
 src/couch/src/couch_httpd.erl | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index 4d32c03..e4ddb6a 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -1036,9 +1036,6 @@ error_headers(#httpd{mochi_req=MochiReq}=Req, 401=Code, ErrorStr, ReasonStr) ->
 error_headers(_, Code, _, _) ->
     {Code, []}.
 
-send_error(_Req, {already_sent, Resp, _Error}) ->
-    {ok, Resp};
-
 send_error(#httpd{} = Req, Error) ->
     update_timeout_stats(Error, Req),
 
diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl
index 3cdfc0c..10b44d1 100644
--- a/src/couch/src/couch_httpd.erl
+++ b/src/couch/src/couch_httpd.erl
@@ -977,9 +977,6 @@ error_headers(#httpd{mochi_req=MochiReq}=Req, Code, ErrorStr, ReasonStr) ->
         {Code, []}
     end.
 
-send_error(_Req, {already_sent, Resp, _Error}) ->
-    {ok, Resp};
-
 send_error(Req, Error) ->
     {Code, ErrorStr, ReasonStr} = error_info(Error),
     {Code1, Headers} = error_headers(Req, Code, ErrorStr, ReasonStr),


[couchdb] 08/08: Remove compiler warning

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit aa359eeb7dc67a0d4177ff9ef3413ca788322928
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Tue Oct 1 16:10:26 2019 +0000

    Remove compiler warning
---
 src/chttpd/src/chttpd_db.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index 08ee96b..30d2490 100644
--- a/src/chttpd/src/chttpd_db.erl
+++ b/src/chttpd/src/chttpd_db.erl
@@ -330,7 +330,7 @@ update_partition_stats(PathParts) ->
 handle_design_req(#httpd{
         path_parts=[_DbName, _Design, Name, <<"_",_/binary>> = Action | _Rest]
     }=Req, Db) ->
-    DbName = fabric2_db:name(Db),
+%%    DbName = fabric2_db:name(Db),
 %%    case ddoc_cache:open(DbName, <<"_design/", Name/binary>>) of
     case fabric2_db:open_doc(Db, <<"_design/", Name/binary>>) of
     {ok, DDoc} ->


[couchdb] 04/08: Set request_ctx on database operations

Posted by ii...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch opentracing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit f4def40cdeb4d2b01083366c3cb03b0dfe622978
Author: ILYA Khlopotov <ii...@apache.org>
AuthorDate: Tue Oct 1 13:59:09 2019 +0000

    Set request_ctx on database operations
---
 src/fabric/src/fabric2_db.erl  | 37 ++++++++++++++++++++++++++++++++++---
 src/fabric/src/fabric2_fdb.erl | 33 ++++++++++++++++++++++++++++-----
 2 files changed, 62 insertions(+), 8 deletions(-)

diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index 9ef0bd3..2c7222c 100644
--- a/src/fabric/src/fabric2_db.erl
+++ b/src/fabric/src/fabric2_db.erl
@@ -45,6 +45,7 @@
     get_security/1,
     get_update_seq/1,
     get_user_ctx/1,
+    get_request_ctx/1,
     get_uuid/1,
     %% get_purge_seq/1,
     %% get_oldest_purge_seq/1,
@@ -123,7 +124,9 @@
     validate_dbname/1,
 
     %% make_doc/5,
-    new_revid/2
+    new_revid/2,
+    contexts_to_list/1,
+    set_contexts/2
 ]).
 
 
@@ -176,8 +179,9 @@ open(DbName, Options) ->
     case fabric2_server:fetch(DbName) of
         #{} = Db ->
             Db1 = maybe_set_user_ctx(Db, Options),
-            ok = check_is_member(Db1),
-            {ok, Db1};
+            Db2 = maybe_set_request_ctx(Db1, Options),
+            ok = check_is_member(Db2),
+            {ok, Db2};
         undefined ->
             Result = fabric2_fdb:transactional(DbName, Options, fun(TxDb) ->
                 fabric2_fdb:open(TxDb, Options)
@@ -385,6 +389,8 @@ get_user_ctx(#{user_ctx := UserCtx}) ->
 get_uuid(#{uuid := UUID}) ->
     UUID.
 
+get_request_ctx(#{request_ctx := RequestCtx}) ->
+    RequestCtx.
 
 is_clustered(#{}) ->
     false.
@@ -988,6 +994,13 @@ maybe_set_user_ctx(Db, Options) ->
             Db
     end.
 
+maybe_set_request_ctx(Db, Options) ->
+    case fabric2_util:get_value(request_ctx, Options) of
+        #{} = RequestCtx ->
+            Db#{request_ctx => RequestCtx};
+        undefined ->
+            Db
+    end.
 
 is_member(Db, {SecProps}) when is_list(SecProps) ->
     case is_admin(Db, {SecProps}) of
@@ -1747,3 +1760,21 @@ stem_revisions(#{} = Db, #doc{} = Doc) ->
         true -> Doc#doc{revs = {RevPos, lists:sublist(Revs, RevsLimit)}};
         false -> Doc
     end.
+
+
+contexts_to_list(Db) ->
+    set_contexts(Db, []).
+
+set_contexts(#{} = Db, KVList0) ->
+    %% Supplied options win
+    RequestCtx = fabric2_fdb:get_request_ctx(Db),
+    UserCtx = fabric2_fdb:get_user_ctx(Db),
+    KVList1 = set_if_not_present(KVList0, request_ctx, RequestCtx),
+    KVList2 = set_if_not_present(KVList1, user_ctx, UserCtx),
+    KVList2.
+
+set_if_not_present(KVList, Key, Value) ->
+    case lists:keymember(Key, 1, KVList) of
+        true -> KVList;
+        false -> [{Key, Value} | KVList]
+    end.
\ No newline at end of file
diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl
index 5c58da4..8d464b6 100644
--- a/src/fabric/src/fabric2_fdb.erl
+++ b/src/fabric/src/fabric2_fdb.erl
@@ -31,6 +31,10 @@
     get_config/2,
     set_config/3,
 
+    get_contexts/1,
+    get_request_ctx/1,
+    get_user_ctx/1,
+
     get_stat/2,
     incr_stat/3,
 
@@ -165,6 +169,8 @@ create(#{} = Db0, Options) ->
 
     UserCtx = fabric2_util:get_value(user_ctx, Options, #user_ctx{}),
     Options1 = lists:keydelete(user_ctx, 1, Options),
+    RequestCtx = fabric2_util:get_value(request_ctx, Options, #{}),
+    Options2 = lists:keydelete(request_ctx, 1, Options),
 
     Db#{
         uuid => UUID,
@@ -174,13 +180,14 @@ create(#{} = Db0, Options) ->
         revs_limit => 1000,
         security_doc => {[]},
         user_ctx => UserCtx,
+        request_ctx => RequestCtx,
 
         validate_doc_update_funs => [],
         before_doc_update => undefined,
         after_doc_read => undefined,
         % All other db things as we add features,
 
-        db_options => Options1
+        db_options => Options2
     }.
 
 
@@ -202,6 +209,8 @@ open(#{} = Db0, Options) ->
 
     UserCtx = fabric2_util:get_value(user_ctx, Options, #user_ctx{}),
     Options1 = lists:keydelete(user_ctx, 1, Options),
+    RequestCtx = fabric2_util:get_value(request_ctx, Options1, #{}),
+    Options2 = lists:keydelete(request_ctx, 1, Options1),
 
     Db2 = Db1#{
         db_prefix => DbPrefix,
@@ -210,14 +219,14 @@ open(#{} = Db0, Options) ->
         revs_limit => 1000,
         security_doc => {[]},
         user_ctx => UserCtx,
+        request_ctx => RequestCtx,
 
         % Place holders until we implement these
         % bits.
         validate_doc_update_funs => [],
         before_doc_update => undefined,
         after_doc_read => undefined,
-
-        db_options => Options1
+        db_options => Options2
     },
 
     Db3 = lists:foldl(fun({Key, Val}, DbAcc) ->
@@ -240,10 +249,12 @@ reopen(#{} = OldDb) ->
         tx := Tx,
         name := DbName,
         db_options := Options,
-        user_ctx := UserCtx
+        user_ctx := UserCtx,
+        request_ctx := RequestCtx
     } = OldDb,
     Options1 = lists:keystore(user_ctx, 1, Options, {user_ctx, UserCtx}),
-    open(init_db(Tx, DbName, Options1), Options1).
+    Options2 = lists:keystore(request_ctx, 1, Options1, {request_ctx, RequestCtx}),
+    open(init_db(Tx, DbName, Options2), Options2).
 
 
 delete(#{} = Db) ->
@@ -374,6 +385,18 @@ set_config(#{} = Db, ConfigKey, ConfigVal) ->
     bump_db_version(Db).
 
 
+get_contexts(#{} = Db) ->
+    [
+        {user_ctx, get_user_ctx(Db)},
+        {request_ctx, get_request_ctx(Db)}
+    ].
+
+get_request_ctx(#{} = Db) ->
+    maps:get(request_ctx, Db, #{}).
+
+get_user_ctx(#{} = Db) ->
+    maps:get(user_ctx, Db, #user_ctx{}).
+
 get_stat(#{} = Db, StatKey) ->
     #{
         tx := Tx,