summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjiangph <jiangph@cn.ibm.com>2020-04-02 20:56:16 +0800
committerjiangph <jiangph@cn.ibm.com>2020-04-02 21:05:09 +0800
commit031cb851696ec801b83faaa6b9d42add64a70a18 (patch)
treeb95b617a532e4b94cc7080207bf83e0e2c9007e1
parentb1d2b08afb80ca9f8d8f4b9ca37ad65cd54b3352 (diff)
downloadcouchdb-031cb851696ec801b83faaa6b9d42add64a70a18.tar.gz
address Paul's comments
-rw-r--r--src/chttpd/src/chttpd_misc.erl2
-rw-r--r--src/chttpd/test/eunit/chttpd_deleted_dbs_test.erl104
-rw-r--r--src/fabric/src/fabric2_db.erl85
-rw-r--r--src/fabric/src/fabric2_fdb.erl7
-rw-r--r--src/fabric/test/fabric2_db_crud_tests.erl22
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).