./ct_report/coverage/ejabberd_sm_cets.COVER.html

1 -module(ejabberd_sm_cets).
2
3 -behavior(ejabberd_sm_backend).
4
5 -include("mongoose.hrl").
6 -include("session.hrl").
7
8 -export([init/1,
9 get_sessions/0,
10 get_sessions/1,
11 get_sessions/2,
12 get_sessions/3,
13 set_session/4,
14 delete_session/4,
15 cleanup/1,
16 total_count/0,
17 unique_count/0]).
18
19 -define(TABLE, cets_session).
20
21 -spec init(map()) -> any().
22 init(_Opts) ->
23
:-(
cets:start(?TABLE, #{}),
24
:-(
cets_discovery:add_table(mongoose_cets_discovery, ?TABLE).
25
26 -spec get_sessions() -> [ejabberd_sm:session()].
27 get_sessions() ->
28
:-(
tuples_to_sessions(ets:tab2list(?TABLE)).
29
30 -spec get_sessions(jid:lserver()) -> [ejabberd_sm:session()].
31 get_sessions(Server) ->
32 %% This is not a full table scan. From the ETS docs:
33 %% For ordered_set a partially bound key will limit the traversal to only
34 %% scan a subset of the table based on term order.
35 %% A partially bound key is either a list or a tuple with
36 %% a prefix that is fully bound.
37
:-(
R = {{Server, '_', '_', '_'}, '_', '_'},
38
:-(
Xs = ets:match_object(?TABLE, R),
39
:-(
tuples_to_sessions(Xs).
40
41 -spec get_sessions(jid:luser(), jid:lserver()) -> [ejabberd_sm:session()].
42 get_sessions(User, Server) ->
43
:-(
R = {{Server, User, '_', '_'}, '_', '_'},
44
:-(
Xs = ets:match_object(?TABLE, R),
45
:-(
tuples_to_sessions(Xs).
46
47 -spec get_sessions(jid:luser(), jid:lserver(), jid:lresource()) ->
48 [ejabberd_sm:session()].
49 get_sessions(User, Server, Resource) ->
50
:-(
R = {{Server, User, Resource, '_'}, '_', '_'},
51
:-(
Xs = ets:match_object(?TABLE, R),
52 %% TODO these sessions should be deduplicated.
53 %% It is possible, that after merging two cets tables we could end up
54 %% with sessions from two nodes for the same full jid.
55 %% One of the sessions must be killed.
56 %% We can detect duplicates on the merging step or on reading (or both).
57
:-(
tuples_to_sessions(Xs).
58
59 -spec set_session(User :: jid:luser(),
60 Server :: jid:lserver(),
61 Resource :: jid:lresource(),
62 Session :: ejabberd_sm:session()) -> ok | {error, term()}.
63 set_session(_User, _Server, _Resource, Session) ->
64
:-(
cets:insert(?TABLE, session_to_tuple(Session)).
65
66 -spec delete_session(SID :: ejabberd_sm:sid(),
67 User :: jid:luser(),
68 Server :: jid:lserver(),
69 Resource :: jid:lresource()) -> ok.
70 delete_session(SID, User, Server, Resource) ->
71
:-(
cets:delete(?TABLE, make_key(User, Server, Resource, SID)).
72
73 %% cleanup is called on each node in the cluster, when Node is down
74 -spec cleanup(atom()) -> any().
75 cleanup(Node) ->
76
:-(
KeyPattern = {'_', '_', '_', {'_', '$1'}},
77
:-(
Guard = {'==', {node, '$1'}, Node},
78
:-(
R = {KeyPattern, '_', '_'},
79
:-(
cets:ping_all(?TABLE),
80 %% This is a full table scan, but cleanup is rare.
81
:-(
Tuples = ets:select(?TABLE, [{R, [Guard], ['$_']}]),
82
:-(
cets:delete_many(?TABLE, [Key || {Key, _, _} <- Tuples]),
83
:-(
lists:foreach(fun(Tuple) ->
84
:-(
Session = tuple_to_session(Tuple),
85
:-(
ejabberd_sm:run_session_cleanup_hook(Session)
86 end, Tuples).
87
88 -spec total_count() -> integer().
89 total_count() ->
90
:-(
ets:info(?TABLE, size).
91
92 %% Counts merged by US
93 -spec unique_count() -> integer().
94 unique_count() ->
95
:-(
compute_unique(ets:first(?TABLE), 0).
96
97 compute_unique('$end_of_table', Sum) ->
98
:-(
Sum;
99 compute_unique({S, U, _, _} = Key, Sum) ->
100
:-(
Key2 = ets:next(?TABLE, Key),
101
:-(
case Key2 of
102 {S, U, _, _} ->
103
:-(
compute_unique(Key2, Sum);
104 _ ->
105
:-(
compute_unique(Key2, Sum + 1)
106 end.
107
108 session_to_tuple(#session{sid = SID, usr = {U, S, R}, priority = Prio, info = Info}) ->
109
:-(
{make_key(U, S, R, SID), Prio, Info}.
110
111 make_key(User, Server, Resource, SID) ->
112
:-(
{Server, User, Resource, SID}.
113
114 tuple_to_session({{S, U, R, SID}, Prio, Info}) ->
115
:-(
#session{sid = SID, usr = {U, S, R}, us = {U, S}, priority = Prio, info = Info}.
116
117 tuples_to_sessions(Xs) ->
118
:-(
[tuple_to_session(X) || X <- Xs].
Line Hits Source