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,
|
||||
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,
|
||||
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]).
|
||||
|
||||
@ -773,6 +774,7 @@ set_opts(State) ->
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, _} ->
|
||||
recompile_options(),
|
||||
set_log_level();
|
||||
{aborted,{no_exists,Table}} ->
|
||||
MnesiaDirectory = mnesia:system_info(directory),
|
||||
@ -788,7 +790,7 @@ set_opts(State) ->
|
||||
end.
|
||||
|
||||
set_log_level() ->
|
||||
Level = ejabberd_config:get_option(
|
||||
Level = get_option(
|
||||
loglevel,
|
||||
fun(P) when P>=0, P=<5 -> P end,
|
||||
4),
|
||||
@ -806,7 +808,8 @@ add_option(Opt, Val) ->
|
||||
mnesia:transaction(fun() ->
|
||||
mnesia:write(#local_config{key = Opt,
|
||||
value = Val})
|
||||
end).
|
||||
end),
|
||||
recompile_options().
|
||||
|
||||
-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, global}, F, Default);
|
||||
get_option(Opt, F, Default) ->
|
||||
case Opt of
|
||||
{O, global} when is_atom(O) -> ok;
|
||||
{O, H} when is_atom(O), is_binary(H) -> ok;
|
||||
_ -> ?WARNING_MSG("Option ~p has invalid (outdated?) format. "
|
||||
"This is likely a bug", [Opt])
|
||||
{Key, Host} = case Opt of
|
||||
{O, global} when is_atom(O) -> Opt;
|
||||
{O, H} when is_atom(O), is_binary(H) -> Opt;
|
||||
_ ->
|
||||
?WARNING_MSG("Option ~p has invalid (outdated?) "
|
||||
"format. This is likely a bug", [Opt]),
|
||||
{undefined, global}
|
||||
end,
|
||||
case ets:lookup(local_config, Opt) of
|
||||
[#local_config{value = Val}] ->
|
||||
prepare_opt_val(Opt, Val, F, Default);
|
||||
_ ->
|
||||
case Opt of
|
||||
{Key, Host} when Host /= global ->
|
||||
get_option({Key, global}, F, Default);
|
||||
_ ->
|
||||
case ejabberd_options:is_known(Key) of
|
||||
true ->
|
||||
try ejabberd_options:Key(Host) of
|
||||
Val -> prepare_opt_val(Opt, Val, F, Default)
|
||||
catch _:function_clause ->
|
||||
Default
|
||||
end;
|
||||
false ->
|
||||
Default
|
||||
end
|
||||
end.
|
||||
|
||||
-spec has_option(atom() | {atom(), global | binary()}) -> any().
|
||||
@ -1481,7 +1485,7 @@ opt_type(_) ->
|
||||
|
||||
-spec may_hide_data(any()) -> any().
|
||||
may_hide_data(Data) ->
|
||||
case ejabberd_config:get_option(
|
||||
case get_option(
|
||||
hide_sensitive_log_data,
|
||||
fun(false) -> false;
|
||||
(true) -> true
|
||||
@ -1531,3 +1535,79 @@ cache_missed(Host) ->
|
||||
%% NOTE: the integer value returned is in *seconds*
|
||||
cache_life_time(Host) ->
|
||||
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},
|
||||
case start_listener(PortIP1, Module, Opts) of
|
||||
{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;
|
||||
{error, {already_started, _Pid}} ->
|
||||
{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