Skip to content

Commit

Permalink
Merge pull request #2690 from apache/fix-create-db-options
Browse files Browse the repository at this point in the history
Fix create db options on secondary shard creation
  • Loading branch information
chewbranca authored and rnewson committed Apr 2, 2020
1 parent 99692f4 commit ba89381
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 21 deletions.
5 changes: 5 additions & 0 deletions src/couch/include/couch_eunit.hrl
Expand Up @@ -49,6 +49,11 @@
Suffix = couch_uuids:random(),
iolist_to_binary(["eunit-test-db-", Suffix])
end).
-define(tempshard,
fun() ->
Suffix = couch_uuids:random(),
iolist_to_binary(["shards/80000000-ffffffff/eunit-test-db-", Suffix])
end).
-define(docid,
fun() ->
integer_to_list(couch_util:unique_monotonic_integer())
Expand Down
2 changes: 1 addition & 1 deletion src/fabric/src/fabric_rpc.erl
Expand Up @@ -439,7 +439,7 @@ get_node_seqs(Db, Nodes) ->


get_or_create_db(DbName, Options) ->
couch_db:open_int(DbName, [{create_if_missing, true} | Options]).
mem3_util:get_or_create_db(DbName, Options).


get_view_cb(#mrargs{extra = Options}) ->
Expand Down
181 changes: 181 additions & 0 deletions src/fabric/test/eunit/fabric_rpc_tests.erl
@@ -0,0 +1,181 @@
% 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(fabric_rpc_tests).


-include_lib("couch/include/couch_eunit.hrl").
-include_lib("couch/include/couch_db.hrl").


-define(TDEF(A), {A, fun A/1}).


main_test_() ->
{
setup,
spawn,
fun setup_all/0,
fun teardown_all/1,
[
{
foreach,
fun setup_no_db_or_config/0,
fun teardown_db/1,
lists:map(fun wrap/1, [
?TDEF(t_no_config_non_shard_db_create_succeeds)
])
},
{
foreach,
fun setup_shard/0,
fun teardown_noop/1,
lists:map(fun wrap/1, [
?TDEF(t_no_db),
?TDEF(t_no_config_db_create_fails_for_shard),
?TDEF(t_no_config_db_create_fails_for_shard_rpc)
])
},
{
foreach,
fun setup_shard/0,
fun teardown_db/1,
lists:map(fun wrap/1, [
?TDEF(t_db_create_with_config)
])
}

]
}.


setup_all() ->
test_util:start_couch([rexi, mem3, fabric]).


teardown_all(Ctx) ->
test_util:stop_couch(Ctx).


setup_no_db_or_config() ->
?tempdb().


setup_shard() ->
?tempshard().


teardown_noop(_DbName) ->
ok.

teardown_db(DbName) ->
ok = couch_server:delete(DbName, []).


wrap({Name, Fun}) ->
fun(Arg) ->
{timeout, 60, {atom_to_list(Name), fun() ->
process_flag(trap_exit, true),
Fun(Arg)
end}}
end.


t_no_db(DbName) ->
?assertEqual({not_found, no_db_file}, couch_db:open_int(DbName, [?ADMIN_CTX])).


t_no_config_non_shard_db_create_succeeds(DbName) ->
?assertEqual({not_found, no_db_file}, couch_db:open_int(DbName, [?ADMIN_CTX])),
?assertEqual(DbName, mem3:dbname(DbName)),
?assertMatch({ok, _}, mem3_util:get_or_create_db(DbName, [?ADMIN_CTX])).


t_no_config_db_create_fails_for_shard(DbName) ->
?assertEqual({not_found, no_db_file}, couch_db:open_int(DbName, [?ADMIN_CTX])),
?assertException(throw, {error, missing_target}, mem3_util:get_or_create_db(DbName, [?ADMIN_CTX])).


t_no_config_db_create_fails_for_shard_rpc(DbName) ->
?assertEqual({not_found, no_db_file}, couch_db:open_int(DbName, [?ADMIN_CTX])),
?assertException(throw, {error, missing_target}, mem3_util:get_or_create_db(DbName, [?ADMIN_CTX])),
MFA = {fabric_rpc, get_db_info, [DbName]},
Ref = rexi:cast(node(), self(), MFA),
Resp = receive
Resp0 -> Resp0
end,
?assertMatch({Ref, {'rexi_EXIT', {{error, missing_target}, _}}}, Resp).


t_db_create_with_config(DbName) ->
MDbName = mem3:dbname(DbName),
DbDoc = #doc{id = MDbName, body = test_db_doc()},

?assertEqual({not_found, no_db_file}, couch_db:open_int(DbName, [?ADMIN_CTX])),

%% Write the dbs db config
couch_util:with_db(mem3_sync:shards_db(), fun(Db) ->
?assertEqual({not_found, missing}, couch_db:open_doc(Db, MDbName, [ejson_body])),
?assertMatch({ok, _}, couch_db:update_docs(Db, [DbDoc]))
end),

%% Test get_or_create_db loads the properties as expected
couch_util:with_db(mem3_sync:shards_db(), fun(Db) ->
?assertMatch({ok, _}, couch_db:open_doc(Db, MDbName, [ejson_body])),
?assertEqual({not_found, no_db_file}, couch_db:open_int(DbName, [?ADMIN_CTX])),
Resp = mem3_util:get_or_create_db(DbName, [?ADMIN_CTX]),
?assertMatch({ok, _}, Resp),
{ok, LDb} = Resp,

{Body} = test_db_doc(),
DbProps = mem3_util:get_shard_opts(Body),
{Props} = case couch_db_engine:get_props(LDb) of
undefined -> {[]};
Else -> {Else}
end,
%% We don't normally store the default engine name
EngineProps = case couch_db_engine:get_engine(LDb) of
couch_bt_engine ->
[];
EngineName ->
[{engine, EngineName}]
end,
?assertEqual([{props, Props} | EngineProps], DbProps)
end).


test_db_doc() ->
{[
{<<"shard_suffix">>, ".1584997648"},
{<<"changelog">>, [
[<<"add">>, <<"00000000-7fffffff">>, <<"node1@127.0.0.1">>],
[<<"add">>, <<"00000000-7fffffff">>, <<"node2@127.0.0.1">>],
[<<"add">>, <<"00000000-7fffffff">>, <<"node3@127.0.0.1">>],
[<<"add">>, <<"80000000-ffffffff">>, <<"node1@127.0.0.1">>],
[<<"add">>, <<"80000000-ffffffff">>, <<"node2@127.0.0.1">>],
[<<"add">>, <<"80000000-ffffffff">>, <<"node3@127.0.0.1">>]
]},
{<<"by_node">>, {[
{<<"node1@127.0.0.1">>, [<<"00000000-7fffffff">>, <<"80000000-ffffffff">>]},
{<<"node2@127.0.0.1">>, [<<"00000000-7fffffff">>, <<"80000000-ffffffff">>]},
{<<"node3@127.0.0.1">>, [<<"00000000-7fffffff">>, <<"80000000-ffffffff">>]}
]}},
{<<"by_range">>, {[
{<<"00000000-7fffffff">>, [<<"node1@127.0.0.1">>, <<"node2@127.0.0.1">>, <<"node3@127.0.0.1">>]},
{<<"80000000-ffffffff">>, [<<"node1@127.0.0.1">>, <<"node2@127.0.0.1">>, <<"node3@127.0.0.1">>]}
]}},
{<<"props">>, {[
{partitioned, true},
{hash, [couch_partition, hash, []]}
]}}
]}.

3 changes: 1 addition & 2 deletions src/mem3/src/mem3_reshard_dbdoc.erl
Expand Up @@ -146,9 +146,8 @@ replicate_to_all_nodes(TimeoutMSec) ->


write_shard_doc(#doc{id = Id} = Doc, Body) ->
DbName = ?l2b(config:get("mem3", "shards_db", "_dbs")),
UpdatedDoc = Doc#doc{body = Body},
couch_util:with_db(DbName, fun(Db) ->
couch_util:with_db(mem3_sync:shards_db(), fun(Db) ->
try
{ok, _} = couch_db:update_doc(Db, UpdatedDoc, [])
catch
Expand Down
2 changes: 1 addition & 1 deletion src/mem3/src/mem3_rpc.erl
Expand Up @@ -401,7 +401,7 @@ rexi_call(Node, MFA, Timeout) ->


get_or_create_db(DbName, Options) ->
couch_db:open_int(DbName, [{create_if_missing, true} | Options]).
mem3_util:get_or_create_db(DbName, Options).


-ifdef(TEST).
Expand Down
22 changes: 14 additions & 8 deletions src/mem3/src/mem3_shards.erl
Expand Up @@ -20,6 +20,7 @@
-export([handle_config_change/5, handle_config_terminate/3]).

-export([start_link/0]).
-export([opts_for_db/1]).
-export([for_db/1, for_db/2, for_docid/2, for_docid/3, get/3, local/1, fold/2]).
-export([for_shard_range/1]).
-export([set_max_size/1]).
Expand All @@ -45,6 +46,15 @@
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

opts_for_db(DbName) ->
{ok, Db} = mem3_util:ensure_exists(mem3_sync:shards_db()),
case couch_db:open_doc(Db, DbName, [ejson_body]) of
{ok, #doc{body = {Props}}} ->
mem3_util:get_shard_opts(Props);
{not_found, _} ->
erlang:error(database_does_not_exist, ?b2l(DbName))
end.

for_db(DbName) ->
for_db(DbName, []).

Expand Down Expand Up @@ -144,8 +154,7 @@ local(DbName) ->
lists:filter(Pred, for_db(DbName)).

fold(Fun, Acc) ->
DbName = config:get("mem3", "shards_db", "_dbs"),
{ok, Db} = mem3_util:ensure_exists(DbName),
{ok, Db} = mem3_util:ensure_exists(mem3_sync:shards_db()),
FAcc = {Db, Fun, Acc},
try
{ok, LastAcc} = couch_db:fold_docs(Db, fun fold_fun/2, FAcc),
Expand Down Expand Up @@ -309,15 +318,13 @@ fold_fun(#doc_info{}=DI, {Db, UFun, UAcc}) ->
end.

get_update_seq() ->
DbName = config:get("mem3", "shards_db", "_dbs"),
{ok, Db} = mem3_util:ensure_exists(DbName),
{ok, Db} = mem3_util:ensure_exists(mem3_sync:shards_db()),
Seq = couch_db:get_update_seq(Db),
couch_db:close(Db),
Seq.

listen_for_changes(Since) ->
DbName = config:get("mem3", "shards_db", "_dbs"),
{ok, Db} = mem3_util:ensure_exists(DbName),
{ok, Db} = mem3_util:ensure_exists(mem3_sync:shards_db()),
Args = #changes_args{
feed = "continuous",
since = Since,
Expand Down Expand Up @@ -362,8 +369,7 @@ changes_callback(timeout, _) ->

load_shards_from_disk(DbName) when is_binary(DbName) ->
couch_stats:increment_counter([mem3, shard_cache, miss]),
X = ?l2b(config:get("mem3", "shards_db", "_dbs")),
{ok, Db} = mem3_util:ensure_exists(X),
{ok, Db} = mem3_util:ensure_exists(mem3_sync:shards_db()),
try
load_shards_from_db(Db, DbName)
after
Expand Down

0 comments on commit ba89381

Please sign in to comment.