Implement cache for mod_last
This commit is contained in:
parent
736a182544
commit
bcb44ccb6f
|
@ -45,17 +45,24 @@
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
-include("mod_last.hrl").
|
-include("mod_last.hrl").
|
||||||
|
|
||||||
|
-define(LAST_CACHE, last_activity_cache).
|
||||||
|
|
||||||
-callback init(binary(), gen_mod:opts()) -> any().
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
-callback import(binary(), #last_activity{}) -> ok | pass.
|
-callback import(binary(), #last_activity{}) -> ok | pass.
|
||||||
-callback get_last(binary(), binary()) ->
|
-callback get_last(binary(), binary()) ->
|
||||||
{ok, non_neg_integer(), binary()} | not_found | {error, any()}.
|
{ok, {non_neg_integer(), binary()}} | error | {error, any()}.
|
||||||
-callback store_last_info(binary(), binary(), non_neg_integer(), binary()) -> any().
|
-callback store_last_info(binary(), binary(), non_neg_integer(), binary()) -> ok | {error, any()}.
|
||||||
-callback remove_user(binary(), binary()) -> any().
|
-callback remove_user(binary(), binary()) -> any().
|
||||||
|
-callback use_cache(binary()) -> boolean().
|
||||||
|
-callback cache_nodes(binary()) -> [node()].
|
||||||
|
|
||||||
|
-optional_callbacks([use_cache/1, cache_nodes/1]).
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
Mod:init(Host, Opts),
|
Mod:init(Host, Opts),
|
||||||
|
init_cache(Mod, Host, Opts),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||||
?NS_LAST, ?MODULE, process_local_iq, IQDisc),
|
?NS_LAST, ?MODULE, process_local_iq, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||||
|
@ -91,6 +98,7 @@ reload(Host, NewOpts, OldOpts) ->
|
||||||
true ->
|
true ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
|
init_cache(NewMod, Host, NewOpts),
|
||||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||||
{false, IQDisc, _} ->
|
{false, IQDisc, _} ->
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
|
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
|
||||||
|
@ -180,13 +188,23 @@ privacy_check_packet(allow, C2SState,
|
||||||
privacy_check_packet(Acc, _, _, _) ->
|
privacy_check_packet(Acc, _, _, _) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
%% @spec (LUser::string(), LServer::string()) ->
|
|
||||||
%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
|
|
||||||
-spec get_last(binary(), binary()) -> {ok, non_neg_integer(), binary()} |
|
-spec get_last(binary(), binary()) -> {ok, non_neg_integer(), binary()} |
|
||||||
not_found | {error, any()}.
|
not_found | {error, any()}.
|
||||||
get_last(LUser, LServer) ->
|
get_last(LUser, LServer) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:get_last(LUser, LServer).
|
Res = case use_cache(Mod, LServer) of
|
||||||
|
true ->
|
||||||
|
ets_cache:lookup(
|
||||||
|
?LAST_CACHE, {LUser, LServer},
|
||||||
|
fun() -> Mod:get_last(LUser, LServer) end);
|
||||||
|
false ->
|
||||||
|
Mod:get_last(LUser, LServer)
|
||||||
|
end,
|
||||||
|
case Res of
|
||||||
|
{ok, {TimeStamp, Status}} -> {ok, TimeStamp, Status};
|
||||||
|
error -> not_found;
|
||||||
|
Err -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
-spec get_last_iq(iq(), binary(), binary()) -> iq().
|
-spec get_last_iq(iq(), binary(), binary()) -> iq().
|
||||||
get_last_iq(#iq{lang = Lang} = IQ, LUser, LServer) ->
|
get_last_iq(#iq{lang = Lang} = IQ, LUser, LServer) ->
|
||||||
|
@ -226,7 +244,16 @@ store_last_info(User, Server, TimeStamp, Status) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:store_last_info(LUser, LServer, TimeStamp, Status).
|
case use_cache(Mod, LServer) of
|
||||||
|
true ->
|
||||||
|
ets_cache:update(
|
||||||
|
?LAST_CACHE, {LUser, LServer}, {ok, {TimeStamp, Status}},
|
||||||
|
fun() ->
|
||||||
|
Mod:store_last_info(LUser, LServer, TimeStamp, Status)
|
||||||
|
end, cache_nodes(Mod, LServer));
|
||||||
|
false ->
|
||||||
|
Mod:store_last_info(LUser, LServer, TimeStamp, Status)
|
||||||
|
end.
|
||||||
|
|
||||||
-spec get_last_info(binary(), binary()) -> {ok, non_neg_integer(), binary()} |
|
-spec get_last_info(binary(), binary()) -> {ok, non_neg_integer(), binary()} |
|
||||||
not_found.
|
not_found.
|
||||||
|
@ -241,7 +268,51 @@ remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:remove_user(LUser, LServer).
|
Mod:remove_user(LUser, LServer),
|
||||||
|
ets_cache:delete(?LAST_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(?LAST_CACHE, CacheOpts);
|
||||||
|
false ->
|
||||||
|
ets_cache:delete(?LAST_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() ->
|
import_info() ->
|
||||||
[{<<"last">>, 3}].
|
[{<<"last">>, 3}].
|
||||||
|
@ -270,4 +341,11 @@ depends(_Host, _Opts) ->
|
||||||
|
|
||||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||||
mod_opt_type(_) -> [db_type, iqdisc].
|
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(_) ->
|
||||||
|
[db_type, iqdisc, cache_life_time, cache_size, use_cache, cache_missed].
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
-behaviour(mod_last).
|
-behaviour(mod_last).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/2, import/2, get_last/2, store_last_info/4, remove_user/2]).
|
-export([init/2, import/2, get_last/2, store_last_info/4,
|
||||||
|
remove_user/2, use_cache/1]).
|
||||||
-export([need_transform/1, transform/1]).
|
-export([need_transform/1, transform/1]).
|
||||||
|
|
||||||
-include("mod_last.hrl").
|
-include("mod_last.hrl").
|
||||||
|
@ -38,31 +39,36 @@
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
init(_Host, _Opts) ->
|
init(_Host, _Opts) ->
|
||||||
ejabberd_mnesia:create(?MODULE, last_activity,
|
ejabberd_mnesia:create(?MODULE, last_activity,
|
||||||
[{disc_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
{attributes, record_info(fields, last_activity)}]).
|
{attributes, record_info(fields, last_activity)}]).
|
||||||
|
|
||||||
|
use_cache(Host) ->
|
||||||
|
case mnesia:table_info(last_activity, storage_type) of
|
||||||
|
disc_only_copies ->
|
||||||
|
gen_mod:get_module_opt(
|
||||||
|
Host, mod_last, use_cache,
|
||||||
|
ejabberd_config:use_cache(Host));
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
get_last(LUser, LServer) ->
|
get_last(LUser, LServer) ->
|
||||||
case mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
case mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
||||||
[] ->
|
[] ->
|
||||||
not_found;
|
error;
|
||||||
[#last_activity{timestamp = TimeStamp,
|
[#last_activity{timestamp = TimeStamp,
|
||||||
status = Status}] ->
|
status = Status}] ->
|
||||||
{ok, TimeStamp, Status}
|
{ok, {TimeStamp, Status}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
||||||
US = {LUser, LServer},
|
mnesia:dirty_write(#last_activity{us = {LUser, LServer},
|
||||||
F = fun () ->
|
timestamp = TimeStamp,
|
||||||
mnesia:write(#last_activity{us = US,
|
status = Status}).
|
||||||
timestamp = TimeStamp,
|
|
||||||
status = Status})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F).
|
|
||||||
|
|
||||||
remove_user(LUser, LServer) ->
|
remove_user(LUser, LServer) ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
F = fun () -> mnesia:delete({last_activity, US}) end,
|
mnesia:dirty_delete({last_activity, US}).
|
||||||
mnesia:transaction(F).
|
|
||||||
|
|
||||||
import(_LServer, #last_activity{} = LA) ->
|
import(_LServer, #last_activity{} = LA) ->
|
||||||
mnesia:dirty_write(LA).
|
mnesia:dirty_write(LA).
|
||||||
|
|
|
@ -43,19 +43,20 @@ get_last(LUser, LServer) ->
|
||||||
{LUser, LServer}) of
|
{LUser, LServer}) of
|
||||||
{ok, #last_activity{timestamp = TimeStamp,
|
{ok, #last_activity{timestamp = TimeStamp,
|
||||||
status = Status}} ->
|
status = Status}} ->
|
||||||
{ok, TimeStamp, Status};
|
{ok, {TimeStamp, Status}};
|
||||||
{error, notfound} ->
|
{error, notfound} ->
|
||||||
not_found;
|
error;
|
||||||
Err ->
|
_Err ->
|
||||||
Err
|
%% TODO: log error
|
||||||
|
{error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
{atomic, ejabberd_riak:put(#last_activity{us = US,
|
ejabberd_riak:put(#last_activity{us = US,
|
||||||
timestamp = TimeStamp,
|
timestamp = TimeStamp,
|
||||||
status = Status},
|
status = Status},
|
||||||
last_activity_schema())}.
|
last_activity_schema()).
|
||||||
|
|
||||||
remove_user(LUser, LServer) ->
|
remove_user(LUser, LServer) ->
|
||||||
{atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}.
|
{atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}.
|
||||||
|
|
|
@ -45,17 +45,24 @@ init(_Host, _Opts) ->
|
||||||
get_last(LUser, LServer) ->
|
get_last(LUser, LServer) ->
|
||||||
case catch sql_queries:get_last(LServer, LUser) of
|
case catch sql_queries:get_last(LServer, LUser) of
|
||||||
{selected, []} ->
|
{selected, []} ->
|
||||||
not_found;
|
error;
|
||||||
{selected, [{TimeStamp, Status}]} ->
|
{selected, [{TimeStamp, Status}]} ->
|
||||||
{ok, TimeStamp, Status};
|
{ok, {TimeStamp, Status}};
|
||||||
Reason ->
|
Reason ->
|
||||||
?ERROR_MSG("failed to get last for user ~s@~s: ~p",
|
?ERROR_MSG("failed to get last for user ~s@~s: ~p",
|
||||||
[LUser, LServer, Reason]),
|
[LUser, LServer, Reason]),
|
||||||
{error, {invalid_result, Reason}}
|
{error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
||||||
sql_queries:set_last_t(LServer, LUser, TimeStamp, Status).
|
case sql_queries:set_last_t(LServer, LUser, TimeStamp, Status) of
|
||||||
|
ok ->
|
||||||
|
ok;
|
||||||
|
Err ->
|
||||||
|
?ERROR_MSG("failed to store last activity for ~s@~s: ~p",
|
||||||
|
[LUser, LServer, Err]),
|
||||||
|
{error, db_failure}
|
||||||
|
end.
|
||||||
|
|
||||||
remove_user(LUser, LServer) ->
|
remove_user(LUser, LServer) ->
|
||||||
sql_queries:del_last(LServer, LUser).
|
sql_queries:del_last(LServer, LUser).
|
||||||
|
|
Loading…
Reference in New Issue