./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 42 {Elapsed, RetVal} = timer:tc(?MODULE, maybe_initial_cleanup, [node(), true]),
33 42 ?LOG_NOTICE(#{what => sm_cleanup_initial,
34 text => <<"SM cleanup on start took">>,
35 42 duration => erlang:round(Elapsed / 1000)}),
36 42 RetVal.
37
38 -spec get_sessions() -> [ejabberd_sm:session()].
39 get_sessions() ->
40 103 Keys = mongoose_redis:cmd(["KEYS", hash(<<"*">>)]),
41 103 lists:flatmap(fun(K) ->
42 37 Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
43 37 lists:map(fun(S) ->
44 37 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 81 Keys = mongoose_redis:cmd(["KEYS", hash(Server)]),
52 81 lists:flatmap(fun(K) ->
53 49 Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
54 49 lists:map(fun(S) ->
55 49 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 38545 Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server)]),
63
64 38545 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 41459 Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server, Resource)]),
70
71 41459 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 11261 OldSessions = get_sessions(User, Server, Resource),
79 11261 case lists:keysearch(Session#session.sid, #session.sid, OldSessions) of
80 {value, OldSession} ->
81 5383 BOldSession = term_to_binary(OldSession),
82 5383 BSession = term_to_binary(Session),
83 5383 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 5878 BSession = term_to_binary(Session),
90 5878 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 5878 Sessions = get_sessions(User, Server, Resource),
101 5878 case lists:keysearch(SID, #session.sid, Sessions) of
102 {value, Session} ->
103 5878 BSession = term_to_binary(Session),
104 5878 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 42 Hashes = mongoose_redis:cmd(["SMEMBERS", n(Node)]),
118 42 mongoose_redis:cmd(["DEL", n(Node)]),
119 42 lists:foreach(fun(H) ->
120 4 [_, U, S, R | SIDEncoded] = re:split(H, ":"),
121 %% Add possible removed ":" from encoded SID
122 4 SID = binary_to_term(mongoose_bin:join(SIDEncoded, <<":">>)),
123 4 delete_session(SID, U, S, R),
124 4 case Initial of
125 true ->
126 4 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 87 {Counts, _} = rpc:multicall(ejabberd_sm, get_node_sessions_number, []),
136 87 lists:sum([Count || Count <- Counts, is_integer(Count)]).
137
138 -spec unique_count() -> integer().
139 unique_count() ->
140 80 length(mongoose_redis:cmd(["KEYS", "s2:*"])).
141
142 %% Internal functions
143
144 -spec hash(binary()) -> iolist().
145 hash(Val1) ->
146 184 ["s3:*:", Val1, ":*"].
147
148 -spec hash(binary(), binary()) -> iolist().
149 hash(Val1, Val2) ->
150 61067 ["s2:", Val1, ":", Val2].
151
152 -spec hash(binary(), binary(), binary()) -> iolist().
153 hash(Val1, Val2, Val3) ->
154 63981 ["s3:", Val1, ":", Val2, ":", Val3].
155
156 -spec hash(binary(), binary(), binary(), ejabberd_sm:sid()) -> iolist().
157 hash(Val1, Val2, Val3, Val4) ->
158 17139 ["s4:", Val1, ":", Val2, ":", Val3, ":", term_to_binary(Val4)].
159
160 -spec n(atom()) -> iolist().
161 n(Node) ->
162 17223 ["n:", atom_to_list(Node)].
Line Hits Source