mirror of
https://github.com/processone/ejabberd.git
synced 2025-01-03 18:02:28 +01:00
parent
00c613b351
commit
6876a37e61
@ -23,14 +23,15 @@
|
|||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
-module(ejabberd_redis).
|
-module(ejabberd_redis).
|
||||||
|
-ifndef(GEN_SERVER).
|
||||||
-behaviour(gen_server).
|
-define(GEN_SERVER, gen_server).
|
||||||
-behaviour(ejabberd_config).
|
-endif.
|
||||||
|
-behaviour(?GEN_SERVER).
|
||||||
|
|
||||||
-compile({no_auto_import, [get/1, put/2]}).
|
-compile({no_auto_import, [get/1, put/2]}).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0, q/1, qp/1, config_reloaded/0, opt_type/1]).
|
-export([start_link/1, get_proc/1, q/1, qp/1]).
|
||||||
%% Commands
|
%% Commands
|
||||||
-export([multi/1, get/1, set/2, del/1,
|
-export([multi/1, get/1, set/2, del/1,
|
||||||
sadd/2, srem/2, smembers/1, sismember/2, scard/1,
|
sadd/2, srem/2, smembers/1, sismember/2, scard/1,
|
||||||
@ -43,31 +44,40 @@
|
|||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
-define(PROCNAME, 'ejabberd_redis_client').
|
-define(PROCNAME, 'ejabberd_redis_client').
|
||||||
-define(TR_STACK, redis_transaction_stack).
|
-define(TR_STACK, redis_transaction_stack).
|
||||||
|
-define(DEFAULT_MAX_QUEUE, 5000).
|
||||||
|
-define(MAX_RETRIES, 1).
|
||||||
|
-define(CALL_TIMEOUT, 60*1000). %% 60 seconds
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
-record(state, {connection :: {pid(), reference()} | undefined}).
|
-record(state, {connection :: pid() | undefined,
|
||||||
|
num :: pos_integer(),
|
||||||
|
pending_q :: p1_queue:queue()}).
|
||||||
|
|
||||||
-type redis_error() :: {error, binary() | atom()}.
|
-type redis_error() :: {error, binary() | atom()}.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
start_link() ->
|
start_link(I) ->
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
?GEN_SERVER:start_link({local, get_proc(I)}, ?MODULE, [I], []).
|
||||||
|
|
||||||
|
get_proc(I) ->
|
||||||
|
aux:binary_to_atom(
|
||||||
|
iolist_to_binary(
|
||||||
|
[atom_to_list(?MODULE), $_, integer_to_list(I)])).
|
||||||
|
|
||||||
|
get_connection(I) ->
|
||||||
|
aux:binary_to_atom(
|
||||||
|
iolist_to_binary(
|
||||||
|
[atom_to_list(?MODULE), "_connection_", integer_to_list(I)])).
|
||||||
|
|
||||||
q(Command) ->
|
q(Command) ->
|
||||||
try eredis:q(?PROCNAME, Command)
|
call(get_worker(), {q, Command}, ?MAX_RETRIES).
|
||||||
catch _:{noproc, _} -> {error, disconnected};
|
|
||||||
_:{timeout, _} -> {error, timeout}
|
|
||||||
end.
|
|
||||||
|
|
||||||
qp(Pipeline) ->
|
qp(Pipeline) ->
|
||||||
try eredis:qp(?PROCNAME, Pipeline)
|
call(get_worker(), {qp, Pipeline}, ?MAX_RETRIES).
|
||||||
catch _:{noproc, _} -> {error, disconnected};
|
|
||||||
_:{timeout, _} -> {error, timeout}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec multi(fun(() -> any())) -> {ok, list()} | redis_error().
|
-spec multi(fun(() -> any())) -> {ok, list()} | redis_error().
|
||||||
multi(F) ->
|
multi(F) ->
|
||||||
@ -91,14 +101,6 @@ multi(F) ->
|
|||||||
{error, nested_transaction}
|
{error, nested_transaction}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
config_reloaded() ->
|
|
||||||
case is_redis_configured() of
|
|
||||||
true ->
|
|
||||||
?MODULE ! connect;
|
|
||||||
false ->
|
|
||||||
?MODULE ! disconnect
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get(iodata()) -> {ok, undefined | binary()} | redis_error().
|
-spec get(iodata()) -> {ok, undefined | binary()} | redis_error().
|
||||||
get(Key) ->
|
get(Key) ->
|
||||||
case erlang:get(?TR_STACK) of
|
case erlang:get(?TR_STACK) of
|
||||||
@ -274,56 +276,63 @@ hkeys(Key) ->
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
init([]) ->
|
init([I]) ->
|
||||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
{_, State} = handle_info(connect, #state{}),
|
QueueType = get_queue_type(),
|
||||||
{ok, State}.
|
Limit = max_fsm_queue(),
|
||||||
|
self() ! connect,
|
||||||
|
{ok, #state{num = I, pending_q = p1_queue:new(QueueType, Limit)}}.
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(connect, From, #state{connection = undefined,
|
||||||
Reply = ok,
|
pending_q = Q} = State) ->
|
||||||
{reply, Reply, State}.
|
CurrTime = p1_time_compat:monotonic_time(milli_seconds),
|
||||||
|
Q2 = try p1_queue:in({From, CurrTime}, Q)
|
||||||
|
catch error:full ->
|
||||||
|
Q1 = clean_queue(Q, CurrTime),
|
||||||
|
p1_queue:in({From, CurrTime}, Q1)
|
||||||
|
end,
|
||||||
|
{noreply, State#state{pending_q = Q2}};
|
||||||
|
handle_call(connect, From, #state{connection = Pid} = State) ->
|
||||||
|
case is_process_alive(Pid) of
|
||||||
|
true ->
|
||||||
|
{reply, ok, State};
|
||||||
|
false ->
|
||||||
|
self() ! connect,
|
||||||
|
handle_call(connect, From, State#state{connection = undefined})
|
||||||
|
end;
|
||||||
|
handle_call(Request, _From, State) ->
|
||||||
|
?WARNING_MSG("unexepected call: ~p", [Request]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
handle_cast(_Msg, State) ->
|
handle_cast(_Msg, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
handle_info(connect, #state{connection = undefined} = State) ->
|
handle_info(connect, #state{connection = undefined} = State) ->
|
||||||
NewState = case is_redis_configured() of
|
NewState = case connect(State) of
|
||||||
true ->
|
{ok, Connection} ->
|
||||||
case connect() of
|
Q1 = flush_queue(State#state.pending_q),
|
||||||
{ok, Connection} ->
|
State#state{connection = Connection, pending_q = Q1};
|
||||||
State#state{connection = Connection};
|
{error, _} ->
|
||||||
{error, _} ->
|
|
||||||
State
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
State
|
State
|
||||||
end,
|
end,
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
handle_info(connect, State) ->
|
handle_info(connect, State) ->
|
||||||
%% Already connected
|
%% Already connected
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
handle_info(disconnect, #state{connection = {Pid, MRef}} = State) ->
|
handle_info({'EXIT', Pid, _}, State) ->
|
||||||
?INFO_MSG("Disconnecting from Redis server", []),
|
case State#state.connection of
|
||||||
erlang:demonitor(MRef, [flush]),
|
Pid ->
|
||||||
eredis:stop(Pid),
|
self() ! connect,
|
||||||
{noreply, State#state{connection = undefined}};
|
{noreply, State#state{connection = undefined}};
|
||||||
handle_info(disconnect, State) ->
|
_ ->
|
||||||
%% Not connected
|
{noreply, State}
|
||||||
{noreply, State};
|
end;
|
||||||
handle_info({'DOWN', MRef, _Type, Pid, Reason},
|
|
||||||
#state{connection = {Pid, MRef}} = State) ->
|
|
||||||
?INFO_MSG("Redis connection has failed: ~p", [Reason]),
|
|
||||||
connect(),
|
|
||||||
{noreply, State#state{connection = undefined}};
|
|
||||||
handle_info({'EXIT', _, _}, State) ->
|
|
||||||
{noreply, State};
|
|
||||||
handle_info(Info, State) ->
|
handle_info(Info, State) ->
|
||||||
?INFO_MSG("unexpected info = ~p", [Info]),
|
?WARNING_MSG("unexpected info = ~p", [Info]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 20).
|
ok.
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
@ -331,37 +340,7 @@ code_change(_OldVsn, State, _Extra) ->
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
is_redis_configured() ->
|
connect(#state{num = Num}) ->
|
||||||
lists:any(fun is_redis_configured/1, ?MYHOSTS).
|
|
||||||
|
|
||||||
is_redis_configured(Host) ->
|
|
||||||
ServerConfigured = ejabberd_config:has_option({redis_server, Host}),
|
|
||||||
PortConfigured = ejabberd_config:has_option({redis_port, Host}),
|
|
||||||
DBConfigured = ejabberd_config:has_option({redis_db, Host}),
|
|
||||||
PassConfigured = ejabberd_config:has_option({redis_password, Host}),
|
|
||||||
ReconnTimeoutConfigured = ejabberd_config:has_option(
|
|
||||||
{redis_reconnect_timeout, Host}),
|
|
||||||
ConnTimeoutConfigured = ejabberd_config:has_option(
|
|
||||||
{redis_connect_timeout, Host}),
|
|
||||||
Modules = ejabberd_config:get_option(
|
|
||||||
{modules, Host},
|
|
||||||
fun(L) when is_list(L) -> L end, []),
|
|
||||||
SMConfigured = ejabberd_config:get_option(
|
|
||||||
{sm_db_type, Host},
|
|
||||||
fun(V) -> V end) == redis,
|
|
||||||
ModuleWithRedisDBConfigured =
|
|
||||||
lists:any(
|
|
||||||
fun({Module, Opts}) ->
|
|
||||||
gen_mod:db_type(Host, Opts, Module) == redis
|
|
||||||
end, Modules),
|
|
||||||
ServerConfigured or PortConfigured or DBConfigured or PassConfigured or
|
|
||||||
ReconnTimeoutConfigured or ConnTimeoutConfigured or
|
|
||||||
SMConfigured or ModuleWithRedisDBConfigured.
|
|
||||||
|
|
||||||
iolist_to_list(IOList) ->
|
|
||||||
binary_to_list(iolist_to_binary(IOList)).
|
|
||||||
|
|
||||||
connect() ->
|
|
||||||
Server = ejabberd_config:get_option(redis_server,
|
Server = ejabberd_config:get_option(redis_server,
|
||||||
fun iolist_to_list/1,
|
fun iolist_to_list/1,
|
||||||
"localhost"),
|
"localhost"),
|
||||||
@ -377,36 +356,60 @@ connect() ->
|
|||||||
Pass = ejabberd_config:get_option(redis_password,
|
Pass = ejabberd_config:get_option(redis_password,
|
||||||
fun iolist_to_list/1,
|
fun iolist_to_list/1,
|
||||||
""),
|
""),
|
||||||
ReconnTimeout = timer:seconds(
|
|
||||||
ejabberd_config:get_option(
|
|
||||||
redis_reconnect_timeout,
|
|
||||||
fun(I) when is_integer(I), I>0 -> I end,
|
|
||||||
1)),
|
|
||||||
ConnTimeout = timer:seconds(
|
ConnTimeout = timer:seconds(
|
||||||
ejabberd_config:get_option(
|
ejabberd_config:get_option(
|
||||||
redis_connect_timeout,
|
redis_connect_timeout,
|
||||||
fun(I) when is_integer(I), I>0 -> I end,
|
fun(I) when is_integer(I), I>0 -> I end,
|
||||||
1)),
|
1)),
|
||||||
try case eredis:start_link(Server, Port, DB, Pass,
|
try case eredis:start_link(Server, Port, DB, Pass,
|
||||||
ReconnTimeout, ConnTimeout) of
|
no_reconnect, ConnTimeout) of
|
||||||
{ok, Client} ->
|
{ok, Client} ->
|
||||||
?INFO_MSG("Connected to Redis at ~s:~p", [Server, Port]),
|
?DEBUG("Connection #~p established to Redis at ~s:~p",
|
||||||
unlink(Client),
|
[Num, Server, Port]),
|
||||||
MRef = erlang:monitor(process, Client),
|
register(get_connection(Num), Client),
|
||||||
register(?PROCNAME, Client),
|
{ok, Client};
|
||||||
{ok, {Client, MRef}};
|
|
||||||
{error, Why} ->
|
{error, Why} ->
|
||||||
erlang:error(Why)
|
erlang:error(Why)
|
||||||
end
|
end
|
||||||
catch _:Reason ->
|
catch _:Reason ->
|
||||||
Timeout = 10,
|
Timeout = randoms:uniform(
|
||||||
?ERROR_MSG("Redis connection at ~s:~p has failed: ~p; "
|
min(10, ejabberd_redis_sup:get_pool_size())),
|
||||||
|
?ERROR_MSG("Redis connection #~p at ~s:~p has failed: ~p; "
|
||||||
"reconnecting in ~p seconds",
|
"reconnecting in ~p seconds",
|
||||||
[Server, Port, Reason, Timeout]),
|
[Num, Server, Port, Reason, Timeout]),
|
||||||
erlang:send_after(timer:seconds(Timeout), self(), connect),
|
erlang:send_after(timer:seconds(Timeout), self(), connect),
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec call({atom(), atom()}, {q | qp, list()}, integer()) ->
|
||||||
|
{error, disconnected | timeout | binary()} | {ok, iodata()}.
|
||||||
|
call({Conn, Parent}, {F, Cmd}, Retries) ->
|
||||||
|
Res = try eredis:F(Conn, Cmd, ?CALL_TIMEOUT) of
|
||||||
|
{error, Reason} when is_atom(Reason) ->
|
||||||
|
try exit(whereis(Conn), kill) catch _:_ -> ok end,
|
||||||
|
{error, disconnected};
|
||||||
|
Other ->
|
||||||
|
Other
|
||||||
|
catch exit:{timeout, _} -> {error, timeout};
|
||||||
|
exit:{_, {gen_server, call, _}} -> {error, disconnected}
|
||||||
|
end,
|
||||||
|
case Res of
|
||||||
|
{error, disconnected} when Retries > 0 ->
|
||||||
|
try ?GEN_SERVER:call(Parent, connect, ?CALL_TIMEOUT) of
|
||||||
|
ok -> call({Conn, Parent}, {F, Cmd}, Retries-1);
|
||||||
|
{error, _} = Err -> Err
|
||||||
|
catch exit:{timeout, _} -> {error, timeout};
|
||||||
|
exit:{_, {?GEN_SERVER, call, _}} -> {error, disconnected}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
Res
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_worker() ->
|
||||||
|
Time = p1_time_compat:system_time(),
|
||||||
|
I = erlang:phash2(Time, ejabberd_redis_sup:get_pool_size()) + 1,
|
||||||
|
{get_connection(I), get_proc(I)}.
|
||||||
|
|
||||||
get_result([{error, _} = Err|_]) ->
|
get_result([{error, _} = Err|_]) ->
|
||||||
Err;
|
Err;
|
||||||
get_result([{ok, _} = OK]) ->
|
get_result([{ok, _} = OK]) ->
|
||||||
@ -436,16 +439,51 @@ reply(Val) ->
|
|||||||
_ -> queued
|
_ -> queued
|
||||||
end.
|
end.
|
||||||
|
|
||||||
opt_type(redis_connect_timeout) ->
|
iolist_to_list(IOList) ->
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
binary_to_list(iolist_to_binary(IOList)).
|
||||||
opt_type(redis_db) ->
|
|
||||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
max_fsm_queue() ->
|
||||||
opt_type(redis_password) -> fun iolist_to_list/1;
|
proplists:get_value(max_queue, fsm_limit_opts(), ?DEFAULT_MAX_QUEUE).
|
||||||
opt_type(redis_port) ->
|
|
||||||
fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
|
fsm_limit_opts() ->
|
||||||
opt_type(redis_reconnect_timeout) ->
|
ejabberd_config:fsm_limit_opts([]).
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(redis_server) -> fun iolist_to_list/1;
|
get_queue_type() ->
|
||||||
opt_type(_) ->
|
case ejabberd_config:get_option(
|
||||||
[redis_connect_timeout, redis_db, redis_password,
|
redis_queue_type,
|
||||||
redis_port, redis_reconnect_timeout, redis_server].
|
ejabberd_redis_sup:opt_type(redis_queue_type)) of
|
||||||
|
undefined ->
|
||||||
|
ejabberd_config:default_queue_type(global);
|
||||||
|
Type ->
|
||||||
|
Type
|
||||||
|
end.
|
||||||
|
|
||||||
|
flush_queue(Q) ->
|
||||||
|
CurrTime = p1_time_compat:monotonic_time(milli_seconds),
|
||||||
|
p1_queue:dropwhile(
|
||||||
|
fun({From, Time}) ->
|
||||||
|
if (CurrTime - Time) >= ?CALL_TIMEOUT ->
|
||||||
|
ok;
|
||||||
|
true ->
|
||||||
|
?GEN_SERVER:reply(From, ok)
|
||||||
|
end,
|
||||||
|
true
|
||||||
|
end, Q).
|
||||||
|
|
||||||
|
clean_queue(Q, CurrTime) ->
|
||||||
|
Q1 = p1_queue:dropwhile(
|
||||||
|
fun({_From, Time}) ->
|
||||||
|
(CurrTime - Time) >= ?CALL_TIMEOUT
|
||||||
|
end, Q),
|
||||||
|
Len = p1_queue:len(Q1),
|
||||||
|
Limit = p1_queue:get_limit(Q1),
|
||||||
|
if Len >= Limit ->
|
||||||
|
?ERROR_MSG("Redis request queue is overloaded", []),
|
||||||
|
p1_queue:dropwhile(
|
||||||
|
fun({From, _Time}) ->
|
||||||
|
?GEN_SERVER:reply(From, {error, disconnected}),
|
||||||
|
true
|
||||||
|
end, Q1);
|
||||||
|
true ->
|
||||||
|
Q1
|
||||||
|
end.
|
||||||
|
159
src/ejabberd_redis_sup.erl
Normal file
159
src/ejabberd_redis_sup.erl
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% Created : 6 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%
|
||||||
|
%%%
|
||||||
|
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||||
|
%%%
|
||||||
|
%%% This program is free software; you can redistribute it and/or
|
||||||
|
%%% modify it under the terms of the GNU General Public License as
|
||||||
|
%%% published by the Free Software Foundation; either version 2 of the
|
||||||
|
%%% License, or (at your option) any later version.
|
||||||
|
%%%
|
||||||
|
%%% This program is distributed in the hope that it will be useful,
|
||||||
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
%%% General Public License for more details.
|
||||||
|
%%%
|
||||||
|
%%% You should have received a copy of the GNU General Public License along
|
||||||
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
%%%
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(ejabberd_redis_sup).
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([start_link/0, get_pool_size/0,
|
||||||
|
host_up/1, config_reloaded/0, opt_type/1]).
|
||||||
|
|
||||||
|
%% Supervisor callbacks
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
-define(DEFAULT_POOL_SIZE, 10).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API functions
|
||||||
|
%%%===================================================================
|
||||||
|
start_link() ->
|
||||||
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
|
host_up(Host) ->
|
||||||
|
case is_redis_configured(Host) of
|
||||||
|
true ->
|
||||||
|
ejabberd:start_app(eredis),
|
||||||
|
lists:foreach(
|
||||||
|
fun(Spec) ->
|
||||||
|
supervisor:start_child(?MODULE, Spec)
|
||||||
|
end, get_specs());
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
config_reloaded() ->
|
||||||
|
case is_redis_configured() of
|
||||||
|
true ->
|
||||||
|
ejabberd:start_app(eredis),
|
||||||
|
lists:foreach(
|
||||||
|
fun(Spec) ->
|
||||||
|
supervisor:start_child(?MODULE, Spec)
|
||||||
|
end, get_specs()),
|
||||||
|
PoolSize = get_pool_size(),
|
||||||
|
lists:foreach(
|
||||||
|
fun({Id, _, _, _}) when Id > PoolSize ->
|
||||||
|
supervisor:terminate_child(?MODULE, Id),
|
||||||
|
supervisor:delete_child(?MODULE, Id);
|
||||||
|
(_) ->
|
||||||
|
ok
|
||||||
|
end, supervisor:which_children(?MODULE));
|
||||||
|
false ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Id, _, _, _}) ->
|
||||||
|
supervisor:terminate_child(?MODULE, Id),
|
||||||
|
supervisor:delete_child(?MODULE, Id)
|
||||||
|
end, supervisor:which_children(?MODULE))
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Supervisor callbacks
|
||||||
|
%%%===================================================================
|
||||||
|
init([]) ->
|
||||||
|
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
||||||
|
ejabberd_hooks:add(host_up, ?MODULE, host_up, 20),
|
||||||
|
Specs = case is_redis_configured() of
|
||||||
|
true ->
|
||||||
|
ejabberd:start_app(eredis),
|
||||||
|
get_specs();
|
||||||
|
false ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
{ok, {{one_for_one, 500, 1}, Specs}}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
is_redis_configured() ->
|
||||||
|
lists:any(fun is_redis_configured/1, ?MYHOSTS).
|
||||||
|
|
||||||
|
is_redis_configured(Host) ->
|
||||||
|
ServerConfigured = ejabberd_config:has_option({redis_server, Host}),
|
||||||
|
PortConfigured = ejabberd_config:has_option({redis_port, Host}),
|
||||||
|
DBConfigured = ejabberd_config:has_option({redis_db, Host}),
|
||||||
|
PassConfigured = ejabberd_config:has_option({redis_password, Host}),
|
||||||
|
PoolSize = ejabberd_config:has_option({redis_pool_size, Host}),
|
||||||
|
ConnTimeoutConfigured = ejabberd_config:has_option(
|
||||||
|
{redis_connect_timeout, Host}),
|
||||||
|
Modules = ejabberd_config:get_option(
|
||||||
|
{modules, Host},
|
||||||
|
fun(L) when is_list(L) -> L end, []),
|
||||||
|
SMConfigured = ejabberd_config:get_option(
|
||||||
|
{sm_db_type, Host},
|
||||||
|
fun(V) -> V end) == redis,
|
||||||
|
RouterConfigured = ejabberd_config:get_option(
|
||||||
|
{router_db_type, Host},
|
||||||
|
fun(V) -> V end) == redis,
|
||||||
|
ModuleWithRedisDBConfigured =
|
||||||
|
lists:any(
|
||||||
|
fun({Module, Opts}) ->
|
||||||
|
gen_mod:db_type(Host, Opts, Module) == redis
|
||||||
|
end, Modules),
|
||||||
|
ServerConfigured or PortConfigured or DBConfigured or PassConfigured or
|
||||||
|
PoolSize or ConnTimeoutConfigured or
|
||||||
|
SMConfigured or RouterConfigured or ModuleWithRedisDBConfigured.
|
||||||
|
|
||||||
|
get_specs() ->
|
||||||
|
lists:map(
|
||||||
|
fun(I) ->
|
||||||
|
{I, {ejabberd_redis, start_link, [I]},
|
||||||
|
transient, 2000, worker, [?MODULE]}
|
||||||
|
end, lists:seq(1, get_pool_size())).
|
||||||
|
|
||||||
|
get_pool_size() ->
|
||||||
|
ejabberd_config:get_option(
|
||||||
|
redis_pool_size,
|
||||||
|
fun(N) when is_integer(N), N >= 1 -> N end,
|
||||||
|
?DEFAULT_POOL_SIZE).
|
||||||
|
|
||||||
|
iolist_to_list(IOList) ->
|
||||||
|
binary_to_list(iolist_to_binary(IOList)).
|
||||||
|
|
||||||
|
opt_type(redis_connect_timeout) ->
|
||||||
|
fun (I) when is_integer(I), I > 0 -> I end;
|
||||||
|
opt_type(redis_db) ->
|
||||||
|
fun (I) when is_integer(I), I >= 0 -> I end;
|
||||||
|
opt_type(redis_password) -> fun iolist_to_list/1;
|
||||||
|
opt_type(redis_port) ->
|
||||||
|
fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
|
||||||
|
opt_type(redis_server) -> fun iolist_to_list/1;
|
||||||
|
opt_type(redis_pool_size) ->
|
||||||
|
fun (I) when is_integer(I), I > 0 -> I end;
|
||||||
|
opt_type(redis_queue_type) ->
|
||||||
|
fun(ram) -> ram; (file) -> file end;
|
||||||
|
opt_type(_) ->
|
||||||
|
[redis_connect_timeout, redis_db, redis_password,
|
||||||
|
redis_port, redis_pool_size, redis_server,
|
||||||
|
redis_pool_size, redis_queue_type].
|
@ -115,8 +115,9 @@ init([]) ->
|
|||||||
RiakSupervisor = {ejabberd_riak_sup,
|
RiakSupervisor = {ejabberd_riak_sup,
|
||||||
{ejabberd_riak_sup, start_link, []},
|
{ejabberd_riak_sup, start_link, []},
|
||||||
permanent, infinity, supervisor, [ejabberd_riak_sup]},
|
permanent, infinity, supervisor, [ejabberd_riak_sup]},
|
||||||
Redis = {ejabberd_redis, {ejabberd_redis, start_link, []},
|
RedisSupervisor = {ejabberd_redis_sup,
|
||||||
permanent, 5000, worker, [ejabberd_redis]},
|
{ejabberd_redis_sup, start_link, []},
|
||||||
|
permanent, infinity, supervisor, [ejabberd_redis_sup]},
|
||||||
Router = {ejabberd_router, {ejabberd_router, start_link, []},
|
Router = {ejabberd_router, {ejabberd_router, start_link, []},
|
||||||
permanent, 5000, worker, [ejabberd_router]},
|
permanent, 5000, worker, [ejabberd_router]},
|
||||||
RouterMulticast = {ejabberd_router_multicast,
|
RouterMulticast = {ejabberd_router_multicast,
|
||||||
@ -168,7 +169,7 @@ init([]) ->
|
|||||||
BackendSupervisor,
|
BackendSupervisor,
|
||||||
SQLSupervisor,
|
SQLSupervisor,
|
||||||
RiakSupervisor,
|
RiakSupervisor,
|
||||||
Redis,
|
RedisSupervisor,
|
||||||
Router,
|
Router,
|
||||||
RouterMulticast,
|
RouterMulticast,
|
||||||
Local,
|
Local,
|
||||||
|
Loading…
Reference in New Issue
Block a user