25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

Implement cache for mod_vcard and mod_vcard_xupdate

This commit is contained in:
Evgeniy Khramtsov 2017-05-17 17:13:34 +03:00
parent fc794b680a
commit 1925b94131
10 changed files with 174 additions and 311 deletions

View File

@ -48,11 +48,12 @@
-include("mod_vcard.hrl").
-define(JUD_MATCHES, 30).
-define(VCARD_CACHE, vcard_cache).
-callback init(binary(), gen_mod:opts()) -> any().
-callback stop(binary()) -> any().
-callback import(binary(), binary(), [binary()]) -> ok.
-callback get_vcard(binary(), binary()) -> [xmlel()] | error.
-callback get_vcard(binary(), binary()) -> {ok, [xmlel()]} | error.
-callback set_vcard(binary(), binary(),
xmlel(), #vcard_search{}) -> {atomic, any()}.
-callback search_fields(binary()) -> [{binary(), binary()}].
@ -61,6 +62,10 @@
infinity | pos_integer()) -> [{binary(), binary()}].
-callback remove_user(binary(), binary()) -> {atomic, any()}.
-callback is_search_supported(binary()) -> boolean().
-callback use_cache(binary()) -> boolean().
-callback cache_nodes(binary()) -> [node()].
-optional_callbacks([use_cache/1, cache_nodes/1]).
-record(state, {host :: binary(), server_host :: binary()}).
@ -80,6 +85,7 @@ init([Host, Opts]) ->
process_flag(trap_exit, true),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
ejabberd_hooks:add(remove_user, Host, ?MODULE,
remove_user, 50),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
@ -288,7 +294,18 @@ disco_identity(Acc, _From, _To, _Node, _Lang) ->
-spec get_vcard(binary(), binary()) -> [xmlel()] | error.
get_vcard(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:get_vcard(LUser, LServer).
Result = case use_cache(Mod, LServer) of
true ->
ets_cache:lookup(
?VCARD_CACHE, {LUser, LServer},
fun() -> Mod:get_vcard(LUser, LServer) end);
false ->
Mod:get_vcard(LUser, LServer)
end,
case Result of
{ok, Els} -> Els;
error -> error
end.
-spec make_vcard_search(binary(), binary(), binary(), xmlel()) -> #vcard_search{}.
make_vcard_search(User, LUser, LServer, VCARD) ->
@ -366,6 +383,8 @@ set_vcard(User, LServer, VCARD) ->
VCardSearch = make_vcard_search(User, LUser, LServer, VCARD),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:set_vcard(LUser, LServer, VCARD, VCardSearch),
ets_cache:delete(?VCARD_CACHE, {LUser, LServer},
cache_nodes(Mod, LServer)),
ejabberd_hooks:run(vcard_set, LServer,
[LUser, LServer, VCARD])
end.
@ -435,12 +454,56 @@ search(LServer, XFields) ->
Mod:search(LServer, Data, AllowReturnAll, MaxMatch).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec remove_user(binary(), binary()) -> any().
-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:remove_user(LUser, LServer).
Mod:remove_user(LUser, LServer),
ets_cache:delete(?VCARD_CACHE, {LUser, LServer}, cache_nodes(Mod, LServer)).
-spec init_cache(module(), binary(), gen_mod:opts()) -> ok.
init_cache(Mod, Host, Opts) ->
case use_cache(Mod, Host) of
true ->
CacheOpts = cache_opts(Host, Opts),
ets_cache:new(?VCARD_CACHE, CacheOpts);
false ->
ets_cache:delete(?VCARD_CACHE)
end.
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
cache_opts(Host, Opts) ->
MaxSize = gen_mod:get_opt(
cache_size, Opts,
ejabberd_config:cache_size(Host)),
CacheMissed = gen_mod:get_opt(
cache_missed, Opts,
ejabberd_config:cache_missed(Host)),
LifeTime = case gen_mod:get_opt(
cache_life_time, Opts,
ejabberd_config:cache_life_time(Host)) of
infinity -> infinity;
I -> timer:seconds(I)
end,
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
-spec use_cache(module(), binary()) -> boolean().
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 1) of
true -> Mod:use_cache(Host);
false ->
gen_mod:get_module_opt(
Host, ?MODULE, use_cache,
ejabberd_config:use_cache(Host))
end.
-spec cache_nodes(module(), binary()) -> [node()].
cache_nodes(Mod, Host) ->
case erlang:function_exported(Mod, cache_nodes, 1) of
true -> Mod:cache_nodes(Host);
false -> ejabberd_cluster:get_nodes()
end.
import_info() ->
[{<<"vcard">>, 3}, {<<"vcard_search">>, 24}].
@ -473,6 +536,13 @@ mod_opt_type(search) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(search_all_hosts) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(O) when O == cache_life_time; O == cache_size ->
fun (I) when is_integer(I), I > 0 -> I;
(infinity) -> infinity
end;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
[allow_return_all, db_type, host, iqdisc, matches,
search, search_all_hosts].
search, search_all_hosts, cache_life_time, cache_size,
use_cache, cache_missed].

View File

@ -97,9 +97,9 @@ get_vcard(LUser, LServer) ->
#eldap_entry{attributes = Attributes} ->
VCard = ldap_attributes_to_vcard(Attributes, VCardMap,
{LUser, LServer}),
[xmpp:encode(VCard)];
{ok, [xmpp:encode(VCard)]};
_ ->
[]
{ok, []}
end.
set_vcard(_LUser, _LServer, _VCard, _VCardSearch) ->

View File

@ -65,7 +65,7 @@ get_vcard(LUser, LServer) ->
F = fun () -> mnesia:read({vcard, US}) end,
case mnesia:transaction(F) of
{atomic, Rs} ->
lists:map(fun (R) -> R#vcard.vcard end, Rs);
{ok, lists:map(fun (R) -> R#vcard.vcard end, Rs)};
{aborted, _Reason} -> error
end.

View File

@ -49,9 +49,9 @@ is_search_supported(_LServer) ->
get_vcard(LUser, LServer) ->
case ejabberd_riak:get(vcard, vcard_schema(), {LUser, LServer}) of
{ok, R} ->
[R#vcard.vcard];
{ok, [R#vcard.vcard]};
{error, notfound} ->
[];
{ok, []};
_ ->
error
end.

View File

@ -55,9 +55,9 @@ get_vcard(LUser, LServer) ->
{selected, [{SVCARD}]} ->
case fxml_stream:parse_element(SVCARD) of
{error, _Reason} -> error;
VCARD -> [VCARD]
VCARD -> {ok, [VCARD]}
end;
{selected, []} -> [];
{selected, []} -> {ok, []};
_ -> error
end.

View File

@ -30,52 +30,39 @@
%% gen_mod callbacks
-export([start/2, stop/1, reload/3]).
-export([update_presence/1, vcard_set/3, export/1,
import_info/0, import/5, import_start/2,
-export([update_presence/1, vcard_set/3, remove_user/2,
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("xmpp.hrl").
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), binary(), [binary()]) -> ok.
-callback add_xupdate(binary(), binary(), binary()) -> {atomic, any()}.
-callback get_xupdate(binary(), binary()) -> binary() | undefined.
-callback remove_xupdate(binary(), binary()) -> {atomic, any()}.
-define(VCARD_XUPDATE_CACHE, vcard_xupdate_cache).
%%====================================================================
%% gen_mod callbacks
%%====================================================================
start(Host, Opts) ->
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Host, Opts),
ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE,
update_presence, 100),
ejabberd_hooks:add(vcard_set, Host, ?MODULE, vcard_set,
100),
ok.
ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50).
stop(Host) ->
ejabberd_hooks:delete(c2s_self_presence, Host,
?MODULE, update_presence, 100),
ejabberd_hooks:delete(vcard_set, Host, ?MODULE,
vcard_set, 100),
ok.
ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50).
reload(Host, NewOpts, OldOpts) ->
NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
ok
end,
ok.
reload(Host, NewOpts, _OldOpts) ->
init_cache(Host, NewOpts).
depends(_Host, _Opts) ->
[].
[{mod_vcard, hard}].
%%====================================================================
%% Hooks
@ -91,51 +78,95 @@ update_presence(Acc) ->
Acc.
-spec vcard_set(binary(), binary(), xmlel()) -> ok.
vcard_set(LUser, LServer, VCARD) ->
US = {LUser, LServer},
case fxml:get_path_s(VCARD,
[{elem, <<"PHOTO">>}, {elem, <<"BINVAL">>}, cdata])
of
<<>> -> remove_xupdate(LUser, LServer);
BinVal ->
add_xupdate(LUser, LServer,
str:sha(misc:decode_base64(BinVal)))
end,
ejabberd_sm:force_update_presence(US).
vcard_set(LUser, LServer, _VCARD) ->
ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}),
ejabberd_sm:force_update_presence({LUser, LServer}).
-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}).
%%====================================================================
%% Storage
%%====================================================================
add_xupdate(LUser, LServer, Hash) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:add_xupdate(LUser, LServer, Hash).
-spec get_xupdate(binary(), binary()) -> binary() | undefined.
get_xupdate(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:get_xupdate(LUser, LServer).
Result = case use_cache(LServer) of
true ->
ets_cache:lookup(
?VCARD_XUPDATE_CACHE, {LUser, LServer},
fun() -> db_get_xupdate(LUser, LServer) end);
false ->
db_get_xupdate(LUser, LServer)
end,
case Result of
{ok, Hash} -> Hash;
error -> undefined
end.
remove_xupdate(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:remove_xupdate(LUser, LServer).
-spec db_get_xupdate(binary(), binary()) -> {ok, binary()} | error.
db_get_xupdate(LUser, LServer) ->
case mod_vcard:get_vcard(LUser, LServer) of
[VCard] ->
{ok, compute_hash(VCard)};
_ ->
error
end.
import_info() ->
[{<<"vcard_xupdate">>, 3}].
-spec init_cache(binary(), gen_mod:opts()) -> ok.
init_cache(Host, Opts) ->
case use_cache(Host) of
true ->
CacheOpts = cache_opts(Host, Opts),
ets_cache:new(?VCARD_XUPDATE_CACHE, CacheOpts);
false ->
ets_cache:delete(?VCARD_XUPDATE_CACHE)
end.
import_start(LServer, DBType) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:init(LServer, []).
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
cache_opts(Host, Opts) ->
MaxSize = gen_mod:get_opt(
cache_size, Opts,
ejabberd_config:cache_size(Host)),
CacheMissed = gen_mod:get_opt(
cache_missed, Opts,
ejabberd_config:cache_missed(Host)),
LifeTime = case gen_mod:get_opt(
cache_life_time, Opts,
ejabberd_config:cache_life_time(Host)) of
infinity -> infinity;
I -> timer:seconds(I)
end,
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
import(LServer, {sql, _}, DBType, Tab, [LUser, Hash, TimeStamp]) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, [LUser, Hash, TimeStamp]).
-spec use_cache(binary()) -> boolean().
use_cache(Host) ->
gen_mod:get_module_opt(
Host, ?MODULE, use_cache,
ejabberd_config:use_cache(Host)).
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
-spec compute_hash(xmlel()) -> binary().
compute_hash(VCard) ->
case fxml:get_path_s(VCard,
[{elem, <<"PHOTO">>},
{elem, <<"BINVAL">>},
cdata]) of
<<>> ->
<<>>;
BinVal ->
str:sha(misc:decode_base64(BinVal))
end.
%%====================================================================
%% Options
%%====================================================================
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) -> [db_type].
mod_opt_type(O) when O == cache_life_time; O == cache_size ->
fun (I) when is_integer(I), I > 0 -> I;
(infinity) -> infinity
end;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
[cache_life_time, cache_size, use_cache, cache_missed].

View File

@ -1,82 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : mod_vcard_xupdate_mnesia.erl
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 13 Apr 2016 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(mod_vcard_xupdate_mnesia).
-behaviour(mod_vcard_xupdate).
%% API
-export([init/2, import/3, add_xupdate/3, get_xupdate/2, remove_xupdate/2]).
-export([need_transform/1, transform/1]).
-include("mod_vcard_xupdate.hrl").
-include("logger.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
ejabberd_mnesia:create(?MODULE, vcard_xupdate,
[{disc_copies, [node()]},
{attributes,
record_info(fields, vcard_xupdate)}]).
add_xupdate(LUser, LServer, Hash) ->
F = fun () ->
mnesia:write(#vcard_xupdate{us = {LUser, LServer},
hash = Hash})
end,
mnesia:transaction(F).
get_xupdate(LUser, LServer) ->
case mnesia:dirty_read(vcard_xupdate, {LUser, LServer})
of
[#vcard_xupdate{hash = Hash}] -> Hash;
_ -> undefined
end.
remove_xupdate(LUser, LServer) ->
F = fun () ->
mnesia:delete({vcard_xupdate, {LUser, LServer}})
end,
mnesia:transaction(F).
import(LServer, <<"vcard_xupdate">>, [LUser, Hash, _TimeStamp]) ->
mnesia:dirty_write(
#vcard_xupdate{us = {LUser, LServer}, hash = Hash}).
need_transform(#vcard_xupdate{us = {U, S}, hash = Hash})
when is_list(U) orelse is_list(S) orelse is_list(Hash) ->
?INFO_MSG("Mnesia table 'vcard_xupdate' will be converted to binary", []),
true;
need_transform(_) ->
false.
transform(#vcard_xupdate{us = {U, S}, hash = Hash} = R) ->
R#vcard_xupdate{us = {iolist_to_binary(U), iolist_to_binary(S)},
hash = iolist_to_binary(Hash)}.
%%%===================================================================
%%% Internal functions
%%%===================================================================

View File

@ -1,64 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : mod_vcard_xupdate_riak.erl
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 13 Apr 2016 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(mod_vcard_xupdate_riak).
-behaviour(mod_vcard_xupdate).
%% API
-export([init/2, import/3, add_xupdate/3, get_xupdate/2, remove_xupdate/2]).
-include("mod_vcard_xupdate.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
ok.
add_xupdate(LUser, LServer, Hash) ->
{atomic, ejabberd_riak:put(#vcard_xupdate{us = {LUser, LServer},
hash = Hash},
vcard_xupdate_schema())}.
get_xupdate(LUser, LServer) ->
case ejabberd_riak:get(vcard_xupdate, vcard_xupdate_schema(),
{LUser, LServer}) of
{ok, #vcard_xupdate{hash = Hash}} -> Hash;
_ -> undefined
end.
remove_xupdate(LUser, LServer) ->
{atomic, ejabberd_riak:delete(vcard_xupdate, {LUser, LServer})}.
import(LServer, <<"vcard_xupdate">>, [LUser, Hash, _TimeStamp]) ->
ejabberd_riak:put(
#vcard_xupdate{us = {LUser, LServer}, hash = Hash},
vcard_xupdate_schema()).
%%%===================================================================
%%% Internal functions
%%%===================================================================
vcard_xupdate_schema() ->
{record_info(fields, vcard_xupdate), #vcard_xupdate{}}.

View File

@ -1,86 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : mod_vcard_xupdate_sql.erl
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 13 Apr 2016 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(mod_vcard_xupdate_sql).
-compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_vcard_xupdate).
%% API
-export([init/2, import/3, add_xupdate/3, get_xupdate/2, remove_xupdate/2,
export/1]).
-include("mod_vcard_xupdate.hrl").
-include("ejabberd_sql_pt.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
ok.
add_xupdate(LUser, LServer, Hash) ->
F = fun () ->
?SQL_UPSERT_T(
"vcard_xupdate",
["!username=%(LUser)s",
"hash=%(Hash)s"])
end,
ejabberd_sql:sql_transaction(LServer, F).
get_xupdate(LUser, LServer) ->
case ejabberd_sql:sql_query(
LServer,
?SQL("select @(hash)s from vcard_xupdate where"
" username=%(LUser)s"))
of
{selected, [{Hash}]} -> Hash;
_ -> undefined
end.
remove_xupdate(LUser, LServer) ->
F = fun () ->
ejabberd_sql:sql_query_t(
?SQL("delete from vcard_xupdate where username=%(LUser)s"))
end,
ejabberd_sql:sql_transaction(LServer, F).
export(_Server) ->
[{vcard_xupdate,
fun(Host, #vcard_xupdate{us = {LUser, LServer}, hash = Hash})
when LServer == Host ->
[?SQL("delete from vcard_xupdate where username=%(LUser)s;"),
?SQL("insert into vcard_xupdate(username, hash) values ("
"%(LUser)s, %(Hash)s);")];
(_Host, _R) ->
[]
end}].
import(_, _, _) ->
ok.
%%%===================================================================
%%% Internal functions
%%%===================================================================

View File

@ -44,8 +44,7 @@ host_config:
db_type: sql
mod_vcard:
db_type: sql
mod_vcard_xupdate:
db_type: sql
mod_vcard_xupdate: []
mod_carboncopy:
ram_db_type: sql
mod_adhoc: []
@ -103,8 +102,7 @@ Welcome to this XMPP server."
db_type: sql
mod_vcard:
db_type: sql
mod_vcard_xupdate:
db_type: sql
mod_vcard_xupdate: []
mod_carboncopy:
ram_db_type: sql
mod_adhoc: []
@ -167,8 +165,7 @@ Welcome to this XMPP server."
db_type: sql
mod_vcard:
db_type: sql
mod_vcard_xupdate:
db_type: sql
mod_vcard_xupdate: []
mod_carboncopy:
ram_db_type: sql
mod_adhoc: []
@ -222,8 +219,7 @@ Welcome to this XMPP server."
db_type: internal
mod_vcard:
db_type: internal
mod_vcard_xupdate:
db_type: internal
mod_vcard_xupdate: []
mod_carboncopy:
ram_db_type: internal
mod_client_state:
@ -282,8 +278,7 @@ Welcome to this XMPP server."
db_type: internal
mod_vcard:
db_type: internal
mod_vcard_xupdate:
db_type: internal
mod_vcard_xupdate: []
mod_carboncopy:
ram_db_type: redis
mod_client_state:
@ -332,8 +327,7 @@ Welcome to this XMPP server."
db_type: riak
mod_vcard:
db_type: riak
mod_vcard_xupdate:
db_type: riak
mod_vcard_xupdate: []
mod_carboncopy:
ram_db_type: riak
mod_adhoc: []