1 |
|
-module(mongoose_user_cache). |
2 |
|
|
3 |
|
-include("mongoose_config_spec.hrl"). |
4 |
|
|
5 |
|
-export([start_new_cache/3, stop_cache/2, handle_telemetry_event/4, |
6 |
|
config_spec/0, process_cache_config/1]). |
7 |
|
|
8 |
|
-export([is_member/3, get_entry/3, merge_entry/4, delete_user/3, delete_domain/3]). |
9 |
|
|
10 |
|
-spec is_member(mongooseim:host_type(), module(), jid:jid()) -> boolean(). |
11 |
|
is_member(HostType, Module, Jid) -> |
12 |
14336 |
CacheName = cache_name(HostType, Module), |
13 |
14336 |
segmented_cache:is_member(CacheName, key(Jid)). |
14 |
|
|
15 |
|
-spec get_entry(mongooseim:host_type(), module(), jid:jid()) -> term() | not_found. |
16 |
|
get_entry(HostType, Module, Jid) -> |
17 |
5316 |
CacheName = cache_name(HostType, Module), |
18 |
5316 |
segmented_cache:get_entry(CacheName, key(Jid)). |
19 |
|
|
20 |
|
-spec merge_entry(mongooseim:host_type(), module(), jid:jid(), map()) -> boolean(). |
21 |
|
merge_entry(HostType, Module, Jid, Entry) -> |
22 |
3640 |
CacheName = cache_name(HostType, Module), |
23 |
3640 |
segmented_cache:merge_entry(CacheName, key(Jid), Entry). |
24 |
|
|
25 |
|
-spec delete_user(mongooseim:host_type(), module(), jid:jid()) -> true. |
26 |
|
delete_user(HostType, Module, Jid) -> |
27 |
6698 |
CacheName = cache_name(HostType, Module), |
28 |
6698 |
segmented_cache:delete_entry(CacheName, key(Jid)). |
29 |
|
|
30 |
|
-spec delete_domain(mongooseim:host_type(), module(), jid:lserver()) -> true. |
31 |
|
delete_domain(HostType, Module, Domain) -> |
32 |
16 |
CacheName = cache_name(HostType, Module), |
33 |
16 |
segmented_cache:delete_pattern(CacheName, {{'_', Domain}, '_'}). |
34 |
|
|
35 |
|
|
36 |
|
%% path: (host_config[].)modules.*.cache |
37 |
|
config_spec() -> |
38 |
624 |
#section{ |
39 |
|
items = #{<<"module">> => #option{type = atom, validate = {enum, [internal, mod_cache_users]}}, |
40 |
|
<<"strategy">> => #option{type = atom, validate = {enum, [fifo, lru]}}, |
41 |
|
<<"time_to_live">> => #option{type = int_or_infinity, validate = positive}, |
42 |
|
<<"number_of_segments">> => #option{type = integer, validate = positive} |
43 |
|
}, |
44 |
|
process = fun ?MODULE:process_cache_config/1, |
45 |
|
defaults = #{<<"module">> => internal, |
46 |
|
<<"strategy">> => fifo, |
47 |
|
<<"time_to_live">> => 480, |
48 |
|
<<"number_of_segments">> => 3} |
49 |
|
}. |
50 |
|
|
51 |
|
%% If an external module is provided, disallow any other configuration key |
52 |
|
process_cache_config(#{module := internal} = Config) -> |
53 |
:-( |
Config; |
54 |
|
process_cache_config(#{module := Module}) -> |
55 |
:-( |
#{module => Module}; |
56 |
|
process_cache_config(Config) -> |
57 |
47 |
Config. |
58 |
|
|
59 |
|
-spec start_new_cache(mongooseim:host_type(), module(), gen_mod:module_opts()) -> any(). |
60 |
|
start_new_cache(HostType, Module, Opts) -> |
61 |
252 |
case gen_mod:get_opt(module, Opts, internal) of |
62 |
252 |
internal -> do_start_new_cache(HostType, Module, Opts); |
63 |
:-( |
_ -> ok |
64 |
|
end. |
65 |
|
|
66 |
|
do_start_new_cache(HostType, Module, Opts) -> |
67 |
252 |
CacheName = gen_mod:get_module_proc(HostType, Module), |
68 |
252 |
CacheOpts = #{scope => mim_scope, |
69 |
|
merger_fun => fun maps:merge/2, |
70 |
|
segment_num => gen_mod:get_opt(number_of_segments, Opts), |
71 |
|
strategy => gen_mod:get_opt(strategy, Opts), |
72 |
|
ttl => gen_mod:get_opt(time_to_live, Opts)}, |
73 |
252 |
Spec = #{id => CacheName, start => {segmented_cache, start_link, [CacheName, CacheOpts]}, |
74 |
|
restart => permanent, shutdown => 5000, |
75 |
|
type => worker, modules => [segmented_cache]}, |
76 |
252 |
{ok, _} = ejabberd_sup:start_child(Spec), |
77 |
252 |
create_metrics(HostType, Module, CacheName), |
78 |
252 |
ok. |
79 |
|
|
80 |
|
create_metrics(HostType, Module, CacheName) -> |
81 |
252 |
telemetry:attach(CacheName, |
82 |
|
[segmented_cache, CacheName, request, stop], |
83 |
|
fun ?MODULE:handle_telemetry_event/4, |
84 |
|
#{host_type => HostType, cache_name => CacheName, module => Module}), |
85 |
252 |
mongoose_metrics:ensure_metric(HostType, [Module, hit], counter), |
86 |
252 |
mongoose_metrics:ensure_metric(HostType, [Module, miss], counter), |
87 |
252 |
mongoose_metrics:ensure_metric(HostType, [Module, latency], histogram). |
88 |
|
|
89 |
|
handle_telemetry_event([segmented_cache, CacheName, request, stop], |
90 |
|
#{duration := Latency}, |
91 |
|
#{hit := Hit}, |
92 |
|
#{host_type := HostType, cache_name := CacheName, module := Module}) -> |
93 |
19652 |
case Hit of |
94 |
15435 |
true -> mongoose_metrics:update(HostType, [Module, hit], 1); |
95 |
4217 |
false -> mongoose_metrics:update(HostType, [Module, miss], 1) |
96 |
|
end, |
97 |
19652 |
mongoose_metrics:update(HostType, [Module, latency], Latency), |
98 |
19652 |
ok. |
99 |
|
|
100 |
|
-spec stop_cache(mongooseim:host_type(), module()) -> ok. |
101 |
|
stop_cache(HostType, Module) -> |
102 |
250 |
case gen_mod:get_module_opt(HostType, Module, module, internal) of |
103 |
250 |
internal -> ok = ejabberd_sup:stop_child(cache_name(HostType, Module)); |
104 |
:-( |
_ConfiguredModule -> ok |
105 |
|
end. |
106 |
|
|
107 |
|
-spec cache_name(mongooseim:host_type(), module()) -> atom(). |
108 |
|
cache_name(HostType, Module) -> |
109 |
30256 |
case gen_mod:get_module_opt(HostType, Module, module, internal) of |
110 |
30256 |
internal -> gen_mod:get_module_proc(HostType, Module); |
111 |
:-( |
ConfiguredModule -> gen_mod:get_module_proc(HostType, ConfiguredModule) |
112 |
|
end. |
113 |
|
|
114 |
|
-compile({inline, [key/1]}). |
115 |
|
-spec key(jid:jid()) -> jid:simple_bare_jid(). |
116 |
|
key(Jid) -> |
117 |
29990 |
jid:to_lus(Jid). |