summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaweł Chmielowski <pchmielowski@process-one.net>2019-07-16 13:57:48 +0200
committerPaweł Chmielowski <pchmielowski@process-one.net>2019-07-16 15:09:58 +0200
commit6b3d0d154e96a42ba1f18ad3a445d9f6e9aaccfa (patch)
tree941d767c01fa655f7c09ba98689b8e08bf6bc81c
parentBump xmpp version and fix revealed bugs (diff)
Add code for hibernating inactive muc_room processes
Diffstat (limited to '')
-rw-r--r--include/mod_muc_room.hrl3
-rw-r--r--src/mod_muc.erl16
-rw-r--r--src/mod_muc_opt.erl7
-rw-r--r--src/mod_muc_room.erl94
4 files changed, 96 insertions, 24 deletions
diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl
index 2f09e5a5..c987b9e2 100644
--- a/include/mod_muc_room.hrl
+++ b/include/mod_muc_room.hrl
@@ -118,7 +118,8 @@
just_created = erlang:system_time(microsecond) :: true | integer(),
activity = treap:empty() :: treap:treap(),
room_shaper = none :: ejabberd_shaper:shaper(),
- room_queue :: p1_queue:queue({message | presence, jid()}) | undefined
+ room_queue :: p1_queue:queue({message | presence, jid()}) | undefined,
+ hibernate_timer = none :: reference() | none | hibernating
}).
-type users() :: #{ljid() => #user{}}.
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index 7915b283..350fe9d3 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -787,7 +787,16 @@ load_room(RMod, Host, ServerHost, Room) ->
?DEBUG("Restore room: ~s", [Room]),
start_room(RMod, Host, ServerHost, Room, Opts0);
_ ->
- {error, notfound}
+ ?DEBUG("Restore hibernated non-persistent room: ~s", [Room]),
+ Res = start_room(RMod, Host, ServerHost, Room, Opts0),
+ Mod = gen_mod:db_mod(ServerHost, mod_muc),
+ case erlang:function_exported(Mod, get_subscribed_rooms, 3) of
+ true ->
+ ok;
+ _ ->
+ forget_room(ServerHost, Host, Room)
+ end,
+ Res
end
end.
@@ -1167,7 +1176,9 @@ mod_opt_type(host) ->
mod_opt_type(hosts) ->
econf:hosts();
mod_opt_type(queue_type) ->
- econf:queue_type().
+ econf:queue_type();
+mod_opt_type(hibernation_timeout) ->
+ econf:non_neg_int(infinity).
mod_options(Host) ->
[{access, all},
@@ -1198,6 +1209,7 @@ mod_options(Host) ->
{user_message_shaper, none},
{user_presence_shaper, none},
{preload_rooms, true},
+ {hibernation_timeout, infinity},
{default_room_options,
[{allow_change_subj,true},
{allow_private_messages,true},
diff --git a/src/mod_muc_opt.erl b/src/mod_muc_opt.erl
index df6d5e78..6beca88c 100644
--- a/src/mod_muc_opt.erl
+++ b/src/mod_muc_opt.erl
@@ -11,6 +11,7 @@
-export([access_register/1]).
-export([db_type/1]).
-export([default_room_options/1]).
+-export([hibernation_timeout/1]).
-export([history_size/1]).
-export([host/1]).
-export([hosts/1]).
@@ -81,6 +82,12 @@ default_room_options(Opts) when is_map(Opts) ->
default_room_options(Host) ->
gen_mod:get_module_opt(Host, mod_muc, default_room_options).
+-spec hibernation_timeout(gen_mod:opts() | global | binary()) -> 'infinity' | non_neg_integer().
+hibernation_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hibernation_timeout, Opts);
+hibernation_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, hibernation_timeout).
+
-spec history_size(gen_mod:opts() | global | binary()) -> non_neg_integer().
history_size(Opts) when is_map(Opts) ->
gen_mod:get_opt(history_size, Opts);
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index 420b9959..d29aefb8 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -280,7 +280,7 @@ init([Host, ServerHost, Access, Room, HistorySize,
[Room, Host, jid:encode(Creator)]),
add_to_log(room_existence, created, State1),
add_to_log(room_existence, started, State1),
- {ok, normal_state, State1};
+ {ok, normal_state, reset_hibernate_timer(State1)};
init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType]) ->
process_flag(trap_exit, true),
Shaper = ejabberd_shaper:new(RoomShaper),
@@ -294,7 +294,7 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType])
room_queue = RoomQueue,
room_shaper = Shaper}),
add_to_log(room_existence, started, State),
- {ok, normal_state, State}.
+ {ok, normal_state, reset_hibernate_timer(State)}.
normal_state({route, <<"">>,
#message{from = From, type = Type, lang = Lang} = Packet},
@@ -619,6 +619,15 @@ normal_state({route, ToNick,
ejabberd_router:route_error(Packet, Err)
end,
{next_state, normal_state, StateData};
+normal_state(hibernate, StateData) ->
+ case maps:size(StateData#state.users) of
+ 0 ->
+ store_room_no_checks(StateData, []),
+ ?INFO_MSG("Hibernating room ~s@~s", [StateData#state.room, StateData#state.host]),
+ {stop, normal, StateData#state{hibernate_timer = hibernating}};
+ _ ->
+ {next_state, normal_state, StateData}
+ end;
normal_state(_Event, StateData) ->
{next_state, normal_state, StateData}.
@@ -882,12 +891,19 @@ terminate(Reason, _StateName,
end,
tab_remove_online_user(JID, StateData)
end, [], get_users_and_subscribers(StateData)),
- add_to_log(room_existence, stopped, StateData),
- case (StateData#state.config)#config.persistent of
- false ->
- ejabberd_hooks:run(room_destroyed, LServer, [LServer, Room, Host]);
+
+ disable_hibernate_timer(StateData),
+ case StateData#state.hibernate_timer of
+ hibernating ->
+ ok;
_ ->
- ok
+ add_to_log(room_existence, stopped, StateData),
+ case (StateData#state.config)#config.persistent of
+ false ->
+ ejabberd_hooks:run(room_destroyed, LServer, [LServer, Room, Host]);
+ _ ->
+ ok
+ end
end,
mod_muc:room_destroyed(Host, Room, self(), LServer)
catch ?EX_RULE(E, R, St) ->
@@ -1300,7 +1316,7 @@ close_room_if_temporary_and_empty(StateData1) ->
"and empty",
[jid:encode(StateData1#state.jid)]),
add_to_log(room_existence, destroyed, StateData1),
- maybe_forget_room(StateData1),
+ forget_room(StateData1),
{stop, normal, StateData1};
_ -> {next_state, normal_state, StateData1}
end.
@@ -1768,7 +1784,7 @@ store_user_activity(JID, UserActivity, StateData) ->
Activity)}
end
end,
- StateData1.
+ reset_hibernate_timer(StateData1).
-spec clean_treap(treap:treap(), integer() | {1, integer()}) -> treap:treap().
clean_treap(Treap, CleanPriority) ->
@@ -1866,7 +1882,7 @@ set_subscriber(JID, Nick, Nodes,
add_online_user(JID, Nick, Role, StateData) ->
tab_add_online_user(JID, StateData),
User = #user{jid = JID, nick = Nick, role = Role},
- update_online_user(JID, User, StateData).
+ reset_hibernate_timer(update_online_user(JID, User, StateData)).
-spec remove_online_user(jid(), state()) -> state().
remove_online_user(JID, StateData) ->
@@ -1887,7 +1903,7 @@ remove_online_user(JID, StateData, Reason) ->
catch _:{badkey, _} ->
StateData#state.nicks
end,
- StateData#state{users = Users, nicks = Nicks}.
+ reset_hibernate_timer(StateData#state{users = Users, nicks = Nicks}).
-spec filter_presence(presence()) -> presence().
filter_presence(Presence) ->
@@ -3633,7 +3649,6 @@ change_config(Config, StateData) ->
maybe_forget_room(StateData),
StateData1#state{affiliations = Affiliations};
_ ->
- maybe_forget_room(StateData),
StateData1
end,
case {(StateData#state.config)#config.members_only,
@@ -3965,9 +3980,16 @@ destroy_room(DEl, StateData) ->
Info#user.jid, Packet,
?NS_MUCSUB_NODES_CONFIG, StateData)
end, ok, get_users_and_subscribers(StateData)),
- maybe_forget_room(StateData),
+ forget_room(StateData),
{result, undefined, stop}.
+-spec forget_room(state()) -> state().
+forget_room(StateData) ->
+ mod_muc:forget_room(StateData#state.server_host,
+ StateData#state.host,
+ StateData#state.room),
+ StateData.
+
-spec maybe_forget_room(state()) -> state().
maybe_forget_room(StateData) ->
Forget = case (StateData#state.config)#config.persistent of
@@ -3979,10 +4001,7 @@ maybe_forget_room(StateData) ->
end,
case Forget of
true ->
- mod_muc:forget_room(StateData#state.server_host,
- StateData#state.host,
- StateData#state.room),
- StateData;
+ forget_room(StateData);
_ ->
StateData
end.
@@ -4549,14 +4568,17 @@ store_room(StateData, ChangesHints) ->
end
end,
if ShouldStore ->
- mod_muc:store_room(StateData#state.server_host,
- StateData#state.host, StateData#state.room,
- make_opts(StateData),
- ChangesHints);
+ store_room_no_checks(StateData, ChangesHints);
true ->
ok
end.
+store_room_no_checks(StateData, ChangesHints) ->
+ mod_muc:store_room(StateData#state.server_host,
+ StateData#state.host, StateData#state.room,
+ make_opts(StateData),
+ ChangesHints).
+
-spec send_subscriptions_change_notifications(jid(), binary(), subscribe|unsubscribe, state()) -> ok.
send_subscriptions_change_notifications(From, Nick, Type, State) ->
maps:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) ->
@@ -4672,3 +4694,33 @@ send_wrapped_multiple(From, Users, Packet, Node, State) ->
-spec has_body_or_subject(message()) -> boolean().
has_body_or_subject(#message{body = Body, subject = Subj}) ->
Body /= [] orelse Subj /= [].
+
+-spec reset_hibernate_timer(state()) -> state().
+reset_hibernate_timer(State) ->
+ case State#state.hibernate_timer of
+ hibernating ->
+ ok;
+ _ ->
+ disable_hibernate_timer(State),
+ NewTimer = case {mod_muc_opt:hibernation_timeout(State#state.server_host),
+ maps:size(State#state.users)} of
+ {infinity, _} ->
+ none;
+ {Timeout, 0} ->
+ p1_fsm:send_event_after(timer:minutes(Timeout), hibernate);
+ _ ->
+ none
+ end,
+ State#state{hibernate_timer = NewTimer}
+ end.
+
+
+-spec disable_hibernate_timer(state()) -> ok.
+disable_hibernate_timer(State) ->
+ case State#state.hibernate_timer of
+ Ref when is_reference(Ref) ->
+ p1_fsm:cancel_timer(Ref),
+ ok;
+ _ ->
+ ok
+ end.