./ct_report/coverage/mongoose_async_pools.COVER.html

1 -module(mongoose_async_pools).
2
3 -include("mongoose_logger.hrl").
4
5 -behaviour(supervisor).
6 -export([start_link/3, init/1]).
7 -ignore_xref([start_link/3]).
8
9 % API
10 -export([start_pool/3, stop_pool/2]).
11 -export([put_task/3, put_task/4, broadcast/3, broadcast_task/4]).
12 -ignore_xref([put_task/3, broadcast/3, broadcast_task/4]).
13 -export([sync/2]).
14
15 -type task() :: term().
16 -type pool_id() :: atom(). % The subsystem, like 'pm_mam', or 'inbox'
17 -type pool_name() :: atom(). % The pool name, like 'inbox_sup_async_pool_localhost'
18 -type pool_type() :: batch | aggregate.
19 -type pool_opts() :: #{pool_type := pool_type(),
20 _ => _}.
21 -type pool_extra() :: #{host_type := mongooseim:host_type(),
22 queue_length => non_neg_integer(),
23 _ => _}.
24
25 -type flush_callback() ::
26 fun(([task()], pool_extra()) ->
27 ok | {error, term()}).
28 -type prep_callback() ::
29 fun((task(), pool_extra()) ->
30 {ok, task()} | {error, term()}).
31 -type aggregate_callback() ::
32 fun((task(), task(), pool_extra()) ->
33 {ok, task()} | {error, term()}).
34 -type request_callback() ::
35 fun((task() | [task()], pool_extra()) ->
36 term()).
37 -type verify_callback() ::
38 fun((term(), task(), pool_extra()) -> term()).
39
40 -export_type([flush_callback/0,
41 prep_callback/0,
42 aggregate_callback/0,
43 request_callback/0,
44 verify_callback/0]).
45
46 -export_type([task/0, pool_id/0, pool_opts/0, pool_extra/0]).
47
48 -spec put_task(mongooseim:host_type(), pool_id(), term()) -> ok.
49 put_task(HostType, PoolId, Task) ->
50
:-(
PoolName = pool_name(HostType, PoolId),
51
:-(
wpool:cast(PoolName, {task, Task}, best_worker).
52
53 -spec put_task(mongooseim:host_type(), pool_id(), term(), term()) -> ok.
54 put_task(HostType, PoolId, Key, Task) ->
55 3636 PoolName = pool_name(HostType, PoolId),
56 3636 wpool:cast(PoolName, {task, Key, Task}, {hash_worker, Key}).
57
58 -spec broadcast(mongooseim:host_type(), pool_id(), term()) -> ok.
59 broadcast(HostType, PoolId, Task) ->
60
:-(
PoolName = pool_name(HostType, PoolId),
61
:-(
wpool:broadcast(PoolName, {broadcast, Task}).
62
63 -spec broadcast_task(mongooseim:host_type(), pool_id(), term(), term()) -> ok.
64 broadcast_task(HostType, PoolId, Key, Task) ->
65
:-(
PoolName = pool_name(HostType, PoolId),
66
:-(
wpool:broadcast(PoolName, {task, Key, Task}).
67
68 %%% API functions
69 -spec start_pool(mongooseim:host_type(), pool_id(), pool_opts()) ->
70 supervisor:startchild_ret().
71 start_pool(HostType, PoolId, PoolOpts) ->
72 89 ?LOG_INFO(#{what => async_pool_starting, host_type => HostType, pool_id => PoolId}),
73 89 Supervisor = sup_name(HostType, PoolId),
74 89 ChildSpec = #{id => Supervisor,
75 start => {?MODULE, start_link, [HostType, PoolId, PoolOpts]},
76 restart => transient,
77 type => supervisor},
78 89 ejabberd_sup:start_child(ChildSpec).
79
80 -spec stop_pool(mongooseim:host_type(), pool_id()) -> ok.
81 stop_pool(HostType, PoolId) ->
82 89 ?LOG_INFO(#{what => async_pool_stopping, host_type => HostType, pool_id => PoolId}),
83 89 ejabberd_sup:stop_child(sup_name(HostType, PoolId)).
84
85 -spec pool_name(mongooseim:host_type(), pool_id()) -> pool_name().
86 pool_name(HostType, PoolId) ->
87 3862 persistent_term:get({?MODULE, HostType, PoolId}).
88
89 -spec sync(mongooseim:host_type(), pool_id()) -> term().
90 sync(HostType, PoolId) ->
91 226 Pool = pool_name(HostType, PoolId),
92 226 WorkerNames = wpool:get_workers(Pool),
93 226 Context = #{what => sync_failed, host_type => HostType, pool_id => PoolId},
94 226 F = fun(Pid) -> safely:apply_and_log(gen_server, call, [Pid, sync], Context) end,
95 226 Results = mongoose_lib:pmap(F, WorkerNames),
96 226 [check_result(Result) || Result <- Results].
97
98 228 check_result({ok, ok}) -> ok;
99 3388 check_result({ok, skipped}) -> ok;
100
:-(
check_result(Other) -> ?LOG_ERROR(#{what => sync_failed, reason => Other}).
101
102 %%% Supervisor callbacks
103 -spec start_link(mongooseim:host_type(), pool_id(), pool_opts()) ->
104 {ok, pid()} | ignore | {error, term()}.
105 start_link(HostType, PoolId, PoolOpts) ->
106 89 Supervisor = sup_name(HostType, PoolId),
107 89 supervisor:start_link({local, Supervisor}, ?MODULE, {HostType, PoolId, PoolOpts}).
108
109 -spec init({mongooseim:host_type(), pool_id(), pool_opts()}) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
110 init({HostType, PoolId, PoolOpts}) ->
111 89 WPoolOpts = process_pool_opts(HostType, PoolId, PoolOpts),
112 89 PoolName = gen_pool_name(HostType, PoolId),
113 89 store_pool_name(HostType, PoolId, PoolName),
114 89 WorkerSpec = #{id => PoolName,
115 start => {wpool, start_pool, [PoolName, WPoolOpts]},
116 restart => permanent,
117 type => supervisor},
118 89 SupFlags = #{strategy => one_for_one,
119 intensity => 1,
120 period => 5},
121 89 {ok, {SupFlags, [WorkerSpec]}}.
122
123 %%% internal callbacks
124 -spec sup_name(mongooseim:host_type(), pool_id()) -> atom().
125 sup_name(HostType, PoolId) ->
126 267 list_to_atom(
127 atom_to_list(PoolId) ++ "_sup_async_pool_" ++ binary_to_list(HostType)).
128
129 -spec store_pool_name(mongooseim:host_type(), pool_id(), pool_name()) -> ok.
130 store_pool_name(HostType, PoolId, PoolName) ->
131 89 persistent_term:put({?MODULE, HostType, PoolId}, PoolName).
132
133 -spec gen_pool_name(mongooseim:host_type(), pool_id()) -> pool_name().
134 gen_pool_name(HostType, PoolId) ->
135 89 list_to_atom(
136 atom_to_list(PoolId) ++ "_async_pool_" ++ binary_to_list(HostType)).
137
138 -spec process_pool_opts(mongooseim:host_type(), pool_id(), pool_opts()) -> [wpool:option()].
139 process_pool_opts(HostType, PoolId, #{pool_size := NumWorkers} = Opts) ->
140 89 WorkerModule = select_worker_module(HostType, PoolId, Opts),
141 89 WorkerOpts = make_worker_opts(HostType, PoolId, Opts),
142 89 Worker = {WorkerModule, WorkerOpts},
143 89 [{worker, Worker},
144 {workers, NumWorkers},
145 {worker_opt, [{spawn_opt, [{message_queue_data, off_heap}]}]},
146 {worker_shutdown, 10000}].
147
148 select_worker_module(HostType, PoolId, #{pool_type := batch}) ->
149 62 mongoose_metrics:ensure_metric(HostType, [?MODULE, PoolId, timed_flushes], counter),
150 62 mongoose_metrics:ensure_metric(HostType, [?MODULE, PoolId, batch_flushes], counter),
151 62 mongoose_batch_worker;
152 select_worker_module(HostType, PoolId, #{pool_type := aggregate}) ->
153 27 mongoose_metrics:ensure_metric(HostType, [?MODULE, PoolId, async_request], counter),
154 27 mongoose_aggregator_worker.
155
156 -spec make_worker_opts(mongooseim:host_type(), pool_id(), pool_opts()) -> map().
157 make_worker_opts(HostType, PoolId, Opts) ->
158 89 Opts#{host_type => HostType, pool_id => PoolId,
159 flush_extra => make_extra(HostType, PoolId, Opts)}.
160
161 -spec make_extra(mongooseim:host_type(), pool_id(), pool_opts()) -> pool_extra().
162 make_extra(HostType, PoolId, Opts) ->
163 89 DefExtra = case maps:get(pool_type, Opts) of
164 62 batch -> #{host_type => HostType, queue_length => 0};
165 27 aggregate -> #{host_type => HostType}
166 end,
167 89 Extra = maps:merge(maps:get(flush_extra, Opts, #{}), DefExtra),
168 89 maybe_init_handler(HostType, PoolId, Opts, Extra).
169
170 maybe_init_handler(HostType, PoolId, Opts = #{init_callback := InitFun}, Extra)
171 when is_function(InitFun, 3) ->
172
:-(
Extra#{init_data => InitFun(HostType, PoolId, Opts)};
173 maybe_init_handler(_, _, _, Extra) ->
174 89 Extra.
Line Hits Source