./ct_report/coverage/ejabberd_sm_redis.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @author Konrad Kaplita <konrad.kaplita@erlang-solutions.com>
3 %%% @copyright (C) 2011, Erlang Solutions Ltd.
4 %%% @doc Implementation of Redis-based session manager
5 %%%
6 %%% @end
7 %%% Created : 17 Nov 2011 by Konrad Kaplita <konrad.kaplita@erlang-solutions.com>
8 %%%-------------------------------------------------------------------
9 -module(ejabberd_sm_redis).
10
11 -include("mongoose.hrl").
12 -include("session.hrl").
13
14 -behavior(ejabberd_sm_backend).
15 -export([init/1,
16 get_sessions/0,
17 get_sessions/1,
18 get_sessions/2,
19 get_sessions/3,
20 create_session/4,
21 update_session/4,
22 delete_session/4,
23 cleanup/1,
24 maybe_initial_cleanup/2,
25 total_count/0,
26 unique_count/0]).
27
28 -ignore_xref([maybe_initial_cleanup/2]).
29
30 -spec init(list()) -> any().
31 init(_Opts) ->
32 %% Clean current node's sessions from previous life
33
:-(
{Elapsed, RetVal} = timer:tc(?MODULE, maybe_initial_cleanup, [node(), true]),
34
:-(
?LOG_NOTICE(#{what => sm_cleanup_initial,
35 text => <<"SM cleanup on start took">>,
36
:-(
duration => erlang:round(Elapsed / 1000)}),
37
:-(
RetVal.
38
39 -spec get_sessions() -> [ejabberd_sm:session()].
40 get_sessions() ->
41
:-(
Keys = mongoose_redis:cmd(["KEYS", hash(<<"*">>)]),
42
:-(
lists:flatmap(fun(K) ->
43
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
44
:-(
lists:map(fun(S) ->
45
:-(
binary_to_term(S)
46 end,
47 Sessions)
48 end, Keys).
49
50 -spec get_sessions(jid:server()) -> [ejabberd_sm:session()].
51 get_sessions(Server) ->
52
:-(
Keys = mongoose_redis:cmd(["KEYS", hash(Server)]),
53
:-(
lists:flatmap(fun(K) ->
54
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
55
:-(
lists:map(fun(S) ->
56
:-(
binary_to_term(S)
57 end,
58 Sessions)
59 end, Keys).
60
61 -spec get_sessions(jid:user(), jid:server()) -> [ejabberd_sm:session()].
62 get_sessions(User, Server) ->
63
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server)]),
64
65
:-(
lists:map(fun(S) -> binary_to_term(S) end, Sessions).
66
67 -spec get_sessions(jid:user(), jid:server(), jid:resource()
68 ) -> [ejabberd_sm:session()].
69 get_sessions(User, Server, Resource) ->
70
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server, Resource)]),
71
72
:-(
lists:map(fun(S) -> binary_to_term(S) end, Sessions).
73
74 -spec create_session(User :: jid:luser(),
75 Server :: jid:lserver(),
76 Resource :: jid:lresource(),
77 Session :: ejabberd_sm:session()) -> ok | {error, term()}.
78 create_session(User, Server, Resource, Session) ->
79
:-(
OldSessions = get_sessions(User, Server, Resource),
80
:-(
case lists:keysearch(Session#session.sid, #session.sid, OldSessions) of
81 {value, OldSession} ->
82
:-(
MergedInfoSession = mongoose_session:merge_info(Session, OldSession),
83
:-(
BOldSession = term_to_binary(OldSession),
84
:-(
BSession = term_to_binary(MergedInfoSession),
85
:-(
mongoose_redis:cmds([["SADD", n(node()), hash(User, Server, Resource, Session#session.sid)],
86 ["SREM", hash(User, Server), BOldSession],
87 ["SREM", hash(User, Server, Resource), BOldSession],
88 ["SADD", hash(User, Server), BSession],
89 ["SADD", hash(User, Server, Resource), BSession]]);
90 false ->
91
:-(
BSession = term_to_binary(Session),
92
:-(
mongoose_redis:cmds([["SADD", n(node()), hash(User, Server, Resource, Session#session.sid)],
93 ["SADD", hash(User, Server), BSession],
94 ["SADD", hash(User, Server, Resource), BSession]])
95 end.
96
97 -spec update_session(User :: jid:luser(),
98 Server :: jid:lserver(),
99 Resource :: jid:lresource(),
100 Session :: ejabberd_sm:session()) -> ok | {error, term()}.
101 update_session(User, Server, Resource, Session) ->
102
:-(
OldSessions = get_sessions(User, Server, Resource),
103
:-(
case lists:keysearch(Session#session.sid, #session.sid, OldSessions) of
104 {value, OldSession} ->
105
:-(
BOldSession = term_to_binary(OldSession),
106
:-(
BSession = term_to_binary(Session),
107
:-(
mongoose_redis:cmds([["SADD", n(node()), hash(User, Server, Resource, Session#session.sid)],
108 ["SREM", hash(User, Server), BOldSession],
109 ["SREM", hash(User, Server, Resource), BOldSession],
110 ["SADD", hash(User, Server), BSession],
111 ["SADD", hash(User, Server, Resource), BSession]]);
112 false ->
113
:-(
BSession = term_to_binary(Session),
114
:-(
mongoose_redis:cmds([["SADD", n(node()), hash(User, Server, Resource, Session#session.sid)],
115 ["SADD", hash(User, Server), BSession],
116 ["SADD", hash(User, Server, Resource), BSession]])
117 end.
118
119 -spec delete_session(SID :: ejabberd_sm:sid(),
120 User :: jid:user(),
121 Server :: jid:server(),
122 Resource :: jid:resource()) -> ok.
123 delete_session(SID, User, Server, Resource) ->
124
:-(
Sessions = get_sessions(User, Server, Resource),
125
:-(
case lists:keysearch(SID, #session.sid, Sessions) of
126 {value, Session} ->
127
:-(
BSession = term_to_binary(Session),
128
:-(
mongoose_redis:cmds([["SREM", hash(User, Server), BSession],
129 ["SREM", hash(User, Server, Resource), BSession],
130 ["SREM", n(node()), hash(User, Server, Resource, SID)]]);
131 false ->
132
:-(
ok
133 end.
134
135 -spec cleanup(atom()) -> any().
136 cleanup(Node) ->
137
:-(
maybe_initial_cleanup(Node, false).
138
139 -spec maybe_initial_cleanup(atom(), boolean()) -> any().
140 maybe_initial_cleanup(Node, Initial) ->
141
:-(
Hashes = mongoose_redis:cmd(["SMEMBERS", n(Node)]),
142
:-(
mongoose_redis:cmd(["DEL", n(Node)]),
143
:-(
lists:foreach(fun(H) ->
144
:-(
[_, U, S, R | SIDEncoded] = re:split(H, ":"),
145 %% Add possible removed ":" from encoded SID
146
:-(
SID = binary_to_term(mongoose_bin:join(SIDEncoded, <<":">>)),
147
:-(
delete_session(SID, U, S, R),
148
:-(
case Initial of
149 true ->
150
:-(
ok;
151 false ->
152
:-(
ejabberd_sm:run_session_cleanup_hook(#session{usr = {U, S, R},
153 sid = SID})
154 end
155 end, Hashes).
156
157 -spec total_count() -> integer().
158 total_count() ->
159
:-(
{Counts, _} = rpc:multicall(supervisor, count_children, [ejabberd_c2s_sup]),
160
:-(
lists:sum([proplists:get_value(active, Count, 0) || Count <- Counts, is_list(Count)]).
161
162 -spec unique_count() -> integer().
163 unique_count() ->
164
:-(
length(mongoose_redis:cmd(["KEYS", "s2:*"])).
165
166 %% Internal functions
167
168 -spec hash(binary()) -> iolist().
169 hash(Val1) ->
170
:-(
["s3:*:", Val1, ":*"].
171
172 -spec hash(binary(), binary()) -> iolist().
173 hash(Val1, Val2) ->
174
:-(
["s2:", Val1, ":", Val2].
175
176 -spec hash(binary(), binary(), binary()) -> iolist().
177 hash(Val1, Val2, Val3) ->
178
:-(
["s3:", Val1, ":", Val2, ":", Val3].
179
180 -spec hash(binary(), binary(), binary(), ejabberd_sm:sid()) -> iolist().
181 hash(Val1, Val2, Val3, Val4) ->
182
:-(
["s4:", Val1, ":", Val2, ":", Val3, ":", term_to_binary(Val4)].
183
184 -spec n(atom()) -> iolist().
185 n(Node) ->
186
:-(
["n:", atom_to_list(Node)].
Line Hits Source