1 |
|
%% Cleaning is triggered from all MongooseIM nodes. |
2 |
|
%% Though, it's quick, if there is nothing to remove. |
3 |
|
-module(mongoose_domain_db_cleaner). |
4 |
|
-include("mongoose_logger.hrl"). |
5 |
|
|
6 |
|
-export([start/1, stop/0]). |
7 |
|
-export([start_link/1]). |
8 |
|
|
9 |
|
%% gen_server callbacks |
10 |
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, |
11 |
|
terminate/2, code_change/3]). |
12 |
|
|
13 |
|
-ignore_xref([code_change/3, handle_call/3, handle_cast/2, handle_info/2, init/1, |
14 |
|
start_link/1, terminate/2]). |
15 |
|
|
16 |
|
%% --------------------------------------------------------------------------- |
17 |
|
%% Config |
18 |
|
|
19 |
|
default_cleaning_interval() -> |
20 |
:-( |
1800. %% 30 minutes |
21 |
|
|
22 |
|
default_max_age() -> |
23 |
:-( |
7200. %% 2 hours |
24 |
|
|
25 |
|
%% --------------------------------------------------------------------------- |
26 |
|
%% Client code |
27 |
|
|
28 |
|
start(Opts) -> |
29 |
:-( |
ChildSpec = |
30 |
|
{?MODULE, |
31 |
|
{?MODULE, start_link, [Opts]}, |
32 |
|
permanent, infinity, worker, [?MODULE]}, |
33 |
:-( |
supervisor:start_child(ejabberd_sup, ChildSpec), |
34 |
:-( |
ok. |
35 |
|
|
36 |
|
stop() -> |
37 |
:-( |
supervisor:terminate_child(ejabberd_sup, ?MODULE), |
38 |
:-( |
supervisor:delete_child(ejabberd_sup, ?MODULE), |
39 |
:-( |
ok. |
40 |
|
|
41 |
|
start_link(Opts) -> |
42 |
:-( |
gen_server:start_link({local, ?MODULE}, ?MODULE, Opts, []). |
43 |
|
|
44 |
|
%% --------------------------------------------------------------------------- |
45 |
|
%% Server callbacks |
46 |
|
|
47 |
|
init(Opts) -> |
48 |
:-( |
Interval = proplists:get_value(event_cleaning_interval, Opts, default_cleaning_interval()), |
49 |
:-( |
MaxAge = proplists:get_value(event_max_age, Opts, default_max_age()), |
50 |
:-( |
?LOG_INFO(#{what => domain_cleaner_start, cleaning_interval => Interval, max_age => MaxAge}), |
51 |
:-( |
State = #{max_age => MaxAge}, |
52 |
:-( |
self() ! schedule_removal, |
53 |
:-( |
timer:send_interval(timer:seconds(Interval), schedule_removal), |
54 |
:-( |
{ok, State}. |
55 |
|
|
56 |
|
handle_call(Request, From, State) -> |
57 |
:-( |
?UNEXPECTED_CALL(Request, From), |
58 |
:-( |
{reply, ok, State}. |
59 |
|
|
60 |
|
handle_cast(Msg, State) -> |
61 |
:-( |
?UNEXPECTED_CAST(Msg), |
62 |
:-( |
{noreply, State}. |
63 |
|
|
64 |
|
handle_info(schedule_removal, State) -> |
65 |
:-( |
{noreply, schedule_removal(State)}; |
66 |
|
handle_info({timeout, TimerRef, Msg}, State) -> |
67 |
:-( |
{noreply, handle_timeout(TimerRef, Msg, State)}; |
68 |
|
handle_info(Info, State) -> |
69 |
:-( |
?UNEXPECTED_INFO(Info), |
70 |
:-( |
{noreply, State}. |
71 |
|
|
72 |
|
terminate(_Reason, _State) -> |
73 |
:-( |
ok. |
74 |
|
|
75 |
|
code_change(_OldVsn, State, _Extra) -> |
76 |
:-( |
{ok, State}. |
77 |
|
|
78 |
|
%% --------------------------------------------------------------------------- |
79 |
|
%% Server helpers |
80 |
|
|
81 |
|
%% We are ensuring that to remove events, they have to be in the database |
82 |
|
%% for some amount of time |
83 |
|
schedule_removal(State = #{max_age := MaxAge}) -> |
84 |
:-( |
try mongoose_domain_sql:get_minmax_event_id() of |
85 |
|
{_Min, LastEventId} -> |
86 |
:-( |
Msg = {do_removal, LastEventId}, |
87 |
:-( |
erlang:start_timer(timer:seconds(MaxAge), self(), Msg) |
88 |
|
catch Class:Reason:Stacktrace -> |
89 |
|
%% It's safe to skip scheduling |
90 |
:-( |
?LOG_ERROR(#{what => domain_cleaning_schedule_failed, |
91 |
|
text => <<"Failed to get LastEventId">>, |
92 |
:-( |
class => Class, reason => Reason, stacktrace => Stacktrace}) |
93 |
|
end, |
94 |
:-( |
State. |
95 |
|
|
96 |
|
handle_timeout(_TimerRef, {do_removal, LastEventId}, State) -> |
97 |
:-( |
mongoose_domain_sql:delete_events_older_than(LastEventId), |
98 |
:-( |
State. |