From ac3d07908c2871abad269c2124d0393e6c8978db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 14 Mar 2017 09:37:49 +0100 Subject: [PATCH 1/4] Make DB connection pool configuration independent from the host config. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To declare a DB connection pool, use on of the following formats: {pool, odbc, PoolName}. {pool, odbc, PoolName, Options}. PoolName is an atom and Options is a list of ODBC options. Options can be still specified at the top level. 1. All hosts use the ‘default’ pool by default. To change this, use the {odbc_pool, Option} option (top-level or per-host). 2. This means that setting ‘odbc_server’ is not enough for a DB connection to work. The default pool has to be declared as well. 3. The ODBC options specified per-host are not allowed anymore. They have been replaced with the Options element of the pool declaration. To update the previous config with ODBC enabled to the new format, add the following line to ejabberd.cfg: {pool, odbc, default}. --- apps/ejabberd/src/ejabberd_config.erl | 82 ++++++++----- apps/ejabberd/src/ejabberd_rdbms.erl | 64 +++------- apps/ejabberd/src/mod_mam_meta.erl | 6 +- .../mod_mam_muc_odbc_async_pool_writer.erl | 6 +- .../src/mod_mam_odbc_async_pool_writer.erl | 8 +- apps/ejabberd/src/mongoose_metrics.erl | 30 +++-- .../src/mongoose_metrics_definitions.hrl | 6 +- apps/ejabberd/src/rdbms/mongoose_rdbms.erl | 109 +++++++++--------- .../src/rdbms/mongoose_rdbms_mysql.erl | 9 +- .../src/rdbms/mongoose_rdbms_odbc.erl | 14 +-- .../src/rdbms/mongoose_rdbms_pgsql.erl | 9 +- .../ejabberd/src/rdbms/mongoose_rdbms_sup.erl | 107 +++++++++-------- doc/Advanced-configuration.md | 20 +++- doc/Basic-configuration.md | 8 +- rel/files/ejabberd.cfg | 4 + test.disabled/ejabberd_tests/test.config | 4 + .../ejabberd_tests/tests/mam_SUITE.erl | 2 +- 17 files changed, 249 insertions(+), 239 deletions(-) diff --git a/apps/ejabberd/src/ejabberd_config.erl b/apps/ejabberd/src/ejabberd_config.erl index 3a09321c586..c844d99ca1f 100644 --- a/apps/ejabberd/src/ejabberd_config.erl +++ b/apps/ejabberd/src/ejabberd_config.erl @@ -77,6 +77,7 @@ -record(state, {opts = [] :: list(), hosts = [] :: [host()], + odbc_pools = [] :: [atom()], override_local = false :: boolean(), override_global = false :: boolean(), override_acls = false :: boolean()}). @@ -177,10 +178,7 @@ get_ejabberd_config_path() -> %% This function will crash if finds some error in the configuration file. -spec load_file(File :: string()) -> ok. load_file(File) -> - Terms = get_plain_terms_file(File), - State = lists:foldl(fun search_hosts/2, #state{}, Terms), - TermsMacros = replace_macros(Terms), - Res = lists:foldl(fun process_term/2, State, TermsMacros), + Res = parse_file(File), set_opts(Res). @@ -220,8 +218,10 @@ get_absolute_path(File) -> end. --spec search_hosts({host|hosts, [host()] | host()}, state()) -> any(). -search_hosts(Term, State) -> +-spec search_hosts_and_pools({host|hosts, [host()] | host()} + | {pool, odbc, atom()} + | {pool, odbc, atom(), list()}, state()) -> any(). +search_hosts_and_pools(Term, State) -> case Term of {host, Host} -> case State of @@ -241,6 +241,10 @@ search_hosts(Term, State) -> "too many hosts definitions", []), exit("too many hosts definitions") end; + {pool, PoolType, PoolName, _Options} -> + search_hosts_and_pools({pool, PoolType, PoolName}, State); + {pool, odbc, PoolName} -> + add_odbc_pool_to_option(PoolName, State); _ -> State end. @@ -252,6 +256,9 @@ add_hosts_to_option(Hosts, State) -> PrepHosts = normalize_hosts(Hosts), add_option(hosts, PrepHosts, State#state{hosts = PrepHosts}). +add_odbc_pool_to_option(PoolName, State) -> + Pools = State#state.odbc_pools, + State#state{odbc_pools = [PoolName | Pools]}. -spec normalize_hosts([host()]) -> [binary() | tuple()]. normalize_hosts(Hosts) -> @@ -508,6 +515,13 @@ process_term(Term, State) -> lists:foldl(fun(T, S) -> process_host_term(T, list_to_binary(Host), S) end, State, Terms); + {pool, odbc, _PoolName} -> + State; + {pool, odbc, PoolName, Options} -> + lists:foldl(fun(T, S) -> + process_db_pool_term(T, PoolName, S) + end, + State, Options); {listen, Listeners} -> Listeners2 = lists:map( @@ -576,10 +590,23 @@ process_term(Term, State) -> {all_metrics_are_global, Value} -> add_option(all_metrics_are_global, Value, State); {_Opt, _Val} -> - lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, - State, State#state.hosts) + State1 = process_term_for_hosts(Term, State), + process_term_for_odbc_pools(Term, State1) end. +process_term_for_hosts(Term, State) -> + lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, + State, State#state.hosts). + +process_term_for_odbc_pools(Term = {Key, _Val}, State) -> + BKey = atom_to_binary(Key, utf8), + case get_key_group(BKey, Key) of + odbc -> + lists:foldl(fun(Pool, S) -> process_db_pool_term(Term, Pool, S) end, + State, State#state.odbc_pools); + _ -> + State + end. -spec process_host_term(Term :: host_term(), Host :: acl:host(), @@ -603,6 +630,8 @@ process_host_term(Term, Host, State) -> State; {odbc_server, ODBCServer} -> add_option({odbc_server, Host}, ODBCServer, State); + {odbc_pool, Pool} when is_atom(Pool) -> + add_option({odbc_pool, Host}, Pool, State); {riak_server, RiakConfig} -> add_option(riak_server, RiakConfig, State); {cassandra_servers, CassandraConfig} -> @@ -611,6 +640,8 @@ process_host_term(Term, Host, State) -> add_option({Opt, Host}, Val, State) end. +process_db_pool_term({Opt, Val}, Pool, State) when is_atom(Pool) -> + add_option({Opt, odbc_pool, Pool}, Val, State). -spec add_option(Opt :: key(), Val :: value(), @@ -786,9 +817,11 @@ is_file_readable(Path) -> -spec parse_file(file:name()) -> state(). parse_file(ConfigFile) -> Terms = get_plain_terms_file(ConfigFile), - State = lists:foldl(fun search_hosts/2, #state{}, Terms), + State = lists:foldl(fun search_hosts_and_pools/2, #state{}, Terms), TermsWExpandedMacros = replace_macros(Terms), - lists:foldl(fun process_term/2, State, TermsWExpandedMacros). + lists:foldl(fun process_term/2, + add_option(odbc_pools, State#state.odbc_pools, State), + TermsWExpandedMacros). -spec reload_local() -> {ok, iolist()} | no_return(). reload_local() -> @@ -1047,8 +1080,6 @@ handle_local_config_change({Key, _Old, _New} = El) -> handle_local_hosts_config_add({{auth, Host}, _}) -> ejabberd_auth:start(Host); -handle_local_hosts_config_add({{odbc, Host}, _}) -> - ejabberd_rdbms:start(Host); handle_local_hosts_config_add({{ldap, _Host}, _}) -> %% ignore ldap section ok; @@ -1072,8 +1103,6 @@ handle_local_hosts_config_del({{auth, Host}, Opts}) -> M:stop(Host) end, AuthModules) end; -handle_local_hosts_config_del({{odbc, Host}, _}) -> - ejabberd_rdbms:stop_odbc(Host); handle_local_hosts_config_del({{ldap, _Host}, _I}) -> %% ignore ldap section, only appli ok; @@ -1087,15 +1116,6 @@ handle_local_hosts_config_del({{Key, _}, _} =El) -> ?WARNING_MSG("local hosts config delete option: ~p unhandled", [El]) end. -handle_local_hosts_config_change({{odbc, Host}, Old, _}) -> - %% stop rdbms - case lists:keyfind({odbc_server, Host}, 1, Old) of - false -> - ok; - #local_config{} -> - ejabberd_rdbms:stop_odbc(Host) - end, - ejabberd_rdbms:start(Host); handle_local_hosts_config_change({{auth, Host}, OldVals, _}) -> case lists:keyfind(auth_method, 1, OldVals) of false -> @@ -1149,9 +1169,10 @@ add_virtual_host(Host) -> ?DEBUG("Register host:~p", [Host]), ejabberd_local:register_host(Host). --spec can_be_ignored(Key :: atom()) -> boolean(). -can_be_ignored(Key) when is_atom(Key) -> - L = [domain_certfile, s2s, all_metrics_are_global], +-spec can_be_ignored(Key :: atom() | tuple()) -> boolean(). +can_be_ignored(Key) when is_atom(Key); + is_tuple(Key) -> + L = [domain_certfile, s2s, all_metrics_are_global, odbc], lists:member(Key, L). -spec remove_virtual_host(ejabberd:server()) -> any(). @@ -1219,11 +1240,16 @@ get_local_config() -> get_global_config() -> mnesia:dirty_match_object(config, {config, '_', '_'}). --spec is_not_host_specific(atom() | {atom(), ejabberd:server()}) -> boolean(). +-spec is_not_host_specific(atom() + | {atom(), ejabberd:server()} + | {atom(), atom(), atom()}) -> boolean(). is_not_host_specific(Key) when is_atom(Key) -> true; is_not_host_specific({Key, Host}) when is_atom(Key), is_binary(Host) -> - false. + false; +is_not_host_specific({Key, PoolType, PoolName}) + when is_atom(Key), is_atom(PoolType), is_atom(PoolName) -> + true. -spec categorize_options([term()]) -> {GlobalConfig, LocalConfig, HostsConfig} when GlobalConfig :: list(), diff --git a/apps/ejabberd/src/ejabberd_rdbms.erl b/apps/ejabberd/src/ejabberd_rdbms.erl index cdc4f89f437..06cd0e607c8 100644 --- a/apps/ejabberd/src/ejabberd_rdbms.erl +++ b/apps/ejabberd/src/ejabberd_rdbms.erl @@ -27,7 +27,7 @@ -module(ejabberd_rdbms). -author('alexey@process-one.net'). --export([start/0, start/1, stop/1, stop_odbc/1]). +-export([start/0, start_pool/1, stop_pool/1, pools/0]). -include("ejabberd.hrl"). -spec start() -> 'ok' | {'error', 'lager_not_running'}. @@ -39,63 +39,29 @@ start() -> ?INFO_MSG("MongooseIM has not been compiled with relational database support. " "Skipping database startup.", []); _ -> - %% If compiled with ODBC, start ODBC on the needed host - start_hosts() + {ok, _Pid} = start_pool_sup(), + [start_pool(Pool) || Pool <- pools()], + ok end. -%% @doc Start relationnal DB module on the nodes where it is needed --spec start_hosts() -> 'ok'. -start_hosts() -> - lists:foreach(fun start/1, ?MYHOSTS). - --spec stop(Host :: ejabberd:server()) -> 'ok'. -stop(Host) -> - case needs_odbc(Host) of - true -> stop_odbc(Host); - false -> ok - end. - --spec start(Host :: ejabberd:server()) -> 'ok'. -start(Host) -> - case needs_odbc(Host) of - true -> start_odbc(Host); - false -> ok - end. - -%% @doc Start the ODBC module on the given host --spec start_odbc(binary() | string()) -> 'ok'. -start_odbc(Host) -> - Supervisor_name = gen_mod:get_module_proc(Host, mongoose_rdbms_sup), +start_pool_sup() -> ChildSpec = - {Supervisor_name, - {mongoose_rdbms_sup, start_link, [Host]}, + {mongoose_rdbms_sup, + {mongoose_rdbms_sup, start_link, []}, transient, infinity, supervisor, [mongoose_rdbms_sup]}, - case supervisor:start_child(ejabberd_sup, ChildSpec) of - {ok, _PID} -> - ok; - _Error -> - ?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying...~n", - [Supervisor_name, _Error]), - start_odbc(Host) - end. + supervisor:start_child(ejabberd_sup, ChildSpec). --spec stop_odbc(binary() | string()) -> 'ok'. -stop_odbc(Host) -> - Proc = gen_mod:get_module_proc(Host, mongoose_rdbms_sup), - supervisor:terminate_child(ejabberd_sup, Proc). +pools() -> + ejabberd_config:get_local_option_or_default(odbc_pools, []). -%% @doc Returns true if we have configured odbc_server for the given host --spec needs_odbc(_) -> boolean(). -needs_odbc(Host) -> - LHost = jid:nameprep(Host), - case ejabberd_config:get_local_option({odbc_server, LHost}) of - undefined -> - false; - _ -> true - end. +start_pool(Pool) -> + mongoose_rdbms_sup:add_pool(Pool). + +stop_pool(Pool) -> + mongoose_rdbms_sup:remove_pool(Pool). compile_odbc_type_helper() -> Key = {odbc_server_type, ?MYNAME}, diff --git a/apps/ejabberd/src/mod_mam_meta.erl b/apps/ejabberd/src/mod_mam_meta.erl index 656d485d9e3..76f38271d73 100644 --- a/apps/ejabberd/src/mod_mam_meta.erl +++ b/apps/ejabberd/src/mod_mam_meta.erl @@ -159,8 +159,8 @@ add_default_odbc_opts(Opts) -> [{cache_users, true}, {async_writer, true}]). --spec parse_backend_opt(Option :: {module(), term()}, Type :: pm | muc, - module(), module(), deps()) -> deps(). +-spec parse_backend_opt(Type :: pm | muc, module(), module(), + Option :: {module(), term()}, deps()) -> deps(). parse_backend_opt(Type, ModODBCArch, ModAsyncWriter, Option, Deps) -> case Option of {cache_users, true} -> @@ -176,6 +176,8 @@ parse_backend_opt(Type, ModODBCArch, ModAsyncWriter, Option, Deps) -> {async_writer, true} -> DepsWithNoWriter = add_dep(ModODBCArch, [no_writer], Deps), add_dep(ModAsyncWriter, [Type], DepsWithNoWriter); + {async_writer_odbc_pool, PoolName} -> + add_dep(ModAsyncWriter, [{odbc_pool, PoolName}], Deps); _ -> Deps end. diff --git a/apps/ejabberd/src/mod_mam_muc_odbc_async_pool_writer.erl b/apps/ejabberd/src/mod_mam_muc_odbc_async_pool_writer.erl index 96d04ee8128..9b8f5d2c94d 100644 --- a/apps/ejabberd/src/mod_mam_muc_odbc_async_pool_writer.erl +++ b/apps/ejabberd/src/mod_mam_muc_odbc_async_pool_writer.erl @@ -89,8 +89,7 @@ worker_number(Host, ArcID) -> -spec start(ejabberd:server(), _) -> 'ok'. start(Host, Opts) -> - PoolName = gen_mod:get_module_proc(Host, ?MODULE), - {ok, _} = mongoose_rdbms_sup:add_pool(Host, ?MODULE, PoolName, worker_count(Host)), + PoolName = gen_mod:get_opt(odbc_pool, Opts, mongoose_rdbms_sup:pool(Host)), MaxSize = gen_mod:get_module_opt(Host, ?MODULE, max_packet_size, 30), mod_mam_muc_odbc_arch:prepare_insert(insert_mam_muc_message, 1), mod_mam_muc_odbc_arch:prepare_insert(insert_mam_muc_messages, MaxSize), @@ -101,8 +100,7 @@ start(Host, Opts) -> -spec stop(ejabberd:server()) -> any(). stop(Host) -> stop_muc(Host), - stop_workers(Host), - mongoose_rdbms_sup:remove_pool(Host, ?MODULE). + stop_workers(Host). %% ---------------------------------------------------------------------- %% Add hooks for mod_mam_muc diff --git a/apps/ejabberd/src/mod_mam_odbc_async_pool_writer.erl b/apps/ejabberd/src/mod_mam_odbc_async_pool_writer.erl index 45a79876f5d..daee6c23443 100644 --- a/apps/ejabberd/src/mod_mam_odbc_async_pool_writer.erl +++ b/apps/ejabberd/src/mod_mam_odbc_async_pool_writer.erl @@ -79,10 +79,7 @@ worker_number(Host, ArcID) -> start(Host, Opts) -> mongoose_metrics:ensure_metric(Host, ?PER_MESSAGE_FLUSH_TIME, histogram), - - PoolName = gen_mod:get_module_proc(Host, ?MODULE), - {ok, _} = mongoose_rdbms_sup:add_pool(Host, ?MODULE, PoolName, worker_count(Host)), - + PoolName = gen_mod:get_opt(odbc_pool, Opts, mongoose_rdbms_sup:pool(Host)), MaxSize = gen_mod:get_module_opt(Host, ?MODULE, max_packet_size, 30), mod_mam_odbc_arch:prepare_insert(insert_mam_message, 1), mod_mam_odbc_arch:prepare_insert(insert_mam_messages, MaxSize), @@ -114,8 +111,7 @@ stop(Host) -> false -> ok end, - stop_workers(Host), - mongoose_rdbms_sup:remove_pool(Host, ?MODULE). + stop_workers(Host). %% ---------------------------------------------------------------------- %% Add hooks for mod_mam diff --git a/apps/ejabberd/src/mongoose_metrics.erl b/apps/ejabberd/src/mongoose_metrics.erl index 16e8b0bd5a0..e0913cce982 100644 --- a/apps/ejabberd/src/mongoose_metrics.erl +++ b/apps/ejabberd/src/mongoose_metrics.erl @@ -24,6 +24,7 @@ init_predefined_host_metrics/1, init_subscriptions/0, create_generic_hook_metric/2, + ensure_db_pool_metric/1, update/3, ensure_metric/3, get_metric_value/1, @@ -34,7 +35,7 @@ get_aggregated_values/1, increment_generic_hook_metric/2, get_odbc_data_stats/0, - get_odbc_mam_async_stats/0, + get_odbc_data_stats/1, get_dist_data_stats/0, get_up_time/0, remove_host_metrics/1, @@ -83,6 +84,12 @@ create_generic_hook_metric(Host, Hook) -> FilteredHookName = filter_hook(Hook), do_create_generic_hook_metric(Host, FilteredHookName). +ensure_db_pool_metric(Pool) -> + ensure_metric(global, + [data, odbc, Pool], + {function, mongoose_metrics, get_odbc_data_stats, [[Pool]], proplist, + [workers | ?INET_STATS]}). + -spec update(Host :: ejabberd:lserver() | global, Name :: term() | list(), Change :: term()) -> any(). update(Host, Name, Change) when is_list(Name) -> @@ -124,22 +131,11 @@ increment_generic_hook_metric(Host, Hook) -> do_increment_generic_hook_metric(Host, FilteredHook). get_odbc_data_stats() -> - RegularODBCWorkers = [catch mongoose_rdbms_sup:get_pids(Host) || Host <- ?MYHOSTS], - get_odbc_stats(lists:flatten(RegularODBCWorkers)). - -get_odbc_mam_async_stats() -> - %% MAM async ODBC workers are organized differently... - GetChildren = fun(Host, Pool) -> - Name = gen_mod:get_module_proc(Host, Pool), - case catch mongoose_rdbms_sup:get_pids(Name) of - [_ | _] = Children -> Children; - _ -> [] - end - end, - - MamPools = [mod_mam_odbc_async_pool_writer, mod_mam_muc_odbc_async_pool_writer], - MamChildren = lists:flatten([GetChildren(Host, Pool) || Host <- ?MYHOSTS, Pool <- MamPools]), - get_odbc_stats(MamChildren). + get_odbc_data_stats(ejabberd_rdbms:pools()). + +get_odbc_data_stats(Pools) -> + ODBCWorkers = [catch mongoose_rdbms_sup:get_pids(Pool) || Pool <- Pools], + get_odbc_stats(lists:flatten(ODBCWorkers)). get_dist_data_stats() -> DistStats = [inet_stats(Port) || {_, Port} <- erlang:system_info(dist_ctrl)], diff --git a/apps/ejabberd/src/mongoose_metrics_definitions.hrl b/apps/ejabberd/src/mongoose_metrics_definitions.hrl index f05018faa59..391ccf28dd5 100644 --- a/apps/ejabberd/src/mongoose_metrics_definitions.hrl +++ b/apps/ejabberd/src/mongoose_metrics_definitions.hrl @@ -92,11 +92,7 @@ [data, xmpp, sent, xml_stanza_size]]). -define(DATA_FUN_METRICS, - [{[data, odbc, regular], - {function, mongoose_metrics, get_odbc_data_stats, [], proplist, [workers | ?INET_STATS]}}, - {[data, odbc, mam_async], - {function, mongoose_metrics, get_odbc_mam_async_stats, [], proplist, [workers | ?INET_STATS]}}, - {[data, dist], + [{[data, dist], {function, mongoose_metrics, get_dist_data_stats, [], proplist, [connections | ?INET_STATS]}}]). diff --git a/apps/ejabberd/src/rdbms/mongoose_rdbms.erl b/apps/ejabberd/src/rdbms/mongoose_rdbms.erl index a7c46db48d9..0ff9edeb4af 100644 --- a/apps/ejabberd/src/rdbms/mongoose_rdbms.erl +++ b/apps/ejabberd/src/rdbms/mongoose_rdbms.erl @@ -31,13 +31,13 @@ -behaviour(gen_server). --callback escape_format(Host :: ejabberd:server()) -> atom(). +-callback escape_format(Pool :: pool()) -> atom(). -callback connect(Args :: any(), QueryTimeout :: non_neg_integer()) -> {ok, Connection :: term()} | {error, Reason :: any()}. -callback disconnect(Connection :: term()) -> any(). -callback query(Connection :: term(), Query :: any(), Timeout :: infinity | non_neg_integer()) -> query_result(). --callback prepare(Host :: ejabberd:server(), Connection :: term(), Name :: atom(), +-callback prepare(Pool :: pool(), Connection :: term(), Name :: atom(), Table :: binary(), Fields :: [binary()], Statement :: iodata()) -> {ok, Ref :: term()} | {error, Reason :: any()}. -callback execute(Connection :: term(), Ref :: term(), Parameters :: [term()], @@ -77,9 +77,12 @@ -include("ejabberd.hrl"). +-type pool() :: atom(). +-export_type([pool/0]). + -record(state, {db_ref, db_type :: atom(), - host :: ejabberd:server(), + pool :: pool(), prepared = #{} :: #{binary() => term()} }). -type state() :: #state{}. @@ -94,8 +97,7 @@ %% the retry counter runs out. We just attempt to reduce log pollution. -define(CONNECT_RETRIES, 5). -%% Points to ODBC server process --type odbc_server() :: binary() | atom(). +-type server() :: binary() | pool(). -type odbc_msg() :: {sql_query, _} | {sql_transaction, fun()} | {sql_execute, atom(), iodata()}. -type single_query_result() :: {selected, [tuple()]} | {updated, non_neg_integer() | undefined} | @@ -123,17 +125,17 @@ prepare(Name, Table, Fields, Statement) when is_atom(Name), is_binary(Table) -> false -> {error, already_exists} end. --spec execute(HostOrPool :: odbc_server(), Name :: atom(), Parameters :: [term()]) -> +-spec execute(HostOrPool :: server(), Name :: atom(), Parameters :: [term()]) -> query_result(). execute(HostOrPool, Name, Parameters) when is_atom(Name), is_list(Parameters) -> sql_call(HostOrPool, {sql_execute, Name, Parameters}). --spec sql_query(HostOrPool :: odbc_server(), Query :: any()) -> query_result(). +-spec sql_query(HostOrPool :: server(), Query :: any()) -> query_result(). sql_query(HostOrPool, Query) -> sql_call(HostOrPool, {sql_query, Query}). %% @doc SQL transaction based on a list of queries --spec sql_transaction(odbc_server(), fun() | maybe_improper_list()) -> transaction_result(). +-spec sql_transaction(server(), fun() | maybe_improper_list()) -> transaction_result(). sql_transaction(HostOrPool, Queries) when is_list(Queries) -> F = fun() -> lists:map(fun sql_query_t/1, Queries) end, sql_transaction(HostOrPool, F); @@ -142,7 +144,7 @@ sql_transaction(HostOrPool, F) when is_function(F) -> sql_call(HostOrPool, {sql_transaction, F}). %% TODO: Better spec for RPC calls --spec sql_call(HostOrPool :: odbc_server(), Msg :: odbc_msg()) -> any(). +-spec sql_call(HostOrPool :: server(), Msg :: odbc_msg()) -> any(). sql_call(HostOrPool, Msg) -> case get(?STATE_KEY) of undefined -> sql_call0(HostOrPool, Msg); @@ -153,31 +155,27 @@ sql_call(HostOrPool, Msg) -> end. --spec sql_call0(HostOrPool :: odbc_server(), Msg :: odbc_msg()) -> any(). -sql_call0(Host, Msg) when is_binary(Host) -> - sql_call0(mongoose_rdbms_sup:default_pool(Host), Msg); -sql_call0(Pool, Msg) when is_atom(Pool) -> - case whereis(Pool) of - undefined -> {error, {no_odbc_pool, Pool}}; +-spec sql_call0(HostOrPool :: server(), Msg :: odbc_msg()) -> any(). +sql_call0(HostOrPool, Msg) -> + PoolProc = mongoose_rdbms_sup:pool_proc(HostOrPool), + case whereis(PoolProc) of + undefined -> {error, {no_odbc_pool, PoolProc}}; _ -> Timestamp = p1_time_compat:monotonic_time(milli_seconds), - wpool:call(Pool, {sql_cmd, Msg, Timestamp}, best_worker, ?TRANSACTION_TIMEOUT) + wpool:call(PoolProc, {sql_cmd, Msg, Timestamp}, best_worker, ?TRANSACTION_TIMEOUT) end. --spec get_db_info(Target :: odbc_server() | pid()) -> +-spec get_db_info(Target :: server() | pid()) -> {ok, DbType :: atom(), DbRef :: term()} | {error, any()}. -get_db_info(Host) when is_binary(Host) -> - get_db_info(mongoose_rdbms_sup:default_pool(Host)); -get_db_info(Pool) when is_atom(Pool) -> - case whereis(Pool) of - undefined -> {error, {no_odbc_pool, Pool}}; - _ -> wpool:call(Pool, get_db_info) - end; get_db_info(Pid) when is_pid(Pid) -> - wpool_process:call(Pid, get_db_info, 5000). - - + wpool_process:call(Pid, get_db_info, 5000); +get_db_info(HostOrPool) -> + PoolProc = mongoose_rdbms_sup:pool_proc(HostOrPool), + case whereis(PoolProc) of + undefined -> {error, {no_odbc_pool, PoolProc}}; + _ -> wpool:call(PoolProc, get_db_info) + end. %% This function is intended to be used from inside an sql_transaction: sql_query_t(Query) -> @@ -215,10 +213,10 @@ escape_like(S) -> rdbms_queries:escape_like_string(S). --spec escape_format(odbc_server()) -> hex | simple_escape. -escape_format(Host) -> - mongoose_rdbms_backend:escape_format(Host). - +-spec escape_format(server()) -> hex | simple_escape. +escape_format(HostOrPool) -> + Pool = mongoose_rdbms_sup:pool(HostOrPool), + mongoose_rdbms_backend:escape_format(Pool). -spec escape_binary('hex' | 'simple_escape', binary()) -> binary() | string(). escape_binary(hex, Bin) when is_binary(Bin) -> @@ -267,16 +265,16 @@ to_bool(_) -> false. %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- --spec init(ejabberd:server()) -> {ok, state()}. -init(Host) -> +-spec init(pool()) -> {ok, state()}. +init(Pool) -> process_flag(trap_exit, true), - backend_module:create(?MODULE, db_engine(Host), [query]), - Settings = ejabberd_config:get_local_option({odbc_server, Host}), - MaxStartInterval = get_start_interval(Host), + backend_module:create(?MODULE, db_engine(Pool), [query]), + Settings = mongoose_rdbms_sup:get_option(Pool, odbc_server), + MaxStartInterval = get_start_interval(Pool), case connect(Settings, ?CONNECT_RETRIES, 2, MaxStartInterval) of {ok, DbRef} -> - schedule_keepalive(Host), - {ok, #state{db_type = db_engine(Host), host = Host, db_ref = DbRef}}; + schedule_keepalive(Pool), + {ok, #state{db_type = db_engine(Pool), pool = Pool, db_ref = DbRef}}; Error -> {stop, Error} end. @@ -299,7 +297,7 @@ code_change(_OldVsn, State, _Extra) -> handle_info(keepalive, State) -> case sql_query_internal([?KEEPALIVE_QUERY], State) of {selected, _} -> - schedule_keepalive(State#state.host), + schedule_keepalive(State#state.pool), {noreply, State}; {error, _} = Error -> {stop, {keepalive_failed, Error}, State} @@ -426,11 +424,11 @@ sql_execute(Name, Params, State = #state{db_ref = DBRef}) -> {Res, NewState}. -spec prepare_statement(Name :: atom(), state()) -> {Ref :: term(), state()}. -prepare_statement(Name, State = #state{db_ref = DBRef, prepared = Prepared, host = Host}) -> +prepare_statement(Name, State = #state{db_ref = DBRef, prepared = Prepared, pool = Pool}) -> case maps:get(Name, Prepared, undefined) of undefined -> [{_, Table, Fields, Statement}] = ets:lookup(prepared_statements, Name), - {ok, Ref} = mongoose_rdbms_backend:prepare(Host, DBRef, Name, Table, Fields, Statement), + {ok, Ref} = mongoose_rdbms_backend:prepare(Pool, DBRef, Name, Table, Fields, Statement), {Ref, State#state{prepared = maps:put(Name, Ref, Prepared)}}; Ref -> @@ -450,13 +448,10 @@ abort_on_driver_error({{error, "Failed sending data on socket" ++ _} = Reply, St abort_on_driver_error({Reply, State}) -> {reply, Reply, State}. - --spec db_engine(Host :: odbc_server()) -> ejabberd_config:value(). -db_engine(Pool) when is_atom(Pool) -> - {ok, DbType, _} = wpool:call(Pool, get_db_info), - DbType; -db_engine(Host) when is_binary(Host) -> - case ejabberd_config:get_local_option({odbc_server, Host}) of +-spec db_engine(server()) -> ejabberd_config:value(). +db_engine(HostOrPool) -> + Pool = mongoose_rdbms_sup:pool(HostOrPool), + case mongoose_rdbms_sup:get_option(Pool, odbc_server) of SQLServer when is_list(SQLServer) -> odbc; Other when is_tuple(Other) -> @@ -482,31 +477,31 @@ connect(Settings, Retry, RetryAfter, MaxRetryDelay) -> end. --spec schedule_keepalive(ejabberd:server()) -> any(). -schedule_keepalive(Host) -> - case ejabberd_config:get_local_option({odbc_keepalive_interval, Host}) of +-spec schedule_keepalive(pool()) -> any(). +schedule_keepalive(Pool) -> + case mongoose_rdbms_sup:get_option(Pool, odbc_keepalive_interval) of KeepaliveInterval when is_integer(KeepaliveInterval) -> erlang:send_after(timer:seconds(KeepaliveInterval), self(), keepalive); undefined -> ok; _Other -> ?ERROR_MSG("Wrong odbc_keepalive_interval definition '~p'" - " for host ~p.~n", [_Other, Host]), + " for pool ~p.~n", [_Other, Pool]), ok end. --spec get_start_interval(ejabberd:server()) -> any(). -get_start_interval(Host) -> +-spec get_start_interval(pool()) -> any(). +get_start_interval(Pool) -> DefaultInterval = 30, - case ejabberd_config:get_local_option({odbc_start_interval, Host}) of + case mongoose_rdbms_sup:get_option(Pool, odbc_start_interval) of StartInterval when is_integer(StartInterval) -> StartInterval; undefined -> DefaultInterval; _Other -> ?ERROR_MSG("Wrong odbc_start_interval definition '~p'" - " for host ~p, defaulting to ~p seconds.~n", - [_Other, Host, DefaultInterval]), + " for pool ~p, defaulting to ~p seconds.~n", + [_Other, Pool, DefaultInterval]), DefaultInterval end. diff --git a/apps/ejabberd/src/rdbms/mongoose_rdbms_mysql.erl b/apps/ejabberd/src/rdbms/mongoose_rdbms_mysql.erl index 8372ae3bb41..31051e3c1e3 100644 --- a/apps/ejabberd/src/rdbms/mongoose_rdbms_mysql.erl +++ b/apps/ejabberd/src/rdbms/mongoose_rdbms_mysql.erl @@ -26,8 +26,8 @@ %% API --spec escape_format(Host :: ejabberd:server()) -> atom(). -escape_format(_Host) -> +-spec escape_format(mongoose_rdbms:pool()) -> atom(). +escape_format(_Pool) -> simple_escape. -spec connect(Args :: any(), QueryTimeout :: non_neg_integer()) -> @@ -51,10 +51,11 @@ disconnect(Connection) -> query(Connection, Query, _Timeout) -> mysql_to_odbc(mysql:query(Connection, Query), Connection). --spec prepare(Host :: ejabberd:server(), Connection :: term(), Name :: atom(), Table :: binary(), +-spec prepare(Pool :: mongoose_rdbms:pool(), + Connection :: term(), Name :: atom(), Table :: binary(), Fields :: [binary()], Statement :: iodata()) -> {ok, term()} | {error, any()}. -prepare(_Host, Connection, Name, _Table, _Fields, Statement) -> +prepare(_Pool, Connection, Name, _Table, _Fields, Statement) -> mysql:prepare(Connection, Name, Statement). -spec execute(Connection :: term(), StatementRef :: term(), Params :: [term()], diff --git a/apps/ejabberd/src/rdbms/mongoose_rdbms_odbc.erl b/apps/ejabberd/src/rdbms/mongoose_rdbms_odbc.erl index 5fd5015daad..22cf6adf224 100644 --- a/apps/ejabberd/src/rdbms/mongoose_rdbms_odbc.erl +++ b/apps/ejabberd/src/rdbms/mongoose_rdbms_odbc.erl @@ -22,10 +22,9 @@ %% API --spec escape_format(Host :: ejabberd:server()) -> atom(). -escape_format(Host) -> - Key = {odbc_server_type, Host}, - case ejabberd_config:get_local_option_or_default(Key, odbc) of +-spec escape_format(mongoose_rdbms:pool()) -> atom(). +escape_format(Pool) -> + case mongoose_rdbms_sup:get_option(Pool, odbc_server_type) of pgsql -> hex; mssql -> @@ -57,11 +56,12 @@ query(Connection, Query, Timeout) when is_binary(Query) -> query(Connection, Query, Timeout) -> parse(odbc:sql_query(Connection, Query, Timeout)). --spec prepare(Host :: ejabberd:server(), Connection :: term(), Name :: atom(), Table :: binary(), +-spec prepare(Pool :: mongoose_rdbms:pool(), + Connection :: term(), Name :: atom(), Table :: binary(), Fields :: [binary()], Statement :: iodata()) -> {ok, {[binary()], [fun((term()) -> tuple())]}}. -prepare(Host, Connection, _Name, Table, Fields, Statement) -> - BinEscapeFormat = escape_format(Host), +prepare(Pool, Connection, _Name, Table, Fields, Statement) -> + BinEscapeFormat = escape_format(Pool), {ok, TableDesc} = odbc:describe_table(Connection, unicode:characters_to_list(Table)), SplitQuery = binary:split(iolist_to_binary(Statement), <<"?">>, [global]), ParamMappers = [field_name_to_mapper(BinEscapeFormat, TableDesc, Field) || Field <- Fields], diff --git a/apps/ejabberd/src/rdbms/mongoose_rdbms_pgsql.erl b/apps/ejabberd/src/rdbms/mongoose_rdbms_pgsql.erl index 0449d65b24c..f712c45cc2b 100644 --- a/apps/ejabberd/src/rdbms/mongoose_rdbms_pgsql.erl +++ b/apps/ejabberd/src/rdbms/mongoose_rdbms_pgsql.erl @@ -26,8 +26,8 @@ %% API --spec escape_format(Host :: ejabberd:server()) -> atom(). -escape_format(_Host) -> +-spec escape_format(mongoose_rdbms:pool()) -> atom(). +escape_format(_Pool) -> hex. -spec connect(Args :: any(), QueryTimeout :: non_neg_integer()) -> @@ -51,10 +51,11 @@ disconnect(Connection) -> query(Connection, Query, _Timeout) -> pgsql_to_odbc(epgsql:squery(Connection, Query)). --spec prepare(Host :: ejabberd:server(), Connection :: term(), Name :: atom(), Table :: binary(), +-spec prepare(Pool :: mongoose_rdbms:pool(), + Connection :: term(), Name :: atom(), Table :: binary(), Fields :: [binary()], Statement :: iodata()) -> {ok, term()} | {error, any()}. -prepare(_Host, Connection, Name, _Table, _Fields, Statement) -> +prepare(_Pool, Connection, Name, _Table, _Fields, Statement) -> BinName = atom_to_binary(Name, latin1), ReplacedStatement = replace_question_marks(Statement), case epgsql:parse(Connection, BinName, ReplacedStatement, []) of diff --git a/apps/ejabberd/src/rdbms/mongoose_rdbms_sup.erl b/apps/ejabberd/src/rdbms/mongoose_rdbms_sup.erl index a015d5b2e31..f48876a2864 100644 --- a/apps/ejabberd/src/rdbms/mongoose_rdbms_sup.erl +++ b/apps/ejabberd/src/rdbms/mongoose_rdbms_sup.erl @@ -30,77 +30,75 @@ -author('konrad.zemek@erlang-solutions.com'). %% API --export([start_link/1, +-export([start_link/0, init/1, get_pids/1, - default_pool/1, - add_pool/4, - remove_pool/2 + pool_proc/1, + pool/1, + add_pool/1, + remove_pool/1, + get_option/2 ]). -include("ejabberd.hrl"). --define(POOL_NAME, odbc_pool). +-define(DEFAULT_POOL_NAME, default). -define(DEFAULT_POOL_SIZE, 10). --spec start_link(binary() | string()) -> ignore | {error, term()} | {ok, pid()}. -start_link(Host) -> - supervisor:start_link({local, gen_mod:get_module_proc(Host, ?MODULE)}, ?MODULE, Host). +-spec start_link() -> ignore | {error, term()} | {ok, pid()}. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). - --spec init(ejabberd:server()) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. -init(Host) -> +-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. +init([]) -> ok = application:ensure_started(worker_pool), catch ets:new(prepared_statements, [public, named_table, {read_concurrency, true}]), - PoolSize = pool_size(Host), - PoolName = default_pool(Host), - ChildrenSpec = [pool_spec(Host, default_pool, PoolName, PoolSize)], - {ok, {{one_for_one, 5, 60}, ChildrenSpec}}. - - --spec add_pool(Host :: ejabberd:server(), Id :: supervisor:child_id(), - Name :: atom(), Size :: pos_integer()) -> supervisor:startchild_ret(). -add_pool(Host, Id, Name, Size) -> - Sup = gen_mod:get_module_proc(Host, ?MODULE), - ChildSpec = pool_spec(Host, Id, Name, Size), - supervisor:start_child(Sup, ChildSpec). + {ok, {{one_for_one, 5, 60}, []}}. +-spec add_pool(Pool :: mongoose_rdbms:pool()) -> supervisor:startchild_ret(). +add_pool(Pool) -> + Size = pool_size(Pool), + ChildSpec = pool_spec(Pool, Size), + mongoose_metrics:ensure_db_pool_metric(Pool), + supervisor:start_child(?MODULE, ChildSpec). --spec remove_pool(Host :: ejabberd:server(), Id :: supervisor:child_id()) -> ok. -remove_pool(Host, Id) -> - Sup = gen_mod:get_module_proc(Host, ?MODULE), - ok = supervisor:terminate_child(Sup, Id), - ok = supervisor:delete_child(Sup, Id). +-spec remove_pool(Pool :: mongoose_rdbms:pool()) -> ok. +remove_pool(Pool) -> + ok = supervisor:terminate_child(?MODULE, Pool), + ok = supervisor:delete_child(?MODULE, Pool). - --spec pool_spec(Host :: ejabberd:server(), Id :: supervisor:child_id(), - Name :: atom(), Size :: pos_integer()) -> supervisor:child_spec(). -pool_spec(Host, Id, Name, Size) -> - Opts = [{workers, Size}, {worker, {mongoose_rdbms, Host}}, {pool_sup_shutdown, infinity}], - {Id, {wpool, start_pool, [Name, Opts]}, transient, 2000, supervisor, dynamic}. +-spec pool_spec(Pool :: mongoose_rdbms:pool(), Size :: pos_integer()) -> supervisor:child_spec(). +pool_spec(Pool, Size) -> + Opts = [{workers, Size}, {worker, {mongoose_rdbms, Pool}}, {pool_sup_shutdown, infinity}], + {Pool, {wpool, start_pool, [pool_proc(Pool), Opts]}, transient, 2000, supervisor, dynamic}. -spec get_pids(ejabberd:server() | atom()) -> [pid()]. -get_pids(Host) when is_binary(Host) -> - get_pids(default_pool(Host)); -get_pids(PoolName) -> - case whereis(PoolName) of +get_pids(HostOrPool) -> + PoolProc = pool_proc(HostOrPool), + case whereis(PoolProc) of undefined -> []; _ -> - Stats = wpool:stats(PoolName), + Stats = wpool:stats(PoolProc), {workers, Workers} = lists:keyfind(workers, 1, Stats), - [whereis(wpool_pool:worker_name(PoolName, WorkerId)) || {WorkerId, _} <- Workers] + [whereis(wpool_pool:worker_name(PoolProc, WorkerId)) || {WorkerId, _} <- Workers] end. - --spec default_pool(Host :: ejabberd:server()) -> atom(). -default_pool(Host) -> - gen_mod:get_module_proc(Host, ?POOL_NAME). - - --spec pool_size(ejabberd:server()) -> integer(). -pool_size(Host) -> - case ejabberd_config:get_local_option({odbc_pool_size, Host}) of +-spec pool_proc(mongoose_rdbms:server()) -> atom(). +pool_proc(Host) when is_binary(Host) -> + pool_proc(pool(Host)); +pool_proc(Pool) when is_atom(Pool) -> + gen_mod:get_module_proc(atom_to_list(Pool), mongoose_rdbms_pool). + +-spec pool(mongoose_rdbms:server()) -> mongoose_rdbms:pool(). +pool(Host) when is_binary(Host) -> + ejabberd_config:get_local_option_or_default({odbc_pool, Host}, ?DEFAULT_POOL_NAME); +pool(Pool) when is_atom(Pool) -> + Pool. + +-spec pool_size(mongoose_rdbms:pool()) -> integer(). +pool_size(Pool) -> + case get_option(Pool, odbc_pool_size) of undefined -> ?DEFAULT_POOL_SIZE; Size when is_integer(Size) -> @@ -108,7 +106,14 @@ pool_size(Host) -> InvalidSize -> Size = ?DEFAULT_POOL_SIZE, ?ERROR_MSG("Wrong odbc_pool_size definition '~p' " - "for host ~p, default to ~p~n", - [InvalidSize, Host, Size]), + "for pool ~p, default to ~p~n", + [InvalidSize, Pool, Size]), Size end. + +-spec get_option(mongoose_rdbms:pool(), atom()) -> term(). +get_option(Pool, Option) -> + ejabberd_config:get_local_option(config_key(Pool, Option)). + +config_key(Pool, Option) -> + {Option, odbc_pool, Pool}. diff --git a/doc/Advanced-configuration.md b/doc/Advanced-configuration.md index 9a9b59529a0..111203346c4 100644 --- a/doc/Advanced-configuration.md +++ b/doc/Advanced-configuration.md @@ -10,7 +10,7 @@ The tuple order is important, unless the no `host_config` option is set. Retaini ## Options -* All options except `hosts`, `host` and `host_config` can be used in `host_config` tuple. +* All options except `hosts`, `host`, `host_config`, `pool` and the ODBC options can be used in `host_config` tuple. * There are two kinds of local options - those that are kept separately for each domain in config file (defined inside `host_config`) and the options local for a node in the cluster. @@ -129,7 +129,23 @@ The tuple order is important, unless the no `host_config` option is set. Retaini ### Database setup -`odbc` prefixes may be misleading. Such options apply to all kinds of DB connections, not only pure ODBC. +#### Connection pools + +* **pool** (multi, local) + * **Description:** Declares a named pool of connections to the database. At least one pool is required to connect to an SQL database. + * **Syntax:** `{pool, odbc, PoolName}.` or `{pool, odbc, PoolName, Options}.` + * **Examples:** `{pool, odbc, default}.` + +* **odbc_pool** (local) + * **Description:** Name of the default connection pool used to connect to the database. + * **Syntax:** `{odbc_pool, PoolName}` + * **Default:** `default` + +#### Connection setup + +The following options can be used to configure a connection pool. To set the options for all connection pools, put them on the top level of the configuration file. To set them for an individual pool, put them inside the `Options` list in a pool specification. Setting `odbc_server` is mandatory. + +*Note*: `odbc` prefixes may be misleading. Such options apply to all kinds of DB connections, not only pure ODBC. * **odbc_server** (local) * **Description:** SQL DB connection configuration. Currently supported DB types are `mysql` and `pgsql`. diff --git a/doc/Basic-configuration.md b/doc/Basic-configuration.md index 1069eb6c077..4ab3542603c 100644 --- a/doc/Basic-configuration.md +++ b/doc/Basic-configuration.md @@ -23,10 +23,14 @@ There are 2 types of options: params and features. Unlike params, features can b * **host_config** - feature * **Description:** List of specific options for chosen XMPP domains. They will override the global ones. Allowed keys are marked on [Advanced configuration](Advanced-configuration.md) page - -* **Syntax:** `"{host_config, \"overridden-domain\", [{key, value}]}."` + * **Syntax:** `"{host_config, \"overridden-domain\", [{key, value}]}."` * **Example:** `"{host_config, \"localhost2\", [{auth_method, anonymous}, {allow_multiple_connections, false}]}." ` +* **pool** - feature + * **Description:** A named pool of connections to an SQL DB. To enable the connection, remove '%%' prefix from value and configure it with the `odbc_server` option (see below). + * **Syntax:** `"{pool, odbc, PoolName}."` or `"{pool, odbc, PoolName, Options}."` + * **Examples:** `"{pool, odbc, default}."` + * **odbc_server** - feature * **Description:** SQL DB connection configuration. Currently supported DB types are `mysql` and `pgsql`. To enable the connection, remove '%%' prefix from value. * **Syntax:** `"{odbc_server, {Type, Host, Port, DBName, Username, Password}}."` diff --git a/rel/files/ejabberd.cfg b/rel/files/ejabberd.cfg index c3a7f98acf6..5d2ba942cca 100755 --- a/rel/files/ejabberd.cfg +++ b/rel/files/ejabberd.cfg @@ -456,6 +456,10 @@ %% %%{odbc_pool_size, 10}. +%% Enable the default database connection pool +%%{pool, odbc, default}. +{{{odbc_pools}}} + %%% ==================== %%% RIAK SETUP %%% ==================== diff --git a/test.disabled/ejabberd_tests/test.config b/test.disabled/ejabberd_tests/test.config index d915a5dba12..3aa48407829 100644 --- a/test.disabled/ejabberd_tests/test.config +++ b/test.disabled/ejabberd_tests/test.config @@ -175,6 +175,7 @@ {pgsql_mnesia, [{sm_backend, "{mnesia, []}"}, {auth_method, "odbc"}, + {odbc_pools, "{pool, odbc, default}."}, {odbc_server, "{odbc_server, {pgsql, \"localhost\", \"ejabberd\", \"ejabberd\", \"%ODBC_PASSWORD%\"}}."}, {mod_last, "{mod_last, [{backend, odbc}]},"}, {mod_privacy, "{mod_privacy, [{backend, odbc}]},"}, @@ -185,6 +186,7 @@ {odbc_pgsql_mnesia, [{sm_backend, "{mnesia, []}"}, {auth_method, "odbc"}, + {odbc_pools, "{pool, odbc, default}."}, {odbc_server, "{odbc_server, \"DSN=ejabberd-pgsql;UID=ejabberd;PWD=%ODBC_PASSWORD%\"}."}, {odbc_server_type, "{odbc_server_type, pgsql}."}, {mod_last, "{mod_last, [{backend, odbc}]},"}, @@ -196,6 +198,7 @@ {mysql_mnesia, [{sm_backend, "{mnesia, []}"}, {auth_method, "odbc"}, + {odbc_pools, "{pool, odbc, default}."}, {odbc_server, "{odbc_server, {mysql, \"localhost\", \"ejabberd\", \"ejabberd\", \"%ODBC_PASSWORD%\"}}."}, {mod_last, "{mod_last, [{backend, odbc}]},"}, {mod_privacy, "{mod_privacy, [{backend, odbc}]},"}, @@ -206,6 +209,7 @@ {mysql_redis, [{sm_backend, "{redis, [{pool_size, 3}, {worker_config, [{host, \"localhost\"}, {port, 6379}]}]}"}, {auth_method, "odbc"}, + {odbc_pools, "{pool, odbc, default}."}, {odbc_server, "{odbc_server, {mysql, \"localhost\", \"ejabberd\", \"ejabberd\", \"%ODBC_PASSWORD%\"}}."}, {mod_last, "{mod_last, [{backend, odbc}]},"}, {mod_privacy, "{mod_privacy, [{backend, odbc}]},"}, diff --git a/test.disabled/ejabberd_tests/tests/mam_SUITE.erl b/test.disabled/ejabberd_tests/tests/mam_SUITE.erl index 00d14d5574c..eb077ab921a 100644 --- a/test.disabled/ejabberd_tests/tests/mam_SUITE.erl +++ b/test.disabled/ejabberd_tests/tests/mam_SUITE.erl @@ -737,7 +737,7 @@ init_per_testcase(C=metric_incremented_when_store_message, ConfigIn) -> Config = case ?config(configuration, ConfigIn) of odbc_async_pool -> MongooseMetrics = [ - {[global, data, odbc, mam_async], + {[global, data, odbc, default], [{recv_oct, '>'}, {send_oct, '>'}]} ], [{mongoose_metrics, MongooseMetrics} | ConfigIn]; From e4cc48ac61f65b28f74d4b5bee6c6606f65b7850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 15 Mar 2017 11:57:38 +0100 Subject: [PATCH 2/4] Do not store ODBC options in host config --- apps/ejabberd/src/ejabberd_config.erl | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/apps/ejabberd/src/ejabberd_config.erl b/apps/ejabberd/src/ejabberd_config.erl index c844d99ca1f..651c7d158e8 100644 --- a/apps/ejabberd/src/ejabberd_config.erl +++ b/apps/ejabberd/src/ejabberd_config.erl @@ -590,22 +590,18 @@ process_term(Term, State) -> {all_metrics_are_global, Value} -> add_option(all_metrics_are_global, Value, State); {_Opt, _Val} -> - State1 = process_term_for_hosts(Term, State), - process_term_for_odbc_pools(Term, State1) + process_term_for_hosts_and_pools(Term, State) end. -process_term_for_hosts(Term, State) -> - lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, - State, State#state.hosts). - -process_term_for_odbc_pools(Term = {Key, _Val}, State) -> +process_term_for_hosts_and_pools(Term = {Key, _Val}, State) -> BKey = atom_to_binary(Key, utf8), case get_key_group(BKey, Key) of odbc -> lists:foldl(fun(Pool, S) -> process_db_pool_term(Term, Pool, S) end, State, State#state.odbc_pools); _ -> - State + lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, + State, State#state.hosts) end. -spec process_host_term(Term :: host_term(), @@ -628,8 +624,6 @@ process_host_term(Term, Host, State) -> State; {hosts, _Hosts} -> State; - {odbc_server, ODBCServer} -> - add_option({odbc_server, Host}, ODBCServer, State); {odbc_pool, Pool} when is_atom(Pool) -> add_option({odbc_pool, Host}, Pool, State); {riak_server, RiakConfig} -> From 2ea991f05ae2cdc0bb894157b1bc78d776c84433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 15 Mar 2017 11:58:11 +0100 Subject: [PATCH 3/4] Fix RDBMS tests after DB pool changes --- apps/ejabberd/test/mongoose_rdbms_SUITE.erl | 11 +++++------ .../ejabberd_tests/tests/metrics_roster_SUITE.erl | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/ejabberd/test/mongoose_rdbms_SUITE.erl b/apps/ejabberd/test/mongoose_rdbms_SUITE.erl index 3f2baaccd7f..7f7abced450 100644 --- a/apps/ejabberd/test/mongoose_rdbms_SUITE.erl +++ b/apps/ejabberd/test/mongoose_rdbms_SUITE.erl @@ -9,7 +9,6 @@ -define(eq(E, I), ?assertEqual(E, I)). -define(ne(E, I), ?assert(E =/= I)). --define(HOST, <<"localhost">>). -define(KEEPALIVE_INTERVAL, 1). -define(KEEPALIVE_QUERY, <<"SELECT 1;">>). @@ -61,7 +60,7 @@ end_per_testcase(_, Config) -> %% Test cases keepalive_interval(Config) -> - {ok, Pid} = gen_server:start_link(mongoose_rdbms, ?HOST, []), + {ok, Pid} = gen_server:start_link(mongoose_rdbms, default, []), true = erlang:unlink(Pid), timer:sleep(5500), ?eq(5, query_calls(Config)), @@ -69,7 +68,7 @@ keepalive_interval(Config) -> ok. keepalive_exit(Config) -> - {ok, Pid} = gen_server:start_link(mongoose_rdbms, ?HOST, []), + {ok, Pid} = gen_server:start_link(mongoose_rdbms, default, []), true = erlang:unlink(Pid), Monitor = erlang:monitor(process, Pid), timer:sleep(3500), @@ -86,10 +85,10 @@ keepalive_exit(Config) -> meck_config(Server, KeepaliveInterval) -> meck:new(ejabberd_config, [no_link]), meck:expect(ejabberd_config, get_local_option, - fun({odbc_keepalive_interval, _Host}) -> KeepaliveInterval; - ({odbc_start_interval, _Host}) -> 30; + fun({odbc_keepalive_interval, odbc_pool, default}) -> KeepaliveInterval; + ({odbc_start_interval, odbc_pool, default}) -> 30; + ({odbc_server, odbc_pool, default}) -> server(Server); (max_fsm_queue) -> 1024; - ({odbc_server, _}) -> server(Server); (all_metrics_are_global) -> false end). diff --git a/test.disabled/ejabberd_tests/tests/metrics_roster_SUITE.erl b/test.disabled/ejabberd_tests/tests/metrics_roster_SUITE.erl index abfeee4b8bb..f6ecd894300 100644 --- a/test.disabled/ejabberd_tests/tests/metrics_roster_SUITE.erl +++ b/test.disabled/ejabberd_tests/tests/metrics_roster_SUITE.erl @@ -63,7 +63,7 @@ init_per_suite(Config) -> MongooseMetrics = [{[global, data, xmpp, received, xml_stanza_size], changed}, {[global, data, xmpp, sent, xml_stanza_size], changed}, - {fun roster_odbc_precondition/0, [global, data, odbc, regular], + {fun roster_odbc_precondition/0, [global, data, odbc, default], [{recv_oct, '>'}, {send_oct, '>'}]}, {[global, backends, mod_roster, get_subscription_lists], changed} ], From 81abee5c610c35190e392ae77f3a874b6ad05ab3 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Mon, 20 Mar 2017 15:19:15 +0100 Subject: [PATCH 4/4] mock mongoose_rdbms_sup:get_config instead of raw ejabberd_config --- apps/ejabberd/test/mongoose_rdbms_SUITE.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/ejabberd/test/mongoose_rdbms_SUITE.erl b/apps/ejabberd/test/mongoose_rdbms_SUITE.erl index 7f7abced450..b1a56a2e86e 100644 --- a/apps/ejabberd/test/mongoose_rdbms_SUITE.erl +++ b/apps/ejabberd/test/mongoose_rdbms_SUITE.erl @@ -85,11 +85,14 @@ keepalive_exit(Config) -> meck_config(Server, KeepaliveInterval) -> meck:new(ejabberd_config, [no_link]), meck:expect(ejabberd_config, get_local_option, - fun({odbc_keepalive_interval, odbc_pool, default}) -> KeepaliveInterval; - ({odbc_start_interval, odbc_pool, default}) -> 30; - ({odbc_server, odbc_pool, default}) -> server(Server); - (max_fsm_queue) -> 1024; + fun(max_fsm_queue) -> 1024; (all_metrics_are_global) -> false + end), + meck:new(mongoose_rdbms_sup, [no_link, passthrough]), + meck:expect(mongoose_rdbms_sup, get_option, + fun(default, odbc_keepalive_interval) -> KeepaliveInterval; + (default, odbc_start_interval) -> 30; + (default, odbc_server) -> server(Server) end). meck_db(odbc) -> @@ -125,6 +128,7 @@ meck_error(pgsql) -> meck_unload(DbType) -> meck:unload(ejabberd_config), + meck:unload(mongoose_rdbms_sup), do_meck_unload(DbType). do_meck_unload(odbc) ->