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, []).