./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 set_session/4,
21 delete_session/4,
22 cleanup/1,
23 maybe_initial_cleanup/2,
24 total_count/0,
25 unique_count/0]).
26
27 -ignore_xref([maybe_initial_cleanup/2]).
28
29 -spec init(map()) -> any().
30 init(_Opts) ->
31 %% Clean current node's sessions from previous life
32
:-(
{Elapsed, RetVal} = timer:tc(?MODULE, maybe_initial_cleanup, [node(), true]),
33
:-(
?LOG_NOTICE(#{what => sm_cleanup_initial,
34 text => <<"SM cleanup on start took">>,
35
:-(
duration => erlang:round(Elapsed / 1000)}),
36
:-(
RetVal.
37
38 -spec get_sessions() -> [ejabberd_sm:session()].
39 get_sessions() ->
40
:-(
Keys = mongoose_redis:cmd(["KEYS", hash(<<"*">>)]),
41
:-(
lists:flatmap(fun(K) ->
42
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
43
:-(
lists:map(fun(S) ->
44
:-(
binary_to_term(S)
45 end,
46 Sessions)
47 end, Keys).
48
49 -spec get_sessions(jid:server()) -> [ejabberd_sm:session()].
50 get_sessions(Server) ->
51
:-(
Keys = mongoose_redis:cmd(["KEYS", hash(Server)]),
52
:-(
lists:flatmap(fun(K) ->
53
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
54
:-(
lists:map(fun(S) ->
55
:-(
binary_to_term(S)
56 end,
57 Sessions)
58 end, Keys).
59
60 -spec get_sessions(jid:user(), jid:server()) -> [ejabberd_sm:session()].
61 get_sessions(User, Server) ->
62
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server)]),
63
64
:-(
lists:map(fun(S) -> binary_to_term(S) end, Sessions).
65
66 -spec get_sessions(jid:user(), jid:server(), jid:resource()
67 ) -> [ejabberd_sm:session()].
68 get_sessions(User, Server, Resource) ->
69
:-(
Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server, Resource)]),
70
71
:-(
lists:map(fun(S) -> binary_to_term(S) end, Sessions).
72
73 -spec set_session(User :: jid:luser(),
74 Server :: jid:lserver(),
75 Resource :: jid:lresource(),
76 Session :: ejabberd_sm:session()) -> ok | {error, term()}.
77 set_session(User, Server, Resource, Session) ->
78
:-(
OldSessions = get_sessions(User, Server, Resource),
79
:-(
Node = sid_to_node(Session#session.sid),
80
:-(
case lists:keysearch(Session#session.sid, #session.sid, OldSessions) of
81 {value, OldSession} ->
82
:-(
BOldSession = term_to_binary(OldSession),
83
:-(
BSession = term_to_binary(Session),
84
:-(
mongoose_redis:cmds([["SADD", n(Node), hash(User, Server, Resource, Session#session.sid)],
85 ["SREM", hash(User, Server), BOldSession],
86 ["SREM", hash(User, Server, Resource), BOldSession],
87 ["SADD", hash(User, Server), BSession],
88 ["SADD", hash(User, Server, Resource), BSession]]);
89 false ->
90
:-(
BSession = term_to_binary(Session),
91
:-(
mongoose_redis:cmds([["SADD", n(Node), hash(User, Server, Resource, Session#session.sid)],
92 ["SADD", hash(User, Server), BSession],
93 ["SADD", hash(User, Server, Resource), BSession]])
94 end.
95
96 -spec delete_session(SID :: ejabberd_sm:sid(),
97 User :: jid:user(),
98 Server :: jid:server(),
99 Resource :: jid:resource()) -> ok.
100 delete_session(SID, User, Server, Resource) ->
101
:-(
Sessions = get_sessions(User, Server, Resource),
102
:-(
case lists:keysearch(SID, #session.sid, Sessions) of
103 {value, Session} ->
104
:-(
BSession = term_to_binary(Session),
105
:-(
mongoose_redis:cmds([["SREM", hash(User, Server), BSession],
106 ["SREM", hash(User, Server, Resource), BSession],
107 ["SREM", n(sid_to_node(SID)), hash(User, Server, Resource, SID)]]);
108 false ->
109
:-(
ok
110 end.
111
112 -spec cleanup(atom()) -> any().
113 cleanup(Node) ->
114
:-(
maybe_initial_cleanup(Node, false).
115
116 -spec maybe_initial_cleanup(atom(), boolean()) -> any().
117 maybe_initial_cleanup(Node, Initial) ->
118
:-(
Hashes = mongoose_redis:cmd(["SMEMBERS", n(Node)]),
119
:-(
mongoose_redis:cmd(["DEL", n(Node)]),
120
:-(
Sessions = lists:map(fun(H) ->
121
:-(
[_, U, S, R | SIDEncoded] = re:split(H, ":"),
122 %% Add possible removed ":" from encoded SID
123
:-(
SID = binary_to_term(mongoose_bin:join(SIDEncoded, <<":">>)),
124
:-(
delete_session(SID, U, S, R),
125
:-(
case Initial of
126 true ->
127
:-(
ok;
128 false ->
129
:-(
Session = #session{usr = {U, S, R}, sid = SID},
130
:-(
ejabberd_sm:session_cleanup(Session),
131
:-(
Session
132 end
133 end, Hashes),
134
:-(
ejabberd_sm:sessions_cleanup(Sessions).
135
136 -spec total_count() -> integer().
137 total_count() ->
138
:-(
{Counts, _} = rpc:multicall(ejabberd_sm, get_node_sessions_number, []),
139
:-(
lists:sum([Count || Count <- Counts, is_integer(Count)]).
140
141 -spec unique_count() -> integer().
142 unique_count() ->
143
:-(
length(mongoose_redis:cmd(["KEYS", "s2:*"])).
144
145 %% Internal functions
146
147 -spec hash(binary()) -> iolist().
148 hash(Val1) ->
149
:-(
["s3:*:", Val1, ":*"].
150
151 -spec hash(binary(), binary()) -> iolist().
152 hash(Val1, Val2) ->
153
:-(
["s2:", Val1, ":", Val2].
154
155 -spec hash(binary(), binary(), binary()) -> iolist().
156 hash(Val1, Val2, Val3) ->
157
:-(
["s3:", Val1, ":", Val2, ":", Val3].
158
159 -spec hash(binary(), binary(), binary(), ejabberd_sm:sid()) -> iolist().
160 hash(Val1, Val2, Val3, Val4) ->
161
:-(
["s4:", Val1, ":", Val2, ":", Val3, ":", term_to_binary(Val4)].
162
163 -spec n(atom()) -> iolist().
164 n(Node) ->
165
:-(
["n:", atom_to_list(Node)].
166
167 sid_to_node(SID) ->
168
:-(
{_, Pid} = SID,
169
:-(
node(Pid).
Line Hits Source