25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-26 17:38:45 +01:00

* src/ejabberd_rdbms.erl: fix SQL database reconnection

issues (EJAB-764) and add odbc_start_interval configuration
directive (default to 30 seconds).
* src/odbc/ejabberd_odbc.erl: likewise.
* src/odbc/ejabberd_odbc_sup.erl: likewise.
* doc/guide.tex: likewise.

SVN Revision: 1600
This commit is contained in:
Jérôme Sautret 2008-10-06 14:18:46 +00:00
parent a2340ea8b8
commit 2f8127d343
5 changed files with 229 additions and 193 deletions

View File

@ -1,3 +1,12 @@
2008-10-06 Jerome Sautret <jerome.sautret@process-one.net>
* src/ejabberd_rdbms.erl: fix SQL database reconnection
issues (EJAB-764) and add odbc_start_interval configuration
directive (default to 30 seconds).
* src/odbc/ejabberd_odbc.erl: likewise.
* src/odbc/ejabberd_odbc_sup.erl: likewise.
* doc/guide.tex: likewise.
2008-10-03 Jerome Sautret <jerome.sautret@process-one.net> 2008-10-03 Jerome Sautret <jerome.sautret@process-one.net>
* src/odbc/odbc_queries.erl: Fix empty query that fail on MySQL. * src/odbc/odbc_queries.erl: Fix empty query that fail on MySQL.

View File

@ -1617,6 +1617,13 @@ Specify in seconds: for example 28800 means 8 hours.
{odbc_keepalive_interval, undefined}. {odbc_keepalive_interval, undefined}.
\end{verbatim} \end{verbatim}
If the connection to the database fails, \ejabberd{} waits 30 seconds before retrying.
You can modify this interval with this option:
\begin{verbatim}
{odbc_start_interval, 30}.
\end{verbatim}
\makesubsubsection{compilemysql}{Driver Compilation} \makesubsubsection{compilemysql}{Driver Compilation}
\ind{MySQL!Driver Compilation} \ind{MySQL!Driver Compilation}

View File

@ -52,14 +52,21 @@ start_hosts() ->
%% Start the ODBC module on the given host %% Start the ODBC module on the given host
start_odbc(Host) -> start_odbc(Host) ->
Supervisor_name = gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
ChildSpec = ChildSpec =
{gen_mod:get_module_proc(Host, ejabberd_odbc_sup), {Supervisor_name,
{ejabberd_odbc_sup, start_link, [Host]}, {ejabberd_odbc_sup, start_link, [Host]},
temporary, transient,
infinity, infinity,
supervisor, supervisor,
[ejabberd_odbc_sup]}, [ejabberd_odbc_sup]},
supervisor:start_child(ejabberd_sup, ChildSpec). 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.
%% Returns true if we have configured odbc_server for the given host %% Returns true if we have configured odbc_server for the given host
needs_odbc(Host) -> needs_odbc(Host) ->

View File

@ -30,7 +30,7 @@
-behaviour(gen_server). -behaviour(gen_server).
%% External exports %% External exports
-export([start/1, start_link/1, -export([start/1, start_link/2,
sql_query/2, sql_query/2,
sql_query_t/1, sql_query_t/1,
sql_transaction/2, sql_transaction/2,
@ -63,8 +63,8 @@
start(Host) -> start(Host) ->
gen_server:start(ejabberd_odbc, [Host], []). gen_server:start(ejabberd_odbc, [Host], []).
start_link(Host) -> start_link(Host, StartInterval) ->
gen_server:start_link(ejabberd_odbc, [Host], []). gen_server:start_link(ejabberd_odbc, [Host, StartInterval], []).
sql_query(Host, Query) -> sql_query(Host, Query) ->
gen_server:call(ejabberd_odbc_sup:get_random_pid(Host), gen_server:call(ejabberd_odbc_sup:get_random_pid(Host),
@ -131,10 +131,10 @@ escape_like(C) -> odbc_queries:escape(C).
%% ignore | %% ignore |
%% {stop, Reason} %% {stop, Reason}
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
init([Host]) -> init([Host, StartInterval]) ->
case ejabberd_config:get_local_option({odbc_keepalive_interval, Host}) of case ejabberd_config:get_local_option({odbc_keepalive_interval, Host}) of
Interval when is_integer(Interval) -> KeepaliveInterval when is_integer(KeepaliveInterval) ->
timer:apply_interval(Interval*1000, ?MODULE, keep_alive, [self()]); timer:apply_interval(KeepaliveInterval*1000, ?MODULE, keep_alive, [self()]);
undefined -> undefined ->
ok; ok;
_Other -> _Other ->
@ -144,16 +144,16 @@ init([Host]) ->
case SQLServer of case SQLServer of
%% Default pgsql port %% Default pgsql port
{pgsql, Server, DB, Username, Password} -> {pgsql, Server, DB, Username, Password} ->
pgsql_connect(Server, ?PGSQL_PORT, DB, Username, Password); pgsql_connect(Server, ?PGSQL_PORT, DB, Username, Password, StartInterval);
{pgsql, Server, Port, DB, Username, Password} when is_integer(Port) -> {pgsql, Server, Port, DB, Username, Password} when is_integer(Port) ->
pgsql_connect(Server, Port, DB, Username, Password); pgsql_connect(Server, Port, DB, Username, Password, StartInterval);
%% Default mysql port %% Default mysql port
{mysql, Server, DB, Username, Password} -> {mysql, Server, DB, Username, Password} ->
mysql_connect(Server, ?MYSQL_PORT, DB, Username, Password); mysql_connect(Server, ?MYSQL_PORT, DB, Username, Password, StartInterval);
{mysql, Server, Port, DB, Username, Password} when is_integer(Port) -> {mysql, Server, Port, DB, Username, Password} when is_integer(Port) ->
mysql_connect(Server, Port, DB, Username, Password); mysql_connect(Server, Port, DB, Username, Password, StartInterval);
_ when is_list(SQLServer) -> _ when is_list(SQLServer) ->
odbc_connect(SQLServer) odbc_connect(SQLServer, StartInterval)
end. end.
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
@ -259,7 +259,7 @@ execute_transaction(State, F, NRestarts) ->
%% part of init/1 %% part of init/1
%% Open an ODBC database connection %% Open an ODBC database connection
odbc_connect(SQLServer) -> odbc_connect(SQLServer, StartInterval) ->
application:start(odbc), application:start(odbc),
case odbc:connect(SQLServer,[{scrollable_cursors, off}]) of case odbc:connect(SQLServer,[{scrollable_cursors, off}]) of
{ok, Ref} -> {ok, Ref} ->
@ -268,8 +268,8 @@ odbc_connect(SQLServer) ->
{error, Reason} -> {error, Reason} ->
?ERROR_MSG("ODBC connection (~s) failed: ~p~n", ?ERROR_MSG("ODBC connection (~s) failed: ~p~n",
[SQLServer, Reason]), [SQLServer, Reason]),
%% If we can't connect we wait for 30 seconds before retrying %% If we can't connect we wait before retrying
timer:sleep(30000), timer:sleep(StartInterval),
{stop, odbc_connection_failed} {stop, odbc_connection_failed}
end. end.
@ -278,15 +278,15 @@ odbc_connect(SQLServer) ->
%% part of init/1 %% part of init/1
%% Open a database connection to PostgreSQL %% Open a database connection to PostgreSQL
pgsql_connect(Server, Port, DB, Username, Password) -> pgsql_connect(Server, Port, DB, Username, Password, StartInterval) ->
case pgsql:connect(Server, DB, Username, Password, Port) of case pgsql:connect(Server, DB, Username, Password, Port) of
{ok, Ref} -> {ok, Ref} ->
erlang:monitor(process, Ref), erlang:monitor(process, Ref),
{ok, #state{db_ref = Ref, db_type = pgsql}}; {ok, #state{db_ref = Ref, db_type = pgsql}};
{error, Reason} -> {error, Reason} ->
?ERROR_MSG("PostgreSQL connection failed: ~p~n", [Reason]), ?ERROR_MSG("PostgreSQL connection failed: ~p~n", [Reason]),
%% If we can't connect we wait for 30 seconds before retrying %% If we can't connect we wait before retrying
timer:sleep(30000), timer:sleep(StartInterval),
{stop, pgsql_connection_failed} {stop, pgsql_connection_failed}
end. end.
@ -317,7 +317,7 @@ pgsql_item_to_odbc(_) ->
%% part of init/1 %% part of init/1
%% Open a database connection to MySQL %% Open a database connection to MySQL
mysql_connect(Server, Port, DB, Username, Password) -> mysql_connect(Server, Port, DB, Username, Password, StartInterval) ->
NoLogFun = fun(_Level,_Format,_Argument) -> ok end, NoLogFun = fun(_Level,_Format,_Argument) -> ok end,
case mysql_conn:start(Server, Port, Username, Password, DB, NoLogFun) of case mysql_conn:start(Server, Port, Username, Password, DB, NoLogFun) of
{ok, Ref} -> {ok, Ref} ->
@ -330,9 +330,10 @@ mysql_connect(Server, Port, DB, Username, Password) ->
"SERIALIZABLE;"], self()), "SERIALIZABLE;"], self()),
{ok, #state{db_ref = Ref, db_type = mysql}}; {ok, #state{db_ref = Ref, db_type = mysql}};
{error, Reason} -> {error, Reason} ->
?ERROR_MSG("MySQL connection failed: ~p~n", [Reason]), ?ERROR_MSG("MySQL connection failed: ~p~nWaiting ~p seconds before retrying...~n",
%% If we can't connect we wait for 30 seconds before retrying [Reason, StartInterval div 1000]),
timer:sleep(30000), %% If we can't connect we wait before retrying
timer:sleep(StartInterval),
{stop, mysql_connection_failed} {stop, mysql_connection_failed}
end. end.

View File

@ -37,32 +37,44 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-define(DEFAULT_POOL_SIZE, 10). -define(DEFAULT_POOL_SIZE, 10).
-define(DEFAULT_ODBC_START_INTERVAL, 30). % 30 seconds
start_link(Host) -> start_link(Host) ->
supervisor:start_link({local, gen_mod:get_module_proc(Host, ?MODULE)}, supervisor:start_link({local, gen_mod:get_module_proc(Host, ?MODULE)},
?MODULE, [Host]). ?MODULE, [Host]).
init([Host]) -> init([Host]) ->
N = case ejabberd_config:get_local_option({odbc_pool_size, Host}) of PoolSize = case ejabberd_config:get_local_option({odbc_pool_size, Host}) of
I when is_integer(I) -> I when is_integer(I) ->
I; I;
undefined -> undefined ->
?DEFAULT_POOL_SIZE; ?DEFAULT_POOL_SIZE;
Other -> Other ->
?ERROR_MSG("Wrong odbc_pool_size definition '~p' for host ~p, default to ~p~n", ?ERROR_MSG("Wrong odbc_pool_size definition '~p' for host ~p, default to ~p~n",
[Other, Host, ?DEFAULT_POOL_SIZE]), [Other, Host, ?DEFAULT_POOL_SIZE]),
?DEFAULT_POOL_SIZE ?DEFAULT_POOL_SIZE
end, end,
{ok, {{one_for_one, 10, 6}, StartInterval = case ejabberd_config:get_local_option({odbc_start_interval, Host}) of
Interval when is_integer(Interval) ->
Interval;
undefined ->
?DEFAULT_ODBC_START_INTERVAL;
_Other2 ->
?ERROR_MSG("Wrong odbc_start_interval definition '~p' for host ~p"
", defaulting to ~p~n",
[_Other2, Host, ?DEFAULT_ODBC_START_INTERVAL]),
?DEFAULT_ODBC_START_INTERVAL
end,
{ok, {{one_for_one, PoolSize+1, StartInterval},
lists:map( lists:map(
fun(I) -> fun(I) ->
{I, {I,
{ejabberd_odbc, start_link, [Host]}, {ejabberd_odbc, start_link, [Host, StartInterval*1000]},
transient, transient,
brutal_kill, brutal_kill,
worker, worker,
[?MODULE]} [?MODULE]}
end, lists:seq(1, N))}}. end, lists:seq(1, PoolSize))}}.
get_pids(Host) -> get_pids(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE), Proc = gen_mod:get_module_proc(Host, ?MODULE),