summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul J. Davis <paul.joseph.davis@gmail.com>2020-03-23 14:27:32 -0500
committerPaul J. Davis <paul.joseph.davis@gmail.com>2020-03-24 13:06:38 -0500
commited83bf95de6c3eb4bf82eec5243112244f1d8277 (patch)
treef323f9266f4f84eb9117f383b0fbc9ac941686de
parent2c704acc78c4f9f7cf8bb40420be5b8c915d39b9 (diff)
downloadcouchdb-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.erl32
-rw-r--r--src/fabric/test/fabric2_get_design_docs_tests.erl138
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]}}.