./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 53 {Elapsed, RetVal} = timer:tc(?MODULE, maybe_initial_cleanup, [node(), true]),
33 53 ?LOG_NOTICE(#{what => sm_cleanup_initial,
34 text => <<"SM cleanup on start took">>,
35 53 duration => erlang:round(Elapsed / 1000)}),
36 53 RetVal.
37
38 -spec get_sessions() -> [ejabberd_sm:session()].
39 get_sessions() ->
40 119 Keys = mongoose_redis:cmd(["KEYS", hash(<<"*">>)]),
41 119 lists:flatmap(fun(K) ->
42 42 Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
43 42 lists:map(fun(S) ->
44 42 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 66 Keys = mongoose_redis:cmd(["KEYS", hash(Server)]),
52 66 lists:flatmap(fun(K) ->
53 54 Sessions = mongoose_redis:cmd(["SMEMBERS", K]),
54 54 lists:map(fun(S) ->
55 52 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 44272 Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server)]),
63
64 44272 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 46350 Sessions = mongoose_redis:cmd(["SMEMBERS", hash(User, Server, Resource)]),
70
71 46350 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 12990 OldSessions = get_sessions(User, Server, Resource),
79 12990 case lists:keysearch(Session#session.sid, #session.sid, OldSessions) of
80 {value, OldSession} ->
81 6383 BOldSession = term_to_binary(OldSession),
82 6383 BSession = term_to_binary(Session),
83 6383 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 6607 BSession = term_to_binary(Session),
90 6607 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 6609 Sessions = get_sessions(User, Server, Resource),
101 6609 case lists:keysearch(SID, #session.sid, Sessions) of
102 {value, Session} ->
103 6607 BSession = term_to_binary(Session),
104 6607 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 2 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 53 Hashes = mongoose_redis:cmd(["SMEMBERS", n(Node)]),
118 53 mongoose_redis:cmd(["DEL", n(Node)]),
119 53 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 121 {Counts, _} = rpc:multicall(ejabberd_sm, get_node_sessions_number, []),
136 121 lists:sum([Count || Count <- Counts, is_integer(Count)]).
137
138 -spec unique_count() -> integer().
139 unique_count() ->
140 114 length(mongoose_redis:cmd(["KEYS", "s2:*"])).
141
142 %% Internal functions
143
144 -spec hash(binary()) -> iolist().
145 hash(Val1) ->
146 185 ["s3:*:", Val1, ":*"].
147
148 -spec hash(binary(), binary()) -> iolist().
149 hash(Val1, Val2) ->
150 70252 ["s2:", Val1, ":", Val2].
151
152 -spec hash(binary(), binary(), binary()) -> iolist().
153 hash(Val1, Val2, Val3) ->
154 72330 ["s3:", Val1, ":", Val2, ":", Val3].
155
156 -spec hash(binary(), binary(), binary(), ejabberd_sm:sid()) -> iolist().
157 hash(Val1, Val2, Val3, Val4) ->
158 19597 ["s4:", Val1, ":", Val2, ":", Val3, ":", term_to_binary(Val4)].
159
160 -spec n(atom()) -> iolist().
161 n(Node) ->
162 19703 ["n:", atom_to_list(Node)].
Line Hits Source