This is an automated email from the ASF dual-hosted git repository. davisp pushed a commit to branch prototype/fdb-layer-get-dbs-info in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit d3b1e1d09236e1f0c0acc0066703538ce11a65a8 Author: Paul J. Davis <paul.joseph.da...@gmail.com> AuthorDate: Tue Dec 3 12:45:36 2019 -0600 Implement `fabric2_db:list_dbs_info/1,2,3` This API allows for listing all database info blobs in a single request. It accepts the same parameters as `_all_dbs` for controlling pagination of results and so on. --- src/fabric/src/fabric2_db.erl | 100 +++++++++++++++++++++++++----- src/fabric/src/fabric2_fdb.erl | 11 ++++ src/fabric/test/fabric2_db_crud_tests.erl | 31 ++++++++- 3 files changed, 126 insertions(+), 16 deletions(-) diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl index 88840e7..688f794 100644 --- a/src/fabric/src/fabric2_db.erl +++ b/src/fabric/src/fabric2_db.erl @@ -22,6 +22,10 @@ list_dbs/1, list_dbs/3, + list_dbs_info/0, + list_dbs_info/1, + list_dbs_info/3, + check_is_admin/1, check_is_member/1, @@ -238,6 +242,46 @@ list_dbs(UserFun, UserAcc0, Options) -> end). +list_dbs_info() -> + list_dbs_info([]). + + +list_dbs_info(Options) -> + Callback = fun(Value, Acc) -> + NewAcc = case Value of + {meta, _} -> Acc; + {row, DbInfo} -> [DbInfo | Acc]; + complete -> Acc + end, + {ok, NewAcc} + end, + {ok, DbInfos} = list_dbs_info(Callback, [], Options), + {ok, lists:reverse(DbInfos)}. + + +list_dbs_info(UserFun, UserAcc0, Options) -> + FoldFun = fun(DbName, InfoFuture, {FutureQ, Count, Acc}) -> + NewFutureQ = queue:in({DbName, InfoFuture}, FutureQ), + drain_info_futures(NewFutureQ, Count + 1, UserFun, Acc) + end, + fabric2_fdb:transactional(fun(Tx) -> + try + UserAcc1 = maybe_stop(UserFun({meta, []}, UserAcc0)), + InitAcc = {queue:new(), 0, UserAcc1}, + {FinalFutureQ, _, UserAcc2} = fabric2_fdb:list_dbs_info( + Tx, + FoldFun, + InitAcc, + Options + ), + UserAcc3 = drain_all_info_futures(FinalFutureQ, UserFun, UserAcc2), + {ok, maybe_stop(UserFun(complete, UserAcc3))} + catch throw:{stop, FinalUserAcc} -> + {ok, FinalUserAcc} + end + end). + + is_admin(Db, {SecProps}) when is_list(SecProps) -> case fabric2_db_plugin:check_is_admin(Db) of true -> @@ -313,21 +357,7 @@ get_db_info(#{} = Db) -> DbProps = fabric2_fdb:transactional(Db, fun(TxDb) -> fabric2_fdb:get_info(TxDb) end), - - BaseProps = [ - {cluster, {[{n, 0}, {q, 0}, {r, 0}, {w, 0}]}}, - {compact_running, false}, - {data_size, 0}, - {db_name, name(Db)}, - {disk_format_version, 0}, - {disk_size, 0}, - {instance_start_time, <<"0">>}, - {purge_seq, 0} - ], - - {ok, lists:foldl(fun({Key, Val}, Acc) -> - lists:keystore(Key, 1, Acc, {Key, Val}) - end, BaseProps, DbProps)}. + {ok, make_db_info(name(Db), DbProps)}. get_del_doc_count(#{} = Db) -> @@ -944,6 +974,46 @@ maybe_add_sys_db_callbacks(Db) -> }. +make_db_info(DbName, Props) -> + BaseProps = [ + {cluster, {[{n, 0}, {q, 0}, {r, 0}, {w, 0}]}}, + {compact_running, false}, + {data_size, 0}, + {db_name, DbName}, + {disk_format_version, 0}, + {disk_size, 0}, + {instance_start_time, <<"0">>}, + {purge_seq, 0} + ], + + lists:foldl(fun({Key, Val}, Acc) -> + lists:keystore(Key, 1, Acc, {Key, Val}) + end, BaseProps, Props). + + +drain_info_futures(FutureQ, Count, _UserFun, Acc) when Count < 100 -> + {FutureQ, Count, Acc}; + +drain_info_futures(FutureQ, Count, UserFun, Acc) when Count >= 100 -> + {{value, {DbName, Future}}, RestQ} = queue:out(FutureQ), + InfoProps = fabric2_fdb:get_info_wait(Future), + DbInfo = make_db_info(DbName, InfoProps), + NewAcc = maybe_stop(UserFun({row, DbInfo}, Acc)), + {RestQ, Count - 1, NewAcc}. + + +drain_all_info_futures(FutureQ, UserFun, Acc) -> + case queue:out(FutureQ) of + {{value, {DbName, Future}}, RestQ} -> + InfoProps = fabric2_fdb:get_info_wait(Future), + DbInfo = make_db_info(DbName, InfoProps), + NewAcc = maybe_stop(UserFun({row, DbInfo}, Acc)), + drain_all_info_futures(RestQ, UserFun, NewAcc); + {empty, _} -> + Acc + end. + + new_revid(Db, Doc) -> #doc{ id = DocId, diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl index f8efbd2..59ea369 100644 --- a/src/fabric/src/fabric2_fdb.erl +++ b/src/fabric/src/fabric2_fdb.erl @@ -27,6 +27,7 @@ get_dir/1, list_dbs/4, + list_dbs_info/4, get_info/1, get_info_future/2, @@ -323,6 +324,16 @@ list_dbs(Tx, Callback, AccIn, Options) -> end, AccIn, Options). +list_dbs_info(Tx, Callback, AccIn, Options) -> + LayerPrefix = get_dir(Tx), + Prefix = erlfdb_tuple:pack({?ALL_DBS}, LayerPrefix), + fold_range({tx, Tx}, Prefix, fun({DbNameKey, DbPrefix}, Acc) -> + {DbName} = erlfdb_tuple:unpack(DbNameKey, Prefix), + InfoFuture = get_info_future(Tx, DbPrefix), + Callback(DbName, InfoFuture, Acc) + end, AccIn, Options). + + get_info(#{} = Db) -> #{ tx := Tx, diff --git a/src/fabric/test/fabric2_db_crud_tests.erl b/src/fabric/test/fabric2_db_crud_tests.erl index 24deeb2..34d16f3 100644 --- a/src/fabric/test/fabric2_db_crud_tests.erl +++ b/src/fabric/test/fabric2_db_crud_tests.erl @@ -31,7 +31,8 @@ crud_test_() -> ?TDEF(create_db), ?TDEF(open_db), ?TDEF(delete_db), - ?TDEF(list_dbs) + ?TDEF(list_dbs), + ?TDEF(list_dbs_info) ] } }. @@ -86,3 +87,31 @@ list_dbs() -> ?assertEqual(ok, fabric2_db:delete(DbName, [])), AllDbs3 = fabric2_db:list_dbs(), ?assert(not lists:member(DbName, AllDbs3)). + + +list_dbs_info() -> + DbName = ?tempdb(), + {ok, AllDbInfos1} = fabric2_db:list_dbs_info(), + + ?assert(is_list(AllDbInfos1)), + ?assert(not is_db_info_member(DbName, AllDbInfos1)), + + ?assertMatch({ok, _}, fabric2_db:create(DbName, [])), + {ok, AllDbInfos2} = fabric2_db:list_dbs_info(), + ?assert(is_db_info_member(DbName, AllDbInfos2)), + + ?assertEqual(ok, fabric2_db:delete(DbName, [])), + {ok, AllDbInfos3} = fabric2_db:list_dbs_info(), + ?assert(not is_db_info_member(DbName, AllDbInfos3)). + + +is_db_info_member(_, []) -> + false; + +is_db_info_member(DbName, [DbInfo | RestInfos]) -> + case lists:keyfind(db_name, 1, DbInfo) of + {db_name, DbName} -> + true; + _E -> + is_db_info_member(DbName, RestInfos) + end.