Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service for database domain management #3052

Merged
merged 44 commits into from
Mar 17, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2d341c6
Initial domains support
arcusfelis Mar 8, 2021
793fa38
Add docs for service_domain_db
arcusfelis Mar 8, 2021
261b007
Use helper functions from rdbms_queries for pagination
arcusfelis Mar 9, 2021
779e50b
Add MySQL schema for the domain service
arcusfelis Mar 9, 2021
1d8dc88
Pass tests for all RDBMS databases
arcusfelis Mar 9, 2021
31b9a73
Fix service_domain_db:config_spec/0 dialyzer warning
arcusfelis Mar 9, 2021
8e5903a
Add DB cleaner fo the domain events
arcusfelis Mar 9, 2021
8fd9252
Document event_cleaning_interval and event_max_age options
arcusfelis Mar 9, 2021
07232c6
Fix dialyzer warning for the null id case
arcusfelis Mar 9, 2021
60a670c
Skip DB tests if RDBMS not available
arcusfelis Mar 9, 2021
367a9de
Add get_all_locked and get_domains_by_host_type functions
arcusfelis Mar 9, 2021
97f5251
Set read_concurrency=true
arcusfelis Mar 9, 2021
3f1ae8b
Return error,locked instead of error,duplicate
arcusfelis Mar 11, 2021
b977a7b
Require to pass HostType into remove_domain/2
arcusfelis Mar 11, 2021
a018f19
Require to pass AllowedHostTypes into init/2
arcusfelis Mar 11, 2021
13e5829
Test cases db_cannot_insert_domain_with_unknown_host_type and db_cann…
arcusfelis Mar 11, 2021
47207f6
Add db_cannot_enable_domain_with_unknown_host_type and db_cannot_disa…
arcusfelis Mar 11, 2021
610e2fe
Core ignores domains with unknown host type
arcusfelis Mar 11, 2021
359c919
Make sync from db slower
arcusfelis Mar 11, 2021
d8d48e0
Check for ignore_domain_from_db_with_different_host_type
arcusfelis Mar 11, 2021
af5a6b2
get_all_locked also returns host type
arcusfelis Mar 11, 2021
070ae3d
Download initial domain list on the start only
arcusfelis Mar 12, 2021
60ab8b6
Start 2 nodes for testing
arcusfelis Mar 12, 2021
6b10cf0
Add distributed tests
arcusfelis Mar 12, 2021
86c6223
Add db_inserted_from_one_node_while_service_disabled_on_another testcase
arcusfelis Mar 12, 2021
4fb9826
Rename locked into static
arcusfelis Mar 16, 2021
26954ef
Rename remove to delete
arcusfelis Mar 16, 2021
2c676ba
Replace Removal with remove
arcusfelis Mar 16, 2021
96e88c5
Clean comments in mongoose_domain_api
arcusfelis Mar 16, 2021
803b5f7
Rename doc/modules/service_domain_db.md -> doc/developers-guide/domai…
arcusfelis Mar 16, 2021
588adba
Change wording in the docs
arcusfelis Mar 16, 2021
508ac8f
Change the way service_domain_db:enabled/0 works
arcusfelis Mar 16, 2021
c98b589
Added comment about cleaning event table happens from all nodes
arcusfelis Mar 16, 2021
7e9cbf6
Rename FromNum to FromId
arcusfelis Mar 16, 2021
b501a7f
Use lists:foreach/2
arcusfelis Mar 16, 2021
ba50ce0
Fix typo
arcusfelis Mar 16, 2021
b4e31a6
Change order of init in service_domain_db
arcusfelis Mar 16, 2021
ea28020
One init_per_testcase for all test cases
arcusfelis Mar 16, 2021
0effe88
Remove precond function
arcusfelis Mar 16, 2021
12568e8
Skip group if db not configured
arcusfelis Mar 16, 2021
d2df37b
Add db_initial_load_crashes_node testcase
arcusfelis Mar 16, 2021
09563fe
Add db_out_of_sync_crashes_node testcase
arcusfelis Mar 16, 2021
0b4bd53
Move db_initial_load_crashes_node into db group
arcusfelis Mar 16, 2021
874999d
Receive all messages in receive_all_check_for_updates
arcusfelis Mar 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions big_tests/default.spec
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
{suites, "tests", vcard_simple_SUITE}.
{suites, "tests", websockets_SUITE}.
{suites, "tests", xep_0352_csi_SUITE}.
{suites, "tests", service_domain_db_SUITE}.

{config, ["test.config"]}.
{logdir, "ct_report"}.
Expand Down
360 changes: 360 additions & 0 deletions big_tests/tests/service_domain_db_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
-module(service_domain_db_SUITE).

-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").

-compile(export_all).
-import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).

suite() ->
require_rpc_nodes([mim]).

all() ->
[
core_lookup_works,
core_lookup_not_found,
core_locked_domain,
core_cannot_insert_locked,
core_cannot_disable_locked,
core_cannot_enable_locked,
core_get_all_locked,
core_get_domains_by_host_type
] ++ maybe_db_cases().
chrzaszcz marked this conversation as resolved.
Show resolved Hide resolved

db_cases() -> [
db_inserted_domain_is_in_db,
db_inserted_domain_is_in_core,
db_removed_domain_from_db,
db_removed_domain_from_core,
db_disabled_domain_is_in_db,
db_disabled_domain_not_in_core,
db_reanabled_domain_is_in_db,
db_reanabled_domain_is_in_core,
Comment on lines +36 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor typo: reenabled

db_can_insert_domain_twice_with_the_same_host_type,
db_cannot_insert_domain_twice_with_the_another_host_type,
sql_select_from_works,
db_records_are_restored_when_restarted,
db_record_is_ignored_if_domain_locked,
db_events_table_gets_truncated,
db_get_all_locked
].

-define(APPS, [inets, crypto, ssl, ranch, cowlib, cowboy]).

maybe_db_cases() ->
case mongoose_helper:is_rdbms_enabled(domain()) of
true -> db_cases();
false -> []
end.

domain() -> ct:get_config({hosts, mim, domain}).

%%--------------------------------------------------------------------
%% Suite configuration
%%--------------------------------------------------------------------
init_per_suite(Config) ->
prepare_erase(mim()),
Dump = dump(mim()),
Loaded = rpc(mim(), mongoose_service, is_loaded, [service_domain_db]),
ServiceOpts = rpc(mim(), mongoose_service, get_service_opts, [service_domain_db]),
escalus:init_per_suite([{orig_dump, Dump},
{orig_service_opts, ServiceOpts},
{orig_loaded, Loaded}|Config]).

end_per_suite(Config) ->
Dump = proplists:get_value(orig_dump, Config),
ServiceOpts = proplists:get_value(orig_service_opts, Config),
Loaded = proplists:get_value(orig_loaded, Config),
restore(mim(), Dump),
rpc(mim(), mongoose_service, stop_service, [service_domain_db]),
case Loaded of
true ->
rpc(mim(), mongoose_service, start_service, [service_domain_db, ServiceOpts]);
_ ->
ok
end,
escalus:end_per_suite(Config).

%%--------------------------------------------------------------------
%% Init & teardown
%%--------------------------------------------------------------------
init_per_group(_GroupName, Config) ->
Config.

end_per_group(_GroupName, Config) ->
Config.

init_per_testcase(_TestcaseName, Config) ->
Config.

end_per_testcase(_TestcaseName, Config) ->
Config.

%%--------------------------------------------------------------------
%% Tests
%%--------------------------------------------------------------------

core_lookup_works(_) ->
precond(off, [<<"example.com">>, <<"type1">>]),
{ok, <<"type1">>} = get_host_type(mim(), <<"example.com">>).

core_lookup_not_found(_) ->
precond(off),
{error, not_found} = get_host_type(mim(), <<"example.life">>).

core_locked_domain(_) ->
precond(off, [<<"example.com">>, <<"type1">>]),
true = is_locked(<<"example.com">>).

core_cannot_insert_locked(_) ->
precond(off, [<<"example.com">>, <<"type1">>]),
{error, duplicate} = insert_domain(mim(), <<"example.com">>, <<"type1">>).

core_cannot_disable_locked(_) ->
precond(off, [<<"example.com">>, <<"type1">>]),
{error, locked} = disable_domain(mim(), <<"example.com">>).

core_cannot_enable_locked(_) ->
precond(off, [<<"example.com">>, <<"type1">>]),
{error, locked} = enable_domain(mim(), <<"example.com">>).

%% See also db_get_all_locked
core_get_all_locked(_) ->
precond(off, [<<"example.com">>, <<"type1">>,
<<"example.org">>, <<"type2">>,
<<"erlang-solutions.com">>, <<"type2">>]),
%% Could be in any order
[<<"erlang-solutions.com">>, <<"example.com">>, <<"example.org">>] =
lists:sort(get_all_locked(mim())).

core_get_domains_by_host_type(_) ->
precond(off, [<<"example.com">>, <<"type1">>,
<<"example.org">>, <<"type2">>,
<<"erlang-solutions.com">>, <<"type2">>]),
[<<"erlang-solutions.com">>, <<"example.org">>] =
lists:sort(get_domains_by_host_type(mim(), <<"type2">>)),
[<<"example.com">>] = get_domains_by_host_type(mim(), <<"type1">>),
[] = get_domains_by_host_type(mim(), <<"type6">>).

%% Similar to as core_get_all_locked, just with DB service enabled
db_get_all_locked(_) ->
precond(on, [<<"example.com">>, <<"type1">>,
<<"example.org">>, <<"type2">>,
<<"erlang-solutions.com">>, <<"type2">>]),
ok = insert_domain(mim(), <<"example.db">>, <<"type1">>),
sync(),
%% Could be in any order
[<<"erlang-solutions.com">>, <<"example.com">>, <<"example.org">>] =
lists:sort(get_all_locked(mim())).

db_inserted_domain_is_in_db(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"testing">>),
{ok, #{host_type := <<"testing">>, enabled := true}} =
select_domain(mim(), <<"example.com">>).

db_inserted_domain_is_in_core(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"testing">>),
sync(),
{ok, <<"testing">>} = get_host_type(mim(), <<"example.com">>).

db_removed_domain_from_db(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"testing">>),
ok = remove_domain(mim(), <<"example.com">>),
{error, not_found} = select_domain(mim(), <<"example.com">>).

db_removed_domain_from_core(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"testing">>),
sync(),
ok = remove_domain(mim(), <<"example.com">>),
sync(),
{error, not_found} = get_host_type(mim(), <<"example.com">>).

db_disabled_domain_is_in_db(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"type1">>),
ok = disable_domain(mim(), <<"example.com">>),
{ok, #{host_type := <<"type1">>, enabled := false}} =
select_domain(mim(), <<"example.com">>).

db_disabled_domain_not_in_core(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"type1">>),
ok = disable_domain(mim(), <<"example.com">>),
sync(),
{error, not_found} = get_host_type(mim(), <<"example.com">>).

db_reanabled_domain_is_in_db(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"type1">>),
ok = disable_domain(mim(), <<"example.com">>),
ok = enable_domain(mim(), <<"example.com">>),
{ok, #{host_type := <<"type1">>, enabled := true}} =
select_domain(mim(), <<"example.com">>).

db_reanabled_domain_is_in_core(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"type1">>),
ok = disable_domain(mim(), <<"example.com">>),
ok = enable_domain(mim(), <<"example.com">>),
sync(),
{ok, <<"type1">>} = get_host_type(mim(), <<"example.com">>).

db_can_insert_domain_twice_with_the_same_host_type(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"testing">>),
ok = insert_domain(mim(), <<"example.com">>, <<"testing">>).

db_cannot_insert_domain_twice_with_the_another_host_type(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"testing">>),
{error, duplicate} = insert_domain(mim(), <<"example.com">>, <<"testing2">>).

sql_select_from_works(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"good">>),
[{_, <<"example.com">>, <<"good">>}] =
rpc(mim(), mongoose_domain_sql, select_from, [0, 100]).

db_records_are_restored_when_restarted(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"cool">>),
%% Simulate MIM restart
service_disabled(),
init_with([]),
{error, not_found} = get_host_type(mim(), <<"example.com">>),
service_enabled(),
%% DB still contains data
{ok, #{host_type := <<"cool">>, enabled := true}} =
select_domain(mim(), <<"example.com">>),
%% Restored
{ok, <<"cool">>} = get_host_type(mim(), <<"example.com">>).

db_record_is_ignored_if_domain_locked(_) ->
precond(on),
ok = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>),
ok = insert_domain(mim(), <<"example.net">>, <<"dbgroup">>),
%% Simulate MIM restart
service_disabled(),
%% Only one domain is locked
init_with([{<<"example.com">>, <<"cfggroup">>}]),
service_enabled(),
%% DB still contains data
{ok, #{host_type := <<"dbgroup">>, enabled := true}} =
select_domain(mim(), <<"example.com">>),
{ok, #{host_type := <<"dbgroup">>, enabled := true}} =
select_domain(mim(), <<"example.net">>),
%% Locked DB records are ignored
{ok, <<"cfggroup">>} = get_host_type(mim(), <<"example.com">>),
{ok, <<"dbgroup">>} = get_host_type(mim(), <<"example.net">>).

db_events_table_gets_truncated(_) ->
precond(off),
%% Configure service with a very short interval
service_enabled([{event_cleaning_interval, 1}, {event_max_age, 3}]),
ok = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>),
ok = insert_domain(mim(), <<"example.net">>, <<"dbgroup">>),
ok = insert_domain(mim(), <<"example.org">>, <<"dbgroup">>),
ok = insert_domain(mim(), <<"example.beta">>, <<"dbgroup">>),
Max = get_max_event_id(mim()),
true = is_integer(Max),
true = Max > 0,
%% The events table is not empty and the size of 1, eventually.
F = fun() -> get_min_event_id(mim()) end,
mongoose_helper:wait_until(F, Max, #{time_left => timer:seconds(15)}),
ok.

%%--------------------------------------------------------------------
%% Helpers
%%--------------------------------------------------------------------

service_enabled() ->
service_enabled([]).

service_enabled(Opts) ->
rpc(mim(), mongoose_service, start_service, [service_domain_db, Opts]),
true = rpc(mim(), service_domain_db, enabled, []).

service_disabled() ->
rpc(mim(), mongoose_service, stop_service, [service_domain_db]),
false = rpc(mim(), service_domain_db, enabled, []).

init_with(Pairs) ->
rpc(mim(), mongoose_domain_core, stop, []),
rpc(mim(), mongoose_domain_api, init, [Pairs]).

insert_domain(Node, Domain, HostType) ->
rpc(Node, mongoose_domain_api, insert_domain, [Domain, HostType]).

remove_domain(Node, Domain) ->
rpc(Node, mongoose_domain_api, remove_domain, [Domain]).

select_domain(Node, Domain) ->
rpc(Node, mongoose_domain_sql, select_domain, [Domain]).

erase_database(Node) ->
case mongoose_helper:is_rdbms_enabled(domain()) of
true -> rpc(Node, mongoose_domain_sql, erase_database, []);
false -> ok
end.

prepare_erase(Node) ->
case mongoose_helper:is_rdbms_enabled(domain()) of
true -> rpc(Node, mongoose_domain_sql, prepare_erase, []);
false -> ok
end.

get_min_event_id(Node) ->
rpc(Node, mongoose_domain_sql, get_min_event_id, []).

get_max_event_id(Node) ->
rpc(Node, mongoose_domain_sql, get_max_event_id, []).

dump(Node) ->
rpc(Node, mongoose_domain_core, dump, []).

restore(Node, Dump) ->
rpc(Node, mongoose_domain_core, restore, [Dump]).

get_host_type(Node, Domain) ->
rpc(Node, mongoose_domain_api, get_host_type, [Domain]).

get_domains_by_host_type(Node, HostType) ->
rpc(Node, mongoose_domain_api, get_domains_by_host_type, [HostType]).

get_all_locked(Node) ->
rpc(Node, mongoose_domain_api, get_all_locked, []).

disable_domain(Node, Domain) ->
rpc(Node, mongoose_domain_api, disable_domain, [Domain]).

enable_domain(Node, Domain) ->
rpc(Node, mongoose_domain_api, enable_domain, [Domain]).

is_locked(Domain) ->
rpc(mim(), mongoose_domain_core, is_locked, [Domain]).

%% Call sync before get_host_type, if there are some async changes expected
sync() ->
rpc(mim(), service_domain_db, sync, []).

precond(Enabled) ->
precond(Enabled, []).

precond(off, FlatPairs) ->
service_disabled(),
erase_database(mim()),
init_with(unflat(FlatPairs));
precond(on, FlatPairs) ->
service_disabled(),
erase_database(mim()),
service_enabled(),
init_with(unflat(FlatPairs)).

unflat([K,V|T]) ->
[{K,V}|unflat(T)];
unflat([]) ->
[].
Loading