./ct_report/coverage/mod_muc_online_cets.COVER.html

1 -module(mod_muc_online_cets).
2 -behaviour(mod_muc_online_backend).
3
4 -export([start/2,
5 stop/1,
6 register_room/4,
7 room_destroyed/4,
8 find_room_pid/3,
9 get_online_rooms/2,
10 node_cleanup/2,
11 clear_table/1]).
12
13 -export([handle_conflict/2]).
14
15 -include_lib("mod_muc.hrl").
16
17 %% Use MucHost first for prefix select optimization in get_online_rooms
18 %% We also don't want to send HostType in muc_online_room.host_type between CETS nodes
19 %% or store it
20 -type muc_tuple() :: {{MucHost :: jid:lserver(), Room :: mod_muc:room()}, Pid :: pid()}.
21
22 table_name(HostType) ->
23
:-(
binary_to_atom(<<"cets_muc_online_room_", HostType/binary>>).
24
25 -spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
26 start(HostType, _Opts) ->
27
:-(
Tab = table_name(HostType),
28 %% Non-random, non-node-specific keys
29 %% This means that default merging would not work
30
:-(
cets:start(Tab, #{handle_conflict => fun ?MODULE:handle_conflict/2}),
31
:-(
cets_discovery:add_table(mongoose_cets_discovery, Tab),
32
:-(
ok.
33
34 -spec stop(mongooseim:host_type()) -> ok.
35 stop(HostType) ->
36
:-(
Tab = table_name(HostType),
37
:-(
cets_discovery:delete_table(mongoose_cets_discovery, Tab),
38
:-(
cets:stop(Tab).
39
40 %% We should keep one room and stop another room
41 %% But stopping logic needs to be tested heavily and designed
42 %% because we would need to figure out how to send presences to participants
43 %% (and maybe document how to rejoin the kicked room)
44 -spec handle_conflict(muc_tuple(), muc_tuple()) -> muc_tuple().
45 handle_conflict(Rec1, Rec2) when Rec1 > Rec2 ->
46
:-(
Rec1;
47 handle_conflict(_Rec1, Rec2) ->
48
:-(
Rec2.
49
50 -spec register_room(
51 HostType :: mongooseim:host_type(),
52 MucHost :: jid:lserver(),
53 Room :: mod_muc:room(),
54 Pid :: pid()) -> ok | {exists, pid()} | {error, term()}.
55 register_room(HostType, MucHost, Room, Pid) ->
56
:-(
register_room(HostType, MucHost, Room, Pid, 3).
57
58 register_room(_HostType, _MucHost, _Room, _Pid, 0) ->
59
:-(
{error, failed_to_register};
60 register_room(HostType, MucHost, Room, Pid, Retries) ->
61
:-(
Tab = table_name(HostType),
62
:-(
Rec = {{MucHost, Room}, Pid},
63
:-(
case find_room_pid(HostType, MucHost, Room) of
64 {ok, OtherPid} ->
65
:-(
{exists, OtherPid};
66 {error, not_found} ->
67
:-(
case cets:insert_new(Tab, Rec) of
68 true ->
69
:-(
ok;
70 false ->
71
:-(
register_room(HostType, MucHost, Room, Pid, Retries - 1)
72 end
73 end.
74
75 %% Race condition is possible between register and room_destroyed
76 %% (Because register is outside of the room process)
77 -spec room_destroyed(mongooseim:host_type(), jid:lserver(), mod_muc:room(), pid()) -> ok.
78 room_destroyed(HostType, MucHost, Room, Pid) ->
79
:-(
Tab = table_name(HostType),
80
:-(
Rec = {{MucHost, Room}, Pid},
81
:-(
cets:delete_object(Tab, Rec),
82
:-(
ok.
83
84 -spec find_room_pid(mongooseim:host_type(), jid:server(), mod_muc:room()) ->
85 {ok, pid()} | {error, not_found}.
86 find_room_pid(HostType, MucHost, Room) ->
87
:-(
Tab = table_name(HostType),
88
:-(
case ets:lookup(Tab, {MucHost, Room}) of
89 [{_, Pid}] ->
90
:-(
{ok, Pid};
91 [] ->
92
:-(
{error, not_found}
93 end.
94
95 %% This is used by MUC discovery but it is not very scalable.
96 %% This function should look like get_online_rooms(HostType, MucHost, AfterRoomName, Limit)
97 %% to reduce the load and still have pagination working.
98 -spec get_online_rooms(mongooseim:host_type(), jid:lserver()) ->
99 [mod_muc:muc_online_room()].
100 get_online_rooms(HostType, MucHost) ->
101
:-(
Tab = table_name(HostType),
102
:-(
[#muc_online_room{name_host = {Room, MucHost}, pid = Pid, host_type = HostType}
103
:-(
|| [Room, Pid] <- ets:match(Tab, {{MucHost, '$1'}, '$2'})].
104
105 -spec node_cleanup(mongooseim:host_type(), node()) -> ok.
106 node_cleanup(HostType, Node) ->
107
:-(
Tab = table_name(HostType),
108
:-(
Pattern = {'$1', '$2'},
109
:-(
Guard = {'==', {node, '$2'}, Node},
110
:-(
Keys = ets:select(Tab, [{Pattern, [Guard], ['$1']}]),
111
:-(
cets:delete_many(Tab, Keys).
112
113 %% Clear table for tests
114 -spec clear_table(mongooseim:host_type()) -> ok.
115 clear_table(HostType) ->
116
:-(
Tab = table_name(HostType),
117
:-(
ets:match_delete(Tab, '_'),
118
:-(
Nodes = cets:other_nodes(Tab),
119
:-(
[rpc:call(Node, ets, match_delete, [Tab, '_']) || Node <- Nodes],
120
:-(
ok.
Line Hits Source