diff options
author | jiangph <jiangph@cn.ibm.com> | 2020-04-02 20:56:16 +0800 |
---|---|---|
committer | jiangph <jiangph@cn.ibm.com> | 2020-04-02 21:05:09 +0800 |
commit | 031cb851696ec801b83faaa6b9d42add64a70a18 (patch) | |
tree | b95b617a532e4b94cc7080207bf83e0e2c9007e1 | |
parent | b1d2b08afb80ca9f8d8f4b9ca37ad65cd54b3352 (diff) | |
download | couchdb-031cb851696ec801b83faaa6b9d42add64a70a18.tar.gz |
address Paul's comments
-rw-r--r-- | src/chttpd/src/chttpd_misc.erl | 2 | ||||
-rw-r--r-- | src/chttpd/test/eunit/chttpd_deleted_dbs_test.erl | 104 | ||||
-rw-r--r-- | src/fabric/src/fabric2_db.erl | 85 | ||||
-rw-r--r-- | src/fabric/src/fabric2_fdb.erl | 7 | ||||
-rw-r--r-- | src/fabric/test/fabric2_db_crud_tests.erl | 22 |
5 files changed, 157 insertions, 63 deletions
diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl index 82f923641..415c6399c 100644 --- a/src/chttpd/src/chttpd_misc.erl +++ b/src/chttpd/src/chttpd_misc.erl @@ -181,7 +181,7 @@ deleted_dbs_get_req(#httpd{method='GET'}=Req) -> {ok, Resp} = chttpd:etag_respond(Req, Etag, fun() -> {ok, Resp} = chttpd:start_delayed_json_response(Req, 200, [{"ETag",Etag}]), - Callback = fun all_dbs_callback/2, + Callback = fun dbs_info_callback/2, Acc = #vacc{req=Req,resp=Resp}, fabric2_db:list_deleted_dbs(Callback, Acc, Options) end), diff --git a/src/chttpd/test/eunit/chttpd_deleted_dbs_test.erl b/src/chttpd/test/eunit/chttpd_deleted_dbs_test.erl index bf8808614..3426b0d03 100644 --- a/src/chttpd/test/eunit/chttpd_deleted_dbs_test.erl +++ b/src/chttpd/test/eunit/chttpd_deleted_dbs_test.erl @@ -28,28 +28,34 @@ setup() -> Port = mochiweb_socket_server:get(chttpd, port), lists:concat(["http://", Addr, ":", Port, "/"]). + teardown(_Url) -> ok = config:delete("couchdb", "enable_database_recovery", false), ok = config:delete("admins", ?USER, _Persist=false). + create_db(Url) -> {ok, Status, _, _} = test_request:put(Url, [?CONTENT_JSON, ?AUTH], "{}"), ?assert(Status =:= 201 orelse Status =:= 202). + delete_db(Url) -> {ok, 200, _, _} = test_request:delete(Url, [?AUTH]). + deleted_dbs_test_() -> { "chttpd deleted dbs tests", { setup, - fun chttpd_test_util:start_couch/0, fun chttpd_test_util:stop_couch/1, + fun chttpd_test_util:start_couch/0, + fun chttpd_test_util:stop_couch/1, { foreach, - fun setup/0, fun teardown/1, + fun setup/0, + fun teardown/1, [ - fun should_return_error_for_deleted_dbs/1, + fun should_return_error_for_unsupported_method/1, fun should_list_deleted_dbs/1, fun should_list_deleted_dbs_info/1, fun should_undelete_db/1, @@ -62,19 +68,16 @@ deleted_dbs_test_() -> }. -should_return_error_for_deleted_dbs(Url) -> +should_return_error_for_unsupported_method(Url) -> ?_test(begin create_and_delete_db(Url), - NewDoc = "{\"keys\": [\"db1\"]}", - {ok, Code, _, ResultBody} = test_request:post(Url ++ "/_deleted_dbs/", - [?CONTENT_JSON, ?AUTH], NewDoc), - - {Body} = jiffy:decode(ResultBody), - [ - ?assertEqual(<<"bad_request">>, - couch_util:get_value(<<"error">>, Body)), - ?assertEqual(400, Code) - ] + {ok, Code, _, Body} = test_request:delete(Url ++ "/_deleted_dbs/", + [?CONTENT_JSON, ?AUTH]), + + {ResultBody} = jiffy:decode(Body), + ?assertEqual(<<"method_not_allowed">>, + couch_util:get_value(<<"error">>, ResultBody)), + ?assertEqual(405, Code) end). @@ -85,8 +88,10 @@ should_list_deleted_dbs(Url) -> {ok, _, _, ResultBody} = test_request:get(Url ++ "/_deleted_dbs/", [?CONTENT_JSON, ?AUTH]), BodyJson = jiffy:decode(ResultBody), - ?assertEqual(true, lists:member(DbName1, BodyJson)), - ?assertEqual(true, lists:member(DbName2, BodyJson)) + DeletedDbs = get_deleted_dbs(BodyJson), + + ?assertEqual(true, lists:member(DbName1, DeletedDbs)), + ?assertEqual(true, lists:member(DbName2, DeletedDbs)) end). @@ -113,15 +118,18 @@ should_undelete_db(Url) -> {Db1Data} = lists:nth(1, BodyJson), TimeStamp = couch_util:get_value(<<"timestamp">>, Db1Data), - NewDoc = "{\"undelete\": {\"source\": \"" ++ ?b2l(DbName) ++ - "\", \"source_timestamp\":\"" ++ ?b2l(TimeStamp) ++ "\"}}", + ErlJSON = {[{undelete, {[ + {source, DbName}, + {source_timestamp, TimeStamp} + ]}}]}, + Body = jiffy:encode(ErlJSON), {ok, Status, _, _} = test_request:post(Url ++ "/_deleted_dbs", - [?CONTENT_JSON, ?AUTH], NewDoc), + [?CONTENT_JSON, ?AUTH], Body), ?assertEqual(200, Status), {ok, Status2, _, _} = test_request:get(Url ++ DbName, - [?CONTENT_JSON, ?AUTH], NewDoc), + [?CONTENT_JSON, ?AUTH]), ?assertEqual(200, Status2) end). @@ -136,12 +144,19 @@ should_remove_deleted_db(Url) -> {Db1Data} = lists:nth(1, BodyJson), TimeStamp = couch_util:get_value(<<"timestamp">>, Db1Data), - NewDoc = "{\"delete\": {\"source\": \"" ++ ?b2l(DbName) ++ - "\", \"source_timestamp\":\"" ++ ?b2l(TimeStamp) ++ "\"}}", + ErlJSON = {[{delete, {[ + {source, DbName}, + {source_timestamp, TimeStamp} + ]}}]}, + Body = jiffy:encode(ErlJSON), {ok, Status, _, _} = test_request:post(Url ++ "/_deleted_dbs", - [?CONTENT_JSON, ?AUTH], NewDoc), - ?assertEqual(200, Status) + [?CONTENT_JSON, ?AUTH], Body), + ?assertEqual(200, Status), + + {ok, _, _, ResultBody2} = test_request:get(Url ++ "/_deleted_dbs/" ++ + DbName, [?CONTENT_JSON, ?AUTH]), + ?assertEqual([], jiffy:decode(ResultBody2)) end). @@ -156,12 +171,15 @@ should_undelete_db_to_target_db(Url) -> TimeStamp = couch_util:get_value(<<"timestamp">>, Db1Data), NewDbName = ?tempdb(), - NewDoc = "{\"undelete\": {\"source\": \"" ++ ?b2l(DbName) ++ - "\", \"source_timestamp\":\"" ++ ?b2l(TimeStamp) ++ - "\", \"target\": \"" ++ ?b2l(NewDbName) ++ "\" }}", + ErlJSON = {[{undelete, {[ + {source, DbName}, + {source_timestamp, TimeStamp}, + {target, NewDbName} + ]}}]}, + Body = jiffy:encode(ErlJSON), {ok, RC2, _, _} = test_request:post(Url ++ "/_deleted_dbs", - [?CONTENT_JSON, ?AUTH], NewDoc), + [?CONTENT_JSON, ?AUTH], Body), ?assertEqual(200, RC2), {ok, RC3, _, _} = test_request:get(Url ++ NewDbName, @@ -182,18 +200,19 @@ should_not_undelete_db_to_existing_db(Url) -> NewDbName = ?tempdb(), create_db(Url ++ NewDbName), - NewDoc = "{\"undelete\": {\"source\": \"" ++ ?b2l(DbName) ++ - "\", \"source_timestamp\":\"" ++ ?b2l(TimeStamp) ++ - "\", \"target\": \"" ++ ?b2l(NewDbName) ++ "\" }}", - - {ok, RC, _, Body} = test_request:post(Url ++ "/_deleted_dbs", - [?CONTENT_JSON, ?AUTH], NewDoc), - {JsonBody} = jiffy:decode(Body), - [ - ?assertEqual(<<"file_exists">>, + ErlJSON = {[{undelete, {[ + {source, DbName}, + {source_timestamp, TimeStamp}, + {target, NewDbName} + ]}}]}, + Body = jiffy:encode(ErlJSON), + + {ok, RC, _, ResultBody2} = test_request:post(Url ++ "/_deleted_dbs", + [?CONTENT_JSON, ?AUTH], Body), + {JsonBody} = jiffy:decode(ResultBody2), + ?assertEqual(<<"file_exists">>, couch_util:get_value(<<"error">>, JsonBody)), - ?assertEqual(412, RC) - ] + ?assertEqual(412, RC) end). @@ -204,3 +223,10 @@ create_and_delete_db(BaseUrl) -> ok = config:set("couchdb", "enable_database_recovery", "true", false), delete_db(DbUrl), DbName. + + +get_deleted_dbs(DeletedDbInfos) -> + lists:foldl(fun({DbInfo}, Acc) -> + DbName = couch_util:get_value(<<"key">>, DbInfo), + [DbName | Acc] + end, [], DeletedDbInfos). diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl index 9acb257a5..cf91bb703 100644 --- a/src/fabric/src/fabric2_db.erl +++ b/src/fabric/src/fabric2_db.erl @@ -282,27 +282,39 @@ list_deleted_dbs() -> list_deleted_dbs(Options) -> - Callback = fun(DbName, Acc) -> [DbName | Acc] end, - DbNames = fabric2_fdb:transactional(fun(Tx) -> - fabric2_fdb:list_deleted_dbs(Tx, Callback, [], Options) - end), - lists:reverse(DbNames). + Callback = fun(Value, Acc) -> + NewAcc = case Value of + {meta, _} -> Acc; + {row, DbInfo} -> [DbInfo | Acc]; + complete -> Acc + end, + {ok, NewAcc} + end, + {ok, DbInfos} = list_deleted_dbs(Callback, [], Options), + {ok, lists:reverse(DbInfos)}. list_deleted_dbs(UserFun, UserAcc0, Options) -> - FoldFun = fun - (DbName, Acc) -> maybe_stop(UserFun({row, [{id, DbName}]}, Acc)) + FoldFun = fun(DbName, TimeStamp, InfoFuture, {FutureQ, Count, Acc}) -> + NewFutureQ = queue:in({DbName, TimeStamp, InfoFuture}, FutureQ), + drain_deleted_dbs_futures(NewFutureQ, Count + 1, UserFun, Acc) end, fabric2_fdb:transactional(fun(Tx) -> try UserAcc1 = maybe_stop(UserFun({meta, []}, UserAcc0)), - UserAcc2 = fabric2_fdb:list_deleted_dbs( - Tx, - FoldFun, - UserAcc1, - Options - ), - {ok, maybe_stop(UserFun(complete, UserAcc2))} + InitAcc = {queue:new(), 0, UserAcc1}, + {FinalFutureQ, _, UserAcc2} = fabric2_fdb:list_deleted_dbs( + Tx, + FoldFun, + InitAcc, + Options + ), + UserAcc3 = drain_all_deleted_dbs_futures( + FinalFutureQ, + UserFun, + UserAcc2 + ), + {ok, maybe_stop(UserFun(complete, UserAcc3))} catch throw:{stop, FinalUserAcc} -> {ok, FinalUserAcc} end @@ -1089,6 +1101,51 @@ drain_all_info_futures(FutureQ, UserFun, Acc) -> Acc end. +make_deleted_dbs_info(DbName, TimeStamp, Props) -> + BaseProps = [ + {key, DbName}, + {timestamp, TimeStamp}, + {value, {[ + {cluster, {[{n, 0}, {q, 0}, {r, 0}, {w, 0}]}}, + {compact_running, false}, + {data_size, 0}, + {db_name, DbName}, + {timestamp, TimeStamp}, + {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_deleted_dbs_futures(FutureQ, Count, _UserFun, Acc) when Count < 100 -> + {FutureQ, Count, Acc}; + +drain_deleted_dbs_futures(FutureQ, Count, UserFun, Acc) when Count >= 100 -> + {{value, {DbName, TimeStamp, Future}}, RestQ} = queue:out(FutureQ), + InfoProps = fabric2_fdb:get_info_wait(Future), + DbInfo = make_deleted_dbs_info(DbName, TimeStamp, InfoProps), + NewAcc = maybe_stop(UserFun({row, DbInfo}, Acc)), + {RestQ, Count - 1, NewAcc}. + + +drain_all_deleted_dbs_futures(FutureQ, UserFun, Acc) -> + case queue:out(FutureQ) of + {{value, {DbName, TimeStamp, Future}}, RestQ} -> + InfoProps = fabric2_fdb:get_info_wait(Future), + DbInfo = make_deleted_dbs_info(DbName, TimeStamp, InfoProps), + NewAcc = maybe_stop(UserFun({row, DbInfo}, Acc)), + drain_all_deleted_dbs_futures(RestQ, UserFun, NewAcc); + {empty, _} -> + Acc + end. + new_revid(Db, Doc) -> #doc{ diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl index 6f3b9a227..97b4eab0d 100644 --- a/src/fabric/src/fabric2_fdb.erl +++ b/src/fabric/src/fabric2_fdb.erl @@ -450,9 +450,10 @@ list_deleted_dbs(Tx, Callback, AccIn, Options0) -> end, LayerPrefix = get_dir(Tx), Prefix = erlfdb_tuple:pack({?DELETED_DBS}, LayerPrefix), - fold_range({tx, Tx}, Prefix, fun({K, _V}, Acc) -> - {DbName, _Timestamp} = erlfdb_tuple:unpack(K, Prefix), - Callback(DbName, Acc) + fold_range({tx, Tx}, Prefix, fun({DbKey, DbPrefix}, Acc) -> + {DbName, TimeStamp} = erlfdb_tuple:unpack(DbKey, Prefix), + InfoFuture = get_info_future(Tx, DbPrefix), + Callback(DbName, TimeStamp, InfoFuture, Acc) end, AccIn, Options). diff --git a/src/fabric/test/fabric2_db_crud_tests.erl b/src/fabric/test/fabric2_db_crud_tests.erl index bd19d9b11..4d9a01224 100644 --- a/src/fabric/test/fabric2_db_crud_tests.erl +++ b/src/fabric/test/fabric2_db_crud_tests.erl @@ -190,7 +190,8 @@ remove_deleted_db(_) -> fabric2_db:delete(BadDbName, [{deleted_at, Timestamp}])), ok = fabric2_db:delete(DbName, [{deleted_at, Timestamp}]), - DeletedDbs = fabric2_db:list_deleted_dbs(), + {ok, DeletedDbInfos} = fabric2_db:list_deleted_dbs(), + DeletedDbs = get_deleted_dbs(DeletedDbInfos), ?assert(not lists:member(DbName, DeletedDbs)). @@ -243,6 +244,7 @@ old_db_handle(_) -> ?assertMatch({ok, _}, fabric2_db:create(DbName5, [])), {ok, Db5} = fabric2_db:open(DbName5, []), ?assertMatch({ok, _}, fabric2_db:get_db_info(Db5)), + ok = config:set("couchdb", "enable_database_recovery", "false", false), ?assertEqual(ok, fabric2_db:delete(DbName5, [])), ?assertMatch({ok, _}, fabric2_db:create(DbName5, [])), ?assertError(database_does_not_exist, fabric2_db:get_db_info(Db5)). @@ -300,8 +302,9 @@ list_deleted_dbs(_) -> AllDbs3 = fabric2_db:list_dbs(), ?assert(not lists:member(DbName, AllDbs3)), - AllDbs4 = fabric2_db:list_deleted_dbs(), - ?assert(lists:member(DbName, AllDbs4)). + {ok, DeletedDbsInfo} = fabric2_db:list_deleted_dbs(), + DeletedDbs4 = get_deleted_dbs(DeletedDbsInfo), + ?assert(lists:member(DbName, DeletedDbs4)). list_deleted_dbs_user_fun(_) -> @@ -311,10 +314,11 @@ list_deleted_dbs_user_fun(_) -> UserFun = fun(Row, Acc) -> {ok, [Row | Acc]} end, {ok, UserAcc} = fabric2_db:list_deleted_dbs(UserFun, [], []), + {ok, DeletedDbsInfo} = fabric2_db:list_deleted_dbs(), - Base = lists:foldl(fun(DbName0, Acc) -> - [{row, [{id, DbName0}]} | Acc] - end, [{meta, []}], fabric2_db:list_deleted_dbs()), + Base = lists:foldl(fun(DbInfo, Acc) -> + [{row, DbInfo} | Acc] + end, [{meta, []}], DeletedDbsInfo), Expect = lists:reverse(Base, [complete]), ?assertEqual(Expect, lists:reverse(UserAcc)). @@ -554,3 +558,9 @@ is_db_info_member(DbName, [DbInfo | RestInfos]) -> _E -> is_db_info_member(DbName, RestInfos) end. + +get_deleted_dbs(DeletedDbInfos) -> + lists:foldl(fun(DbInfo, Acc) -> + DbName = fabric2_util:get_value(key, DbInfo), + [DbName | Acc] + end, [], DeletedDbInfos). |