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 |
4681 |
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 |
463 |
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 |
463 |
Tab = table_name(HostType), |
62 |
463 |
Rec = {{MucHost, Room}, Pid}, |
63 |
463 |
case find_room_pid(HostType, MucHost, Room) of |
64 |
|
{ok, OtherPid} -> |
65 |
3 |
{exists, OtherPid}; |
66 |
|
{error, not_found} -> |
67 |
460 |
case cets:insert_new(Tab, Rec) of |
68 |
|
true -> |
69 |
460 |
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 |
460 |
Tab = table_name(HostType), |
80 |
460 |
Rec = {{MucHost, Room}, Pid}, |
81 |
460 |
cets:delete_object(Tab, Rec), |
82 |
458 |
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 |
3620 |
Tab = table_name(HostType), |
88 |
3620 |
case ets:lookup(Tab, {MucHost, Room}) of |
89 |
|
[{_, Pid}] -> |
90 |
2838 |
{ok, Pid}; |
91 |
|
[] -> |
92 |
782 |
{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'}, |
109 |
:-( |
Guard = {'==', {node, '$1'}, Node}, |
110 |
:-( |
ets:select_delete(Tab, [{Pattern, [Guard], [true]}]), |
111 |
:-( |
ok. |
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. |