aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_sql_sup.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/ejabberd_sql_sup.erl')
-rw-r--r--src/ejabberd_sql_sup.erl223
1 files changed, 114 insertions, 109 deletions
diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl
index 682414557..074080f5f 100644
--- a/src/ejabberd_sql_sup.erl
+++ b/src/ejabberd_sql_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -25,57 +25,75 @@
-module(ejabberd_sql_sup).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
--export([start_link/1, init/1, add_pid/2, remove_pid/2,
- get_pids/1, get_random_pid/1, transform_options/1,
- opt_type/1]).
+-export([start/1, stop/1, stop/0]).
+-export([start_link/0, start_link/1]).
+-export([init/1, reload/1, config_reloaded/0, is_started/1]).
--include("ejabberd.hrl").
-include("logger.hrl").
--define(PGSQL_PORT, 5432).
-
--define(MYSQL_PORT, 3306).
-
--define(DEFAULT_POOL_SIZE, 10).
+start(Host) ->
+ case is_started(Host) of
+ true -> ok;
+ false ->
+ App = case ejabberd_option:sql_type(Host) of
+ mysql -> p1_mysql;
+ pgsql -> p1_pgsql;
+ sqlite -> sqlite3;
+ _ -> odbc
+ end,
+ ejabberd:start_app(App),
+ Spec = #{id => gen_mod:get_module_proc(Host, ?MODULE),
+ start => {ejabberd_sql_sup, start_link, [Host]},
+ restart => transient,
+ shutdown => infinity,
+ type => supervisor,
+ modules => [?MODULE]},
+ case supervisor:start_child(ejabberd_db_sup, Spec) of
+ {ok, _} -> ok;
+ {error, {already_started, Pid}} ->
+ %% Wait for the supervisor to fully start
+ _ = supervisor:count_children(Pid),
+ ok;
+ {error, Why} = Err ->
+ ?ERROR_MSG("Failed to start ~ts: ~p", [?MODULE, Why]),
+ Err
+ end
+ end.
--define(DEFAULT_SQL_START_INTERVAL, 30).
+stop(Host) ->
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
+ case supervisor:terminate_child(ejabberd_db_sup, Proc) of
+ ok -> supervisor:delete_child(ejabberd_db_sup, Proc);
+ Err -> Err
+ end.
--define(CONNECT_TIMEOUT, 500).
--record(sql_pool, {host, pid}).
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
start_link(Host) ->
- mnesia:create_table(sql_pool,
- [{ram_copies, [node()]}, {type, bag},
- {local_content, true},
- {attributes, record_info(fields, sql_pool)}]),
- mnesia:add_table_copy(sql_pool, node(), ram_copies),
- F = fun () -> mnesia:delete({sql_pool, Host}) end,
- mnesia:ets(F),
supervisor:start_link({local,
gen_mod:get_module_proc(Host, ?MODULE)},
?MODULE, [Host]).
+stop() ->
+ ejabberd_hooks:delete(host_up, ?MODULE, start, 20),
+ ejabberd_hooks:delete(host_down, ?MODULE, stop, 90),
+ ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 20).
+
+init([]) ->
+ file:delete(ejabberd_sql:freetds_config()),
+ file:delete(ejabberd_sql:odbc_config()),
+ file:delete(ejabberd_sql:odbcinst_config()),
+ ejabberd_hooks:add(host_up, ?MODULE, start, 20),
+ ejabberd_hooks:add(host_down, ?MODULE, stop, 90),
+ ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
+ ignore;
init([Host]) ->
- PoolSize = ejabberd_config:get_option(
- {sql_pool_size, Host},
- fun(I) when is_integer(I), I>0 -> I end,
- ?DEFAULT_POOL_SIZE),
- StartInterval = ejabberd_config:get_option(
- {sql_start_interval, Host},
- fun(I) when is_integer(I), I>0 -> I end,
- ?DEFAULT_SQL_START_INTERVAL),
- Type = ejabberd_config:get_option({sql_type, Host},
- fun(mysql) -> mysql;
- (pgsql) -> pgsql;
- (sqlite) -> sqlite;
- (mssql) -> mssql;
- (odbc) -> odbc
- end, odbc),
+ Type = ejabberd_option:sql_type(Host),
+ PoolSize = get_pool_size(Type, Host),
case Type of
sqlite ->
check_sqlite_db(Host);
@@ -84,57 +102,63 @@ init([Host]) ->
_ ->
ok
end,
-
- {ok,
- {{one_for_one, PoolSize * 10, 1},
- lists:map(fun (I) ->
- {I,
- {ejabberd_sql, start_link,
- [Host, StartInterval * 1000]},
- transient, 2000, worker, [?MODULE]}
- end,
- lists:seq(1, PoolSize))}}.
-
-get_pids(Host) ->
- Rs = mnesia:dirty_read(sql_pool, Host),
- [R#sql_pool.pid || R <- Rs].
-
-get_random_pid(Host) ->
- case get_pids(Host) of
- [] -> none;
- Pids -> lists:nth(erlang:phash(p1_time_compat:unique_integer(), length(Pids)), Pids)
+ {ok, {{one_for_one, PoolSize * 10, 1}, child_specs(Host, PoolSize)}}.
+
+-spec config_reloaded() -> ok.
+config_reloaded() ->
+ lists:foreach(fun reload/1, ejabberd_option:hosts()).
+
+-spec reload(binary()) -> ok.
+reload(Host) ->
+ case is_started(Host) of
+ true ->
+ Sup = gen_mod:get_module_proc(Host, ?MODULE),
+ Type = ejabberd_option:sql_type(Host),
+ PoolSize = get_pool_size(Type, Host),
+ lists:foreach(
+ fun(Spec) ->
+ supervisor:start_child(Sup, Spec)
+ end, child_specs(Host, PoolSize)),
+ lists:foreach(
+ fun({Id, _, _, _}) when Id > PoolSize ->
+ case supervisor:terminate_child(Sup, Id) of
+ ok -> supervisor:delete_child(Sup, Id);
+ _ -> ok
+ end;
+ (_) ->
+ ok
+ end, supervisor:which_children(Sup));
+ false ->
+ ok
end.
-add_pid(Host, Pid) ->
- F = fun () ->
- mnesia:write(#sql_pool{host = Host, pid = Pid})
- end,
- mnesia:ets(F).
-
-remove_pid(Host, Pid) ->
- F = fun () ->
- mnesia:delete_object(#sql_pool{host = Host, pid = Pid})
- end,
- mnesia:ets(F).
-
-transform_options(Opts) ->
- lists:foldl(fun transform_options/2, [], Opts).
-
-transform_options({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
- [{sql_type, Type},
- {sql_server, Server},
- {sql_port, Port},
- {sql_database, DB},
- {sql_username, User},
- {sql_password, Pass}|Opts];
-transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
- transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
-transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
- transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
-transform_options({odbc_server, {sqlite, DB}}, Opts) ->
- transform_options({odbc_server, {sqlite, DB}}, Opts);
-transform_options(Opt, Opts) ->
- [Opt|Opts].
+-spec is_started(binary()) -> boolean().
+is_started(Host) ->
+ whereis(gen_mod:get_module_proc(Host, ?MODULE)) /= undefined.
+
+-spec get_pool_size(atom(), binary()) -> pos_integer().
+get_pool_size(SQLType, Host) ->
+ PoolSize = ejabberd_option:sql_pool_size(Host),
+ if PoolSize > 1 andalso SQLType == sqlite ->
+ ?WARNING_MSG("It's not recommended to set sql_pool_size > 1 for "
+ "sqlite, because it may cause race conditions", []);
+ true ->
+ ok
+ end,
+ PoolSize.
+
+-spec child_spec(binary(), pos_integer()) -> supervisor:child_spec().
+child_spec(Host, I) ->
+ #{id => I,
+ start => {ejabberd_sql, start_link, [Host, I]},
+ restart => transient,
+ shutdown => 2000,
+ type => worker,
+ modules => [?MODULE]}.
+
+-spec child_specs(binary(), pos_integer()) -> [supervisor:child_spec()].
+child_specs(Host, PoolSize) ->
+ [child_spec(Host, I) || I <- lists:seq(1, PoolSize)].
check_sqlite_db(Host) ->
DB = ejabberd_sql:sqlite_db(Host),
@@ -161,16 +185,11 @@ check_sqlite_db(Host) ->
ok
end;
{error, Reason} ->
- ?INFO_MSG("Failed open sqlite database, reason ~p", [Reason])
+ ?WARNING_MSG("Failed open sqlite database, reason ~p", [Reason])
end.
create_sqlite_tables(DB) ->
- SqlDir = case code:priv_dir(ejabberd) of
- {error, _} ->
- ?SQL_DIR;
- PrivDir ->
- filename:join(PrivDir, "sql")
- end,
+ SqlDir = misc:sql_dir(),
File = filename:join(SqlDir, "lite.sql"),
case file:open(File, [read, binary]) of
{ok, Fd} ->
@@ -179,8 +198,8 @@ create_sqlite_tables(DB) ->
[ok = sqlite3:sql_exec(DB, Q) || Q <- Qs],
ok = sqlite3:sql_exec(DB, "commit");
{error, Reason} ->
- ?INFO_MSG("Failed to read SQLite schema file: ~s",
- [file:format_error(Reason)])
+ ?WARNING_MSG("Failed to read SQLite schema file: ~ts",
+ [file:format_error(Reason)])
end.
read_lines(Fd, File, Acc) ->
@@ -210,17 +229,3 @@ read_lines(Fd, File, Acc) ->
?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]),
[]
end.
-
-opt_type(sql_pool_size) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(sql_start_interval) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(sql_type) ->
- fun (mysql) -> mysql;
- (pgsql) -> pgsql;
- (sqlite) -> sqlite;
- (mssql) -> mssql;
- (odbc) -> odbc
- end;
-opt_type(_) ->
- [sql_pool_size, sql_start_interval, sql_type].