diff options
author | Paul J. Davis <paul.joseph.davis@gmail.com> | 2020-03-23 14:27:32 -0500 |
---|---|---|
committer | Paul J. Davis <paul.joseph.davis@gmail.com> | 2020-03-24 13:06:38 -0500 |
commit | ed83bf95de6c3eb4bf82eec5243112244f1d8277 (patch) | |
tree | f323f9266f4f84eb9117f383b0fbc9ac941686de | |
parent | 2c704acc78c4f9f7cf8bb40420be5b8c915d39b9 (diff) | |
download | couchdb-ed83bf95de6c3eb4bf82eec5243112244f1d8277.tar.gz |
Implement fabric2_db:get_design_docs/1
This is a more efficient method to get all of the design documents than
relying on fabric2_db:fold_docs which doesn't load doc bodies in
parallel.
-rw-r--r-- | src/fabric/src/fabric2_db.erl | 32 | ||||
-rw-r--r-- | src/fabric/test/fabric2_get_design_docs_tests.erl | 138 |
2 files changed, 167 insertions, 3 deletions
diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl index 129dea2d7..ca9f037ec 100644 --- a/src/fabric/src/fabric2_db.erl +++ b/src/fabric/src/fabric2_db.erl @@ -80,9 +80,7 @@ get_full_doc_info/2, get_full_doc_infos/2, get_missing_revs/2, - %% get_design_doc/2, - %% get_design_docs/1, - %% get_design_doc_count/1, + get_design_docs/1, %% get_purge_infos/2, %% get_minimum_purge_seq/1, @@ -657,6 +655,34 @@ get_missing_revs(Db, JsonIdRevs) -> {ok, AllMissing}. +get_design_docs(Db) -> + fabric2_fdb:transactional(Db, fun(TxDb) -> + #{ + db_prefix := DbPrefix + } = TxDb, + + Prefix = erlfdb_tuple:pack({?DB_ALL_DOCS}, DbPrefix), + Options = set_design_doc_keys([]), + FoldFun = fun({Key, Val}, Acc) -> + {DocId} = erlfdb_tuple:unpack(Key, Prefix), + RevId = erlfdb_tuple:unpack(Val), + Rev = #{ + rev_id => RevId, + rev_path => [] + }, + Future = fabric2_fdb:get_doc_body_future(TxDb, DocId, Rev), + [{DocId, Rev, Future} | Acc] + end, + Futures = fabric2_fdb:fold_range(TxDb, Prefix, FoldFun, [], Options), + + % Using foldl instead of map means that the design + % docs come out in sorted order. + lists:foldl(fun({DocId, Rev, Future}, Acc) -> + [fabric2_fdb:get_doc_body_wait(TxDb, DocId, Rev, Future) | Acc] + end, [], Futures) + end). + + validate_docid(<<"">>) -> throw({illegal_docid, <<"Document id must not be empty">>}); validate_docid(<<"_design/">>) -> diff --git a/src/fabric/test/fabric2_get_design_docs_tests.erl b/src/fabric/test/fabric2_get_design_docs_tests.erl new file mode 100644 index 000000000..eb227835c --- /dev/null +++ b/src/fabric/test/fabric2_get_design_docs_tests.erl @@ -0,0 +1,138 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(fabric2_get_design_docs_tests). + + +-include_lib("couch/include/couch_db.hrl"). +-include_lib("couch/include/couch_eunit.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include("fabric2_test.hrl"). + + +get_design_docs_test_() -> + { + "Test get_design_docs", + { + setup, + fun setup_all/0, + fun cleanup_all/1, + { + foreach, + fun setup/0, + fun cleanup/1, + [ + ?TDEF_FE(empty_db), + ?TDEF_FE(get_one), + ?TDEF_FE(get_two), + ?TDEF_FE(get_many), + ?TDEF_FE(get_many_with_regular_docs), + ?TDEF_FE(dont_return_deleted_ddocs) + ] + } + } + }. + + +setup_all() -> + test_util:start_couch([fabric]). + + +cleanup_all(Ctx) -> + test_util:stop_couch(Ctx). + + +setup() -> + {ok, Db} = fabric2_db:create(?tempdb(), [{user_ctx, ?ADMIN_USER}]), + Db. + + +cleanup(Db) -> + ok = fabric2_db:delete(fabric2_db:name(Db), []). + + +empty_db(Db) -> + DDocs = fabric2_db:get_design_docs(Db), + ?assertEqual([], DDocs). + + +get_one(Db) -> + DDoc = create_ddoc(Db, <<"foo">>), + DDocs = fabric2_db:get_design_docs(Db), + ?assertEqual([DDoc], DDocs). + + +get_two(Db) -> + DDoc1 = create_ddoc(Db, <<"foo">>), + DDoc2 = create_ddoc(Db, <<"bar">>), + DDocs = fabric2_db:get_design_docs(Db), + % DDocs come back sorted + ?assertEqual([DDoc2, DDoc1], DDocs). + + +get_many(Db) -> + DDocsIn = lists:map(fun(Seq) -> + Id = io_lib:format("~2..0b", [Seq]), + create_ddoc(Db, iolist_to_binary(Id)) + end, lists:seq(1, 10)), + DDocsOut = fabric2_db:get_design_docs(Db), + ?assertEqual(DDocsIn, DDocsOut). + + +get_many_with_regular_docs(Db) -> + RegularIds = [ + <<"0">>, + <<"012aCb">>, + <<"Another_doc">>, + <<"Znother_doc">>, + <<"a_doc_as_well">>, + <<"zebra_doc">> + ], + lists:foreach(fun(DocId) -> + create_doc(Db, DocId) + end, RegularIds), + DDocsIn = lists:map(fun(Seq) -> + Id = io_lib:format("~2..0b", [Seq]), + create_ddoc(Db, iolist_to_binary(Id)) + end, lists:seq(1, 10)), + DDocsOut = fabric2_db:get_design_docs(Db), + ?assertEqual(DDocsIn, DDocsOut). + + +dont_return_deleted_ddocs(Db) -> + DDocsIn = lists:flatmap(fun(Seq) -> + Id = io_lib:format("~2..0b", [Seq]), + DDoc = create_ddoc(Db, iolist_to_binary(Id)), + case Seq rem 2 == 0 of + true -> + delete_ddoc(Db, DDoc), + []; + false -> + [DDoc] + end + end, lists:seq(1, 10)), + DDocsOut = fabric2_db:get_design_docs(Db), + ?assertEqual(DDocsIn, DDocsOut). + + +create_ddoc(Db, Id) -> + create_doc(Db, <<"_design/", Id/binary>>). + + +delete_ddoc(Db, DDoc) -> + {ok, _} = fabric2_db:update_doc(Db, DDoc#doc{deleted = true}). + + +create_doc(Db, Id) -> + Doc = #doc{id = Id}, + {ok, {Pos, Rev}} = fabric2_db:update_doc(Db, Doc), + Doc#doc{revs = {Pos, [Rev]}}. |