mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
Speedup configuration options lookup
We now avoid excessive ETS lookups; instead, we use dynamically compiled module 'ejabberd_options' keeping the configuration options
This commit is contained in:
parent
3b14b35252
commit
78dba217bf
@ -38,7 +38,8 @@
|
|||||||
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
|
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
|
||||||
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
|
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
|
||||||
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
|
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
|
||||||
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1]).
|
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
|
||||||
|
dump/0]).
|
||||||
|
|
||||||
-export([start/2]).
|
-export([start/2]).
|
||||||
|
|
||||||
@ -773,6 +774,7 @@ set_opts(State) ->
|
|||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, _} ->
|
{atomic, _} ->
|
||||||
|
recompile_options(),
|
||||||
set_log_level();
|
set_log_level();
|
||||||
{aborted,{no_exists,Table}} ->
|
{aborted,{no_exists,Table}} ->
|
||||||
MnesiaDirectory = mnesia:system_info(directory),
|
MnesiaDirectory = mnesia:system_info(directory),
|
||||||
@ -788,7 +790,7 @@ set_opts(State) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
set_log_level() ->
|
set_log_level() ->
|
||||||
Level = ejabberd_config:get_option(
|
Level = get_option(
|
||||||
loglevel,
|
loglevel,
|
||||||
fun(P) when P>=0, P=<5 -> P end,
|
fun(P) when P>=0, P=<5 -> P end,
|
||||||
4),
|
4),
|
||||||
@ -806,7 +808,8 @@ add_option(Opt, Val) ->
|
|||||||
mnesia:transaction(fun() ->
|
mnesia:transaction(fun() ->
|
||||||
mnesia:write(#local_config{key = Opt,
|
mnesia:write(#local_config{key = Opt,
|
||||||
value = Val})
|
value = Val})
|
||||||
end).
|
end),
|
||||||
|
recompile_options().
|
||||||
|
|
||||||
-spec prepare_opt_val(any(), any(), check_fun(), any()) -> any().
|
-spec prepare_opt_val(any(), any(), check_fun(), any()) -> any().
|
||||||
|
|
||||||
@ -871,22 +874,23 @@ get_option(Opt, F) ->
|
|||||||
get_option(Opt, F, Default) when is_atom(Opt) ->
|
get_option(Opt, F, Default) when is_atom(Opt) ->
|
||||||
get_option({Opt, global}, F, Default);
|
get_option({Opt, global}, F, Default);
|
||||||
get_option(Opt, F, Default) ->
|
get_option(Opt, F, Default) ->
|
||||||
case Opt of
|
{Key, Host} = case Opt of
|
||||||
{O, global} when is_atom(O) -> ok;
|
{O, global} when is_atom(O) -> Opt;
|
||||||
{O, H} when is_atom(O), is_binary(H) -> ok;
|
{O, H} when is_atom(O), is_binary(H) -> Opt;
|
||||||
_ -> ?WARNING_MSG("Option ~p has invalid (outdated?) format. "
|
_ ->
|
||||||
"This is likely a bug", [Opt])
|
?WARNING_MSG("Option ~p has invalid (outdated?) "
|
||||||
end,
|
"format. This is likely a bug", [Opt]),
|
||||||
case ets:lookup(local_config, Opt) of
|
{undefined, global}
|
||||||
[#local_config{value = Val}] ->
|
end,
|
||||||
prepare_opt_val(Opt, Val, F, Default);
|
case ejabberd_options:is_known(Key) of
|
||||||
_ ->
|
true ->
|
||||||
case Opt of
|
try ejabberd_options:Key(Host) of
|
||||||
{Key, Host} when Host /= global ->
|
Val -> prepare_opt_val(Opt, Val, F, Default)
|
||||||
get_option({Key, global}, F, Default);
|
catch _:function_clause ->
|
||||||
_ ->
|
Default
|
||||||
Default
|
end;
|
||||||
end
|
false ->
|
||||||
|
Default
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec has_option(atom() | {atom(), global | binary()}) -> any().
|
-spec has_option(atom() | {atom(), global | binary()}) -> any().
|
||||||
@ -1481,7 +1485,7 @@ opt_type(_) ->
|
|||||||
|
|
||||||
-spec may_hide_data(any()) -> any().
|
-spec may_hide_data(any()) -> any().
|
||||||
may_hide_data(Data) ->
|
may_hide_data(Data) ->
|
||||||
case ejabberd_config:get_option(
|
case get_option(
|
||||||
hide_sensitive_log_data,
|
hide_sensitive_log_data,
|
||||||
fun(false) -> false;
|
fun(false) -> false;
|
||||||
(true) -> true
|
(true) -> true
|
||||||
@ -1531,3 +1535,79 @@ cache_missed(Host) ->
|
|||||||
%% NOTE: the integer value returned is in *seconds*
|
%% NOTE: the integer value returned is in *seconds*
|
||||||
cache_life_time(Host) ->
|
cache_life_time(Host) ->
|
||||||
get_option({cache_life_time, Host}, opt_type(cache_life_time), 3600).
|
get_option({cache_life_time, Host}, opt_type(cache_life_time), 3600).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Dynamic config compilation
|
||||||
|
%%%===================================================================
|
||||||
|
-spec recompile_options() -> ok.
|
||||||
|
recompile_options() ->
|
||||||
|
Exprs = get_exprs(),
|
||||||
|
try compile_exprs(Exprs)
|
||||||
|
catch E:R ->
|
||||||
|
?CRITICAL_MSG("Failed to compile ejabberd_options:~n~s",
|
||||||
|
[string:join(Exprs, io_lib:nl())]),
|
||||||
|
erlang:raise(E, R, erlang:get_stacktrace())
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec get_exprs() -> [string()].
|
||||||
|
get_exprs() ->
|
||||||
|
Opts = lists:foldl(
|
||||||
|
fun(#local_config{key = {Opt, Host}, value = Val}, D) ->
|
||||||
|
Hosts = maps:get(Opt, D, #{}),
|
||||||
|
maps:put(Opt, maps:put(Host, Val, Hosts), D)
|
||||||
|
end, #{}, ets:tab2list(local_config)),
|
||||||
|
Funs = maps:fold(
|
||||||
|
fun(Opt, Vals, Acc) ->
|
||||||
|
HostVals = lists:reverse(lists:keysort(1, maps:to_list(Vals))),
|
||||||
|
[string:join(
|
||||||
|
lists:map(
|
||||||
|
fun({global, Val}) ->
|
||||||
|
io_lib:format("'~s'(_) -> ~p", [Opt, Val]);
|
||||||
|
({Host, Val}) ->
|
||||||
|
io_lib:format("'~s'(~p) -> ~p", [Opt, Host, Val])
|
||||||
|
end, HostVals),
|
||||||
|
";" ++ io_lib:nl()) ++ "."|Acc]
|
||||||
|
end, [], Opts),
|
||||||
|
Module = "-module(ejabberd_options).",
|
||||||
|
Export = "-compile(export_all).",
|
||||||
|
Knowns = maps:fold(
|
||||||
|
fun(Opt, _, Acc) ->
|
||||||
|
io_lib:format("is_known('~s') -> true;~n", [Opt]) ++ Acc
|
||||||
|
end, "", Opts) ++ "is_known(_) -> false.",
|
||||||
|
[Module, Export, Knowns|Funs].
|
||||||
|
|
||||||
|
-spec compile_exprs([string()]) -> ok.
|
||||||
|
compile_exprs(Exprs) ->
|
||||||
|
Forms = lists:map(
|
||||||
|
fun(Expr) ->
|
||||||
|
{ok, Tokens, _} = erl_scan:string(lists:flatten(Expr)),
|
||||||
|
{ok, Form} = erl_parse:parse_form(Tokens),
|
||||||
|
Form
|
||||||
|
end, Exprs),
|
||||||
|
{ok, Code} = case compile:forms(Forms, []) of
|
||||||
|
{ok, ejabberd_options, Bin} -> {ok, Bin};
|
||||||
|
{ok, ejabberd_options, Bin, _Warnings} -> {ok, Bin};
|
||||||
|
Error -> Error
|
||||||
|
end,
|
||||||
|
{module, _} = code:load_binary(ejabberd_options, "nofile", Code),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
%% @doc This is only for debugging purposes, likely to report a bug
|
||||||
|
-spec dump() -> ok.
|
||||||
|
dump() ->
|
||||||
|
ETSFile = filename:join("/tmp", "ejabberd_options.ets"),
|
||||||
|
ErlFile = filename:join("/tmp", "ejabberd_options.erl"),
|
||||||
|
ETSData = io_lib:format("~p~n", [ets:tab2list(local_config)]),
|
||||||
|
ErlData = io_lib:format("~s~n", [str:join(get_exprs(), io_lib:nl())]),
|
||||||
|
case file:write_file(ETSFile, ETSData) of
|
||||||
|
ok -> io:format("ETS data written to ~s~n", [ETSFile]);
|
||||||
|
{error, Reason1} ->
|
||||||
|
io:format("Failed to write to ~s: ~s",
|
||||||
|
[ETSFile, file:format_error(Reason1)])
|
||||||
|
end,
|
||||||
|
case file:write_file(ErlFile, ErlData) of
|
||||||
|
ok -> io:format("Dynamic module written to ~s~n", [ErlFile]);
|
||||||
|
{error, Reason2} ->
|
||||||
|
io:format("Failed to write to ~s: ~s",
|
||||||
|
[ErlFile, file:format_error(Reason2)])
|
||||||
|
end.
|
||||||
|
@ -408,17 +408,6 @@ add_listener(PortIP, Module, Opts) ->
|
|||||||
PortIP1 = {Port, IPT, Proto},
|
PortIP1 = {Port, IPT, Proto},
|
||||||
case start_listener(PortIP1, Module, Opts) of
|
case start_listener(PortIP1, Module, Opts) of
|
||||||
{ok, _Pid} ->
|
{ok, _Pid} ->
|
||||||
Ports = case ejabberd_config:get_option(
|
|
||||||
listen, fun validate_cfg/1) of
|
|
||||||
undefined ->
|
|
||||||
[];
|
|
||||||
Ls ->
|
|
||||||
Ls
|
|
||||||
end,
|
|
||||||
Ports1 = lists:keydelete(PortIP1, 1, Ports),
|
|
||||||
Ports2 = [{PortIP1, Module, Opts} | Ports1],
|
|
||||||
Ports3 = lists:map(fun transform_option/1, Ports2),
|
|
||||||
ejabberd_config:add_option(listen, Ports3),
|
|
||||||
ok;
|
ok;
|
||||||
{error, {already_started, _Pid}} ->
|
{error, {already_started, _Pid}} ->
|
||||||
{error, {already_started, PortIP}};
|
{error, {already_started, PortIP}};
|
||||||
|
41
src/ejabberd_options.erl
Normal file
41
src/ejabberd_options.erl
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @doc
|
||||||
|
%%% This is a stub module which will be replaced during
|
||||||
|
%%% configuration load, see ejabberd_config:recompile_options/0.
|
||||||
|
%%% The only purpose of this file is to shut up xref/dialyzer
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 16 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_options).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([is_known/1]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
is_known(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
Loading…
Reference in New Issue
Block a user