./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 5140 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 38 Tab = table_name(HostType),
28 %% Non-random, non-node-specific keys
29 %% This means that default merging would not work
30 38 cets:start(Tab, #{handle_conflict => fun ?MODULE:handle_conflict/2}),
31 38 cets_discovery:add_table(mongoose_cets_discovery, Tab),
32 38 ok.
33
34 -spec stop(mongooseim:host_type()) -> ok.
35 stop(HostType) ->
36 38 Tab = table_name(HostType),
37 38 cets_discovery:delete_table(mongoose_cets_discovery, Tab),
38 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 528 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 528 Tab = table_name(HostType),
62 528 Rec = {{MucHost, Room}, Pid},
63 528 case find_room_pid(HostType, MucHost, Room) of
64 {ok, OtherPid} ->
65 43 {exists, OtherPid};
66 {error, not_found} ->
67 485 case cets:insert_new(Tab, Rec) of
68 true ->
69 485 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 524 Tab = table_name(HostType),
80 524 Rec = {{MucHost, Room}, Pid},
81 524 cets:delete_object(Tab, Rec),
82 523 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 3950 Tab = table_name(HostType),
88 3950 case ets:lookup(Tab, {MucHost, Room}) of
89 [{_, Pid}] ->
90 3122 {ok, Pid};
91 [] ->
92 828 {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 44 Tab = table_name(HostType),
102 44 [#muc_online_room{name_host = {Room, MucHost}, pid = Pid, host_type = HostType}
103 44 || [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 18 Tab = table_name(HostType),
117 18 ets:match_delete(Tab, '_'),
118 18 Nodes = cets:other_nodes(Tab),
119 18 [rpc:call(Node, ets, match_delete, [Tab, '_']) || Node <- Nodes],
120 18 ok.
Line Hits Source