Repository: couchdb-fabric
Updated Branches:
  refs/heads/merge-diff-from-cloudant-fork [created] ece2b2ed9


Provide an access to a document info

The functions `get_doc_info/3` and `get_full_doc_info/3` were
added to API to provide an access to the records `#doc_info{}`
and `#full_doc_info{}` accordingly.

The functions are re-using `fabric_open_doc` coordinator
and consequently are the subject of the same read quorum rules
as `open_doc/3` function. However the info functions do not trigger
read repair on a not fully complete quorum.

Function `get_full_doc_info/3` accepts an option `deleted` to
allow to provide an information for a deleted document,
similar to `open_doc/3`.

FogBugz: 12933

This is a cherry-pick of:

https://github.com/cloudant/fabric/commit/c85569287ad8f86122b47775adc2ab9218db0322

Conflicts:
        src/fabric_rpc.erl


Project: http://git-wip-us.apache.org/repos/asf/couchdb-fabric/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fabric/commit/d7d6be85
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fabric/tree/d7d6be85
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fabric/diff/d7d6be85

Branch: refs/heads/merge-diff-from-cloudant-fork
Commit: d7d6be8535443fd3ddbe65c0a6a35534b4b98e29
Parents: d44f7fb
Author: Eric Avdey <e...@eiri.ca>
Authored: Mon Nov 10 20:12:42 2014 -0400
Committer: Mike Wallace <mikewall...@apache.org>
Committed: Wed Jun 3 14:54:21 2015 +0100

----------------------------------------------------------------------
 src/fabric.erl          | 34 ++++++++++++++++++++--
 src/fabric_doc_open.erl | 68 +++++++++++++++++++++++++++++++++++++++++---
 src/fabric_rpc.erl      | 10 +++++--
 3 files changed, 103 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fabric/blob/d7d6be85/src/fabric.erl
----------------------------------------------------------------------
diff --git a/src/fabric.erl b/src/fabric.erl
index 958e63c..25205f8 100644
--- a/src/fabric.erl
+++ b/src/fabric.erl
@@ -23,8 +23,9 @@
     get_security/2, get_all_security/1, get_all_security/2]).
 
 % Documents
--export([open_doc/3, open_revs/4, get_missing_revs/2, get_missing_revs/3,
-    update_doc/3, update_docs/3, purge_docs/2, att_receiver/2]).
+-export([open_doc/3, open_revs/4, get_doc_info/3, get_full_doc_info/3,
+    get_missing_revs/2, get_missing_revs/3, update_doc/3, update_docs/3,
+    purge_docs/2, att_receiver/2]).
 
 % Views
 -export([all_docs/4, all_docs/5, changes/4, query_view/3, query_view/4,
@@ -161,7 +162,12 @@ get_all_security(DbName, Options) ->
     {error, any()} |
     {error, any() | any()}.
 open_doc(DbName, Id, Options) ->
-    fabric_doc_open:go(dbname(DbName), docid(Id), opts(Options)).
+    case proplists:get_value(doc_info, Options) of
+    undefined ->
+        fabric_doc_open:go(dbname(DbName), docid(Id), opts(Options));
+    Else ->
+        {error, {invalid_option, {doc_info, Else}}}
+    end.
 
 %% @doc retrieve a collection of revisions, possible all
 -spec open_revs(dbname(), docid(), [revision()] | all, [option()]) ->
@@ -172,6 +178,28 @@ open_doc(DbName, Id, Options) ->
 open_revs(DbName, Id, Revs, Options) ->
     fabric_doc_open_revs:go(dbname(DbName), docid(Id), Revs, opts(Options)).
 
+%% @doc Retrieves an information on a document with a given id
+-spec get_doc_info(dbname(), docid(), [options()]) ->
+    {ok, #doc_info{}} |
+    {not_found, missing} |
+    {timeout, any()} |
+    {error, any()} |
+    {error, any() | any()}.
+get_doc_info(DbName, Id, Options) ->
+    Options1 = [doc_info|Options],
+    fabric_doc_open:go(dbname(DbName), docid(Id), opts(Options1)).
+
+%% @doc Retrieves a full information on a document with a given id
+-spec get_full_doc_info(dbname(), docid(), [options()]) ->
+    {ok, #full_doc_info{}} |
+    {not_found, missing | deleted} |
+    {timeout, any()} |
+    {error, any()} |
+    {error, any() | any()}.
+get_full_doc_info(DbName, Id, Options) ->
+    Options1 = [{doc_info, full}|Options],
+    fabric_doc_open:go(dbname(DbName), docid(Id), opts(Options1)).
+
 %% @equiv get_missing_revs(DbName, IdsRevs, [])
 get_missing_revs(DbName, IdsRevs) ->
     get_missing_revs(DbName, IdsRevs, []).

http://git-wip-us.apache.org/repos/asf/couchdb-fabric/blob/d7d6be85/src/fabric_doc_open.erl
----------------------------------------------------------------------
diff --git a/src/fabric_doc_open.erl b/src/fabric_doc_open.erl
index c7d90a4..1607946 100644
--- a/src/fabric_doc_open.erl
+++ b/src/fabric_doc_open.erl
@@ -30,7 +30,12 @@
 
 
 go(DbName, Id, Options) ->
-    Workers = fabric_util:submit_jobs(mem3:shards(DbName,Id), open_doc,
+    Handler = case proplists:get_value(doc_info, Options) of
+    true -> get_doc_info;
+    full -> get_full_doc_info;
+    undefined -> open_doc
+    end,
+    Workers = fabric_util:submit_jobs(mem3:shards(DbName,Id), Handler,
         [Id, [deleted|Options]]),
     SuppressDeletedDoc = not lists:member(deleted, Options),
     N = mem3:n(DbName),
@@ -44,11 +49,15 @@ go(DbName, Id, Options) ->
     },
     RexiMon = fabric_util:create_monitors(Workers),
     try fabric_util:recv(Workers, #shard.ref, fun handle_message/3, Acc0) of
-    {ok, #acc{}=Acc} ->
+    {ok, #acc{}=Acc} when Handler =:= open_doc ->
         Reply = handle_response(Acc),
         format_reply(Reply, SuppressDeletedDoc);
+    {ok, #acc{state = r_not_met}} ->
+        {error, quorum_not_met};
+    {ok, #acc{q_reply = QuorumReply}} ->
+        format_reply(QuorumReply, SuppressDeletedDoc);
     {timeout, #acc{workers=DefunctWorkers}} ->
-        fabric_util:log_timeout(DefunctWorkers, "open_doc"),
+        fabric_util:log_timeout(DefunctWorkers, atom_to_list(Handler)),
         {error, timeout};
     Error ->
         Error
@@ -156,12 +165,15 @@ choose_reply(Docs) ->
     end, Docs),
     {ok, Winner}.
 
+format_reply({ok, #full_doc_info{deleted=true}}, true) ->
+    {not_found, deleted};
 format_reply({ok, #doc{deleted=true}}, true) ->
     {not_found, deleted};
+format_reply(not_found, _) ->
+    {not_found, missing};
 format_reply(Else, _) ->
     Else.
 
-
 is_r_met_test() ->
     Workers0 = [],
     Workers1 = [nil],
@@ -472,6 +484,54 @@ handle_response_quorum_met_test() ->
     stop_meck_(),
     ok.
 
+get_doc_info_test() ->
+    start_meck_(),
+    meck:new([mem3, rexi_monitor, fabric_util]),
+    meck:expect(twig, log, fun(_, _, _) -> ok end),
+    meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end),
+    meck:expect(couch_stats, increment_counter, fun(_) -> ok end),
+    meck:expect(fabric_util, submit_jobs, fun(_, _, _) -> ok end),
+    meck:expect(fabric_util, create_monitors, fun(_) -> ok end),
+    meck:expect(rexi_monitor, stop, fun(_) -> ok end),
+    meck:expect(mem3, shards, fun(_, _) -> ok end),
+    meck:expect(mem3, n, fun(_) -> 3 end),
+    meck:expect(mem3, quorum, fun(_) -> 2 end),
+
+    meck:expect(fabric_util, recv, fun(_, _, _, _) ->
+        {ok, #acc{state = r_not_met}}
+    end),
+    Rsp1 = fabric_doc_open:go("test", "one", [doc_info]),
+    ?assertEqual({error, quorum_not_met}, Rsp1),
+
+    Rsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]),
+    ?assertEqual({error, quorum_not_met}, Rsp2),
+
+    meck:expect(fabric_util, recv, fun(_, _, _, _) ->
+        {ok, #acc{state = r_met, q_reply = not_found}}
+    end),
+    MissingRsp1 = fabric_doc_open:go("test", "one", [doc_info]),
+    ?assertEqual({not_found, missing}, MissingRsp1),
+    MissingRsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]),
+    ?assertEqual({not_found, missing}, MissingRsp2),
+
+    meck:expect(fabric_util, recv, fun(_, _, _, _) ->
+        A = #doc_info{},
+        {ok, #acc{state = r_met, q_reply = {ok, A}}}
+    end),
+    {ok, Rec1} = fabric_doc_open:go("test", "one", [doc_info]),
+    ?assert(is_record(Rec1, doc_info)),
+
+    meck:expect(fabric_util, recv, fun(_, _, _, _) ->
+        A = #full_doc_info{deleted = true},
+        {ok, #acc{state = r_met, q_reply = {ok, A}}}
+    end),
+    Rsp3 = fabric_doc_open:go("test", "one", [{doc_info, full}]),
+    ?assertEqual({not_found, deleted}, Rsp3),
+    {ok, Rec2} = fabric_doc_open:go("test", "one", [{doc_info, full},deleted]),
+    ?assert(is_record(Rec2, full_doc_info)),
+
+    meck:unload([mem3, rexi_monitor, fabric_util]),
+    stop_meck_().
 
 start_meck_() ->
     meck:new([couch_log, rexi, fabric, couch_stats]).

http://git-wip-us.apache.org/repos/asf/couchdb-fabric/blob/d7d6be85/src/fabric_rpc.erl
----------------------------------------------------------------------
diff --git a/src/fabric_rpc.erl b/src/fabric_rpc.erl
index eeaebd1..057dec3 100644
--- a/src/fabric_rpc.erl
+++ b/src/fabric_rpc.erl
@@ -13,8 +13,8 @@
 -module(fabric_rpc).
 
 -export([get_db_info/1, get_doc_count/1, get_update_seq/1]).
--export([open_doc/3, open_revs/4, get_missing_revs/2, get_missing_revs/3,
-    update_docs/3]).
+-export([open_doc/3, open_revs/4, get_doc_info/3, get_full_doc_info/3,
+    get_missing_revs/2, get_missing_revs/3, update_docs/3]).
 -export([all_docs/3, changes/3, map_view/4, reduce_view/4, group_info/2]).
 -export([create_db/1, delete_db/1, reset_validation_funs/1, set_security/3,
     set_revs_limit/3, create_shard_db_doc/2, delete_shard_db_doc/2]).
@@ -174,6 +174,12 @@ open_doc(DbName, DocId, Options) ->
 open_revs(DbName, Id, Revs, Options) ->
     with_db(DbName, Options, {couch_db, open_doc_revs, [Id, Revs, Options]}).
 
+get_full_doc_info(DbName, DocId, Options) ->
+    with_db(DbName, Options, {couch_db, get_full_doc_info, [DocId]}).
+
+get_doc_info(DbName, DocId, Options) ->
+    with_db(DbName, Options, {couch_db, get_doc_info, [DocId]}).
+
 get_missing_revs(DbName, IdRevsList) ->
     get_missing_revs(DbName, IdRevsList, []).
 

Reply via email to