From cba6e1b3abbb76addfafedabcdecae9a447549b2 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Tue, 28 Mar 2017 19:34:04 +0300 Subject: [PATCH] Add Redis as router RAM backend --- src/ejabberd_redis.erl | 4 +- src/ejabberd_router.erl | 7 ++ src/ejabberd_router_mnesia.erl | 6 +- src/ejabberd_router_redis.erl | 154 +++++++++++++++++++++++++++++++++ src/ejabberd_router_sql.erl | 18 +++- 5 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 src/ejabberd_router_redis.erl diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index f349baac5..1d8f32c28 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -72,8 +72,8 @@ config_reloaded() -> init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20), process_flag(trap_exit, true), - self() ! connect, - {ok, #state{}}. + {_, State} = handle_info(connect, #state{}), + {ok, State}. handle_call(_Request, _From, State) -> Reply = ok, diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index dca3ac25d..7591822e3 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -49,6 +49,7 @@ get_all_routes/0, is_my_route/1, is_my_host/1, + find_routes/0, get_backend/0]). -export([start_link/0]). @@ -70,6 +71,7 @@ undefined | pos_integer(), pid()) -> ok | {error, term()}. -callback unregister_route(binary(), undefined | pos_integer(), pid()) -> ok | {error, term()}. -callback find_routes(binary()) -> [#route{}]. +-callback find_routes() -> [#route{}]. -callback host_of_route(binary()) -> {ok, binary()} | error. -callback is_my_route(binary()) -> boolean(). -callback is_my_host(binary()) -> boolean(). @@ -202,6 +204,11 @@ get_all_routes() -> Mod = get_backend(), Mod:get_all_routes(). +-spec find_routes() -> [#route{}]. +find_routes() -> + Mod = get_backend(), + Mod:find_routes(). + -spec host_of_route(binary()) -> binary(). host_of_route(Domain) -> case jid:nameprep(Domain) of diff --git a/src/ejabberd_router_mnesia.erl b/src/ejabberd_router_mnesia.erl index 15cdf64c0..e3b550a75 100644 --- a/src/ejabberd_router_mnesia.erl +++ b/src/ejabberd_router_mnesia.erl @@ -25,7 +25,8 @@ %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, - host_of_route/1, is_my_route/1, is_my_host/1, get_all_routes/0]). + host_of_route/1, is_my_route/1, is_my_host/1, get_all_routes/0, + find_routes/0]). %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2, code_change/3, start_link/0]). @@ -152,6 +153,9 @@ get_all_routes() -> when Domain /= ServerHost -> Domain end)). +find_routes() -> + ets:tab2list(route). + %%%=================================================================== %%% gen_server callbacks %%%=================================================================== diff --git a/src/ejabberd_router_redis.erl b/src/ejabberd_router_redis.erl new file mode 100644 index 000000000..be8d166b9 --- /dev/null +++ b/src/ejabberd_router_redis.erl @@ -0,0 +1,154 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% Created : 28 Mar 2017 by Evgeny Khramtsov +%%% +%%% +%%% 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_router_redis). +-behaviour(ejabberd_router). + +%% API +-export([init/0, register_route/5, unregister_route/3, find_routes/1, + host_of_route/1, is_my_route/1, is_my_host/1, get_all_routes/0, + find_routes/0]). + +-include("ejabberd.hrl"). +-include("logger.hrl"). +-include("ejabberd_router.hrl"). + +-define(ROUTES_KEY, "ejabberd:routes"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init() -> + clean_table(). + +register_route(Domain, ServerHost, LocalHint, _, Pid) -> + DomKey = domain_key(Domain), + PidKey = term_to_binary(Pid), + T = term_to_binary({ServerHost, LocalHint}), + case ejabberd_redis:qp([["HSET", DomKey, PidKey, T], + ["SADD", ?ROUTES_KEY, Domain]]) of + [{ok, _}, {ok, _}] -> + ok; + Err -> + ?ERROR_MSG("failed to register route in redis: ~p", [Err]), + Err + end. + +unregister_route(Domain, _, Pid) -> + DomKey = domain_key(Domain), + PidKey = term_to_binary(Pid), + try + {ok, _} = ejabberd_redis:q(["HDEL", DomKey, PidKey]), + {ok, Num} = ejabberd_redis:q(["HLEN", DomKey]), + case binary_to_integer(Num) of + 0 -> + {ok, _} = ejabberd_redis:q(["SREM", ?ROUTES_KEY, Domain]), + ok; + _ -> + ok + end + catch _:{badmatch, Err} -> + ?ERROR_MSG("failed to unregister route in redis: ~p", [Err]), + Err + end. + +find_routes(Domain) -> + DomKey = domain_key(Domain), + case ejabberd_redis:q(["HGETALL", DomKey]) of + {ok, Vals} -> + decode_routes(Domain, Vals); + Err -> + ?ERROR_MSG("failed to find routes in redis: ~p", [Err]), + [] + end. + +host_of_route(Domain) -> + DomKey = domain_key(Domain), + case ejabberd_redis:q(["HGETALL", DomKey]) of + {ok, [_, Data|_]} -> + {ServerHost, _} = binary_to_term(Data), + {ok, ServerHost}; + {ok, []} -> + error; + Err -> + ?ERROR_MSG("failed to get host of route in redis: ~p", [Err]), + error + end. + +is_my_route(Domain) -> + case ejabberd_redis:q(["SISMEMBER", ?ROUTES_KEY, Domain]) of + {ok, <<"1">>} -> true; + {ok, _} -> false; + Err -> + ?ERROR_MSG("failed to check route in redis: ~p", [Err]), + false + end. + +is_my_host(Domain) -> + {ok, Domain} == host_of_route(Domain). + +get_all_routes() -> + case ejabberd_redis:q(["SMEMBERS", ?ROUTES_KEY]) of + {ok, Routes} -> + Routes; + Err -> + ?ERROR_MSG("failed to fetch routes from redis: ~p", [Err]), + [] + end. + +find_routes() -> + lists:flatmap( + fun(Domain) -> + DomKey = domain_key(Domain), + case ejabberd_redis:q(["HGETALL", DomKey]) of + {ok, Vals} -> + decode_routes(Domain, Vals); + Err -> + ?ERROR_MSG("failed to fetch routes from redis: ~p", + [Err]), + [] + end + end, get_all_routes()). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +clean_table() -> + lists:foreach( + fun(#route{domain = Domain, pid = Pid}) when node(Pid) == node() -> + unregister_route(Domain, undefined, Pid); + (_) -> + ok + end, find_routes()). + +domain_key(Domain) -> + <<"ejabberd:route:", Domain/binary>>. + +decode_routes(Domain, [Pid, Data|Vals]) -> + {ServerHost, LocalHint} = binary_to_term(Data), + [#route{domain = Domain, + pid = binary_to_term(Pid), + server_host = ServerHost, + local_hint = LocalHint}| + decode_routes(Domain, Vals)]; +decode_routes(_, []) -> + []. diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl index 1daa92fb1..e64b6afeb 100644 --- a/src/ejabberd_router_sql.erl +++ b/src/ejabberd_router_sql.erl @@ -27,7 +27,8 @@ %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, - host_of_route/1, is_my_route/1, is_my_host/1, get_all_routes/0]). + host_of_route/1, is_my_route/1, is_my_host/1, get_all_routes/0, + find_routes/0]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -124,6 +125,21 @@ get_all_routes() -> [] end. +find_routes() -> + case ejabberd_sql:sql_query( + ?MYNAME, + ?SQL("select @(domain)s, @(server_host)s, @(node)s, @(pid)s, " + "@(local_hint)s from route")) of + {selected, Rows} -> + lists:flatmap( + fun({Domain, ServerHost, Node, Pid, LocalHint}) -> + row_to_route(Domain, {ServerHost, Node, Pid, LocalHint}) + end, Rows); + Err -> + ?ERROR_MSG("failed to select from 'route' table: ~p", [Err]), + [] + end. + %%%=================================================================== %%% Internal functions %%%===================================================================