./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
:-(
case lists:keysearch(Session#session.sid, #session.sid, OldSessions) of
80 {value, OldSession} ->
81
:-(
BOldSession = term_to_binary(OldSession),
82
:-(
BSession = term_to_binary(Session),
83
:-(
mongoose_redis:cmds([["SADD", n(node()), hash(User, Server, Resource, Session#session.sid)],
84 ["SREM", hash(User, Server), BOldSession],
85 ["SREM", hash(User, Server, Resource), BOldSession],
86 ["SADD", hash(User, Server), BSession],
87 ["SADD", hash(User, Server, Resource), BSession]]);
88 false ->
89
:-(
BSession = term_to_binary(Session),
90
:-(
mongoose_redis:cmds([["SADD", n(node()), hash(User, Server, Resource, Session#session.sid)],
91 ["SADD", hash(User, Server), BSession],
92 ["SADD", hash(User, Server, Resource), BSession]])
93 end.
94
95 -spec delete_session(SID :: ejabberd_sm:sid(),
96 User :: jid:user(),
97 Server :: jid:server(),
98 Resource :: jid:resource()) -> ok.
99 delete_session(SID, User, Server, Resource) ->
100
:-(
Sessions = get_sessions(User, Server, Resource),
101
:-(
case lists:keysearch(SID, #session.sid, Sessions) of
102 {value, Session} ->
103
:-(
BSession = term_to_binary(Session),
104
:-(
mongoose_redis:cmds([["SREM", hash(User, Server), BSession],
105 ["SREM", hash(User, Server, Resource), BSession],
106 ["SREM", n(node()), hash(User, Server, Resource, SID)]]);
107 false ->
108
:-(
ok
109 end.
110
111 -spec cleanup(atom()) -> any().
112 cleanup(Node) ->
113
:-(
maybe_initial_cleanup(Node, false).
114
115 -spec maybe_initial_cleanup(atom(), boolean()) -> any().
116 maybe_initial_cleanup(Node, Initial) ->
117
:-(
Hashes = mongoose_redis:cmd(["SMEMBERS", n(Node)]),
118
:-(
mongoose_redis:cmd(["DEL", n(Node)]),
119
:-(
lists:foreach(fun(H) ->
120
:-(
[_, U, S, R | SIDEncoded] = re:split(H, ":"),
121 %% Add possible removed ":" from encoded SID
122
:-(
SID = binary_to_term(mongoose_bin:join(SIDEncoded, <<":">>)),
123
:-(
delete_session(SID, U, S, R),
124
:-(
case Initial of
125 true ->
126
:-(
ok;
127 false ->
128
:-(
ejabberd_sm:run_session_cleanup_hook(#session{usr = {U, S, R},
129 sid = SID})
130 end
131 end, Hashes).
132
133 -spec total_count() -> integer().
134 total_count() ->
135
:-(
{Counts, _} = rpc:multicall(ejabberd_sm, get_node_sessions_number, []),
136
:-(
lists:sum([Count || Count <- Counts, is_integer(Count)]).
137
138 -spec unique_count() -> integer().
139 unique_count() ->
140
:-(
length(mongoose_redis:cmd(["KEYS", "s2:*"])).
141
142 %% Internal functions
143
144 -spec hash(binary()) -> iolist().
145 hash(Val1) ->
146
:-(
["s3:*:", Val1, ":*"].
147
148 -spec hash(binary(), binary()) -> iolist().
149 hash(Val1, Val2) ->
150
:-(
["s2:", Val1, ":", Val2].
151
152 -spec hash(binary(), binary(), binary()) -> iolist().
153 hash(Val1, Val2, Val3) ->
154
:-(
["s3:", Val1, ":", Val2, ":", Val3].
155
156 -spec hash(binary(), binary(), binary(), ejabberd_sm:sid()) -> iolist().
157 hash(Val1, Val2, Val3, Val4) ->
158
:-(
["s4:", Val1, ":", Val2, ":", Val3, ":", term_to_binary(Val4)].
159
160 -spec n(atom()) -> iolist().
161 n(Node) ->
162
:-(
["n:", atom_to_list(Node)].
Line Hits Source