1 |
|
%%%------------------------------------------------------------------- |
2 |
|
%%% @author Michal Ptaszek <michal.ptaszek@erlang-solutions.com> |
3 |
|
%%% @copyright (C) 2011, Erlang Solutions Ltd. |
4 |
|
%%% @doc Implementation of Mnesia-based session manager |
5 |
|
%%% |
6 |
|
%%% @end |
7 |
|
%%% Created : 17 Nov 2011 by Michal Ptaszek <michal.ptaszek@erlang-solutions.com> |
8 |
|
%%%------------------------------------------------------------------- |
9 |
|
-module(ejabberd_sm_mnesia). |
10 |
|
|
11 |
|
-behavior(ejabberd_sm_backend). |
12 |
|
|
13 |
|
-include("mongoose.hrl"). |
14 |
|
-include("session.hrl"). |
15 |
|
|
16 |
|
-export([init/1, |
17 |
|
get_sessions/0, |
18 |
|
get_sessions/1, |
19 |
|
get_sessions/2, |
20 |
|
get_sessions/3, |
21 |
|
create_session/4, |
22 |
|
update_session/4, |
23 |
|
delete_session/4, |
24 |
|
cleanup/1, |
25 |
|
total_count/0, |
26 |
|
unique_count/0]). |
27 |
|
|
28 |
|
-spec init(list()) -> any(). |
29 |
|
init(_Opts) -> |
30 |
80 |
mnesia:create_table(session, |
31 |
|
[{ram_copies, [node()]}, |
32 |
|
{attributes, record_info(fields, session)}]), |
33 |
80 |
mnesia:add_table_index(session, usr), |
34 |
80 |
mnesia:add_table_index(session, us), |
35 |
80 |
mnesia:add_table_copy(session, node(), ram_copies). |
36 |
|
|
37 |
|
-spec get_sessions() -> [ejabberd_sm:session()]. |
38 |
|
get_sessions() -> |
39 |
84 |
mnesia:activity(transaction, |
40 |
|
fun() -> |
41 |
84 |
mnesia:foldl(fun(Session, AccIn) -> [Session | AccIn] end, |
42 |
|
[], session) |
43 |
|
end). |
44 |
|
|
45 |
|
-spec get_sessions(jid:lserver()) -> [ejabberd_sm:session()]. |
46 |
|
get_sessions(Server) -> |
47 |
20 |
mnesia:dirty_select( |
48 |
|
session, |
49 |
|
[{#session{usr = '$1', sid='$2', priority='$3', info='$4', _ = '_' }, |
50 |
|
[{'==', {element, 2, '$1'}, Server}], |
51 |
|
['$_']}]). |
52 |
|
|
53 |
|
-spec get_sessions(jid:luser(), jid:lserver()) -> [ejabberd_sm:session()]. |
54 |
|
get_sessions(User, Server) -> |
55 |
18594 |
mnesia:dirty_index_read(session, {User, Server}, #session.us). |
56 |
|
|
57 |
|
-spec get_sessions(jid:luser(), jid:lserver(), jid:lresource() |
58 |
|
) -> [ejabberd_sm:session()]. |
59 |
|
get_sessions(User, Server, Resource) -> |
60 |
24850 |
mnesia:dirty_index_read(session, {User, Server, Resource}, #session.usr). |
61 |
|
|
62 |
|
-spec create_session(_User :: jid:luser(), |
63 |
|
_Server :: jid:lserver(), |
64 |
|
_Resource :: jid:lresource(), |
65 |
|
Session :: ejabberd_sm:session()) -> ok | {error, term()}. |
66 |
|
create_session(User, Server, Resource, Session) -> |
67 |
5354 |
case get_sessions(User, Server, Resource) of |
68 |
2649 |
[] -> mnesia:sync_dirty(fun() -> mnesia:write(Session) end); |
69 |
|
Sessions when is_list(Sessions) -> |
70 |
|
%% Fix potential race condition during XMPP bind, where |
71 |
|
%% multiple calls (> 2) to ejabberd_sm:open_session |
72 |
|
%% have been made, resulting in >1 sessions for this resource |
73 |
2705 |
MergedSession = mongoose_session:merge_info |
74 |
|
(Session, hd(lists:sort(Sessions))), |
75 |
2705 |
mnesia:sync_dirty(fun() -> mnesia:write(MergedSession) end) |
76 |
|
end. |
77 |
|
|
78 |
|
-spec update_session(_User :: jid:luser(), |
79 |
|
_Server :: jid:lserver(), |
80 |
|
_Resource :: jid:lresource(), |
81 |
|
Session :: ejabberd_sm:session()) -> ok | {error, term()}. |
82 |
|
update_session(_User, _Server, _Resource, Session) -> |
83 |
358 |
mnesia:sync_dirty(fun() -> mnesia:write(Session) end). |
84 |
|
|
85 |
|
-spec delete_session(ejabberd_sm:sid(), |
86 |
|
_User :: jid:luser(), |
87 |
|
_Server :: jid:lserver(), |
88 |
|
_Resource :: jid:lresource()) -> ok. |
89 |
|
delete_session(SID, _User, _Server, _Resource) -> |
90 |
2655 |
mnesia:sync_dirty(fun() -> |
91 |
2655 |
mnesia:delete({session, SID}) |
92 |
|
end). |
93 |
|
|
94 |
|
-spec cleanup(atom()) -> any(). |
95 |
|
cleanup(Node) -> |
96 |
:-( |
F = fun() -> |
97 |
:-( |
Es = mnesia:select( |
98 |
|
session, |
99 |
|
[{#session{sid = {'_', '$1'}, _ = '_'}, |
100 |
|
[{'==', {node, '$1'}, Node}], |
101 |
|
['$_']}]), |
102 |
:-( |
lists:foreach(fun(#session{sid = SID} = Session) -> |
103 |
:-( |
mnesia:delete({session, SID}), |
104 |
:-( |
ejabberd_sm:run_session_cleanup_hook(Session) |
105 |
|
end, Es) |
106 |
|
end, |
107 |
:-( |
mnesia:async_dirty(F). |
108 |
|
|
109 |
|
-spec total_count() -> integer(). |
110 |
|
total_count() -> |
111 |
96 |
mnesia:table_info(session, size). |
112 |
|
|
113 |
|
-spec unique_count() -> integer(). |
114 |
|
unique_count() -> |
115 |
94 |
compute_unique(mnesia:dirty_first(session), |
116 |
|
sets:new()). |
117 |
|
|
118 |
|
-spec compute_unique(term(), sets:set()) -> integer(). |
119 |
|
compute_unique('$end_of_table', Set) -> |
120 |
94 |
sets:size(Set); |
121 |
|
compute_unique(Key, Set) -> |
122 |
14 |
NewSet = case mnesia:dirty_read(session, Key) of |
123 |
|
[Session] -> |
124 |
14 |
sets:add_element(Session#session.us, Set); |
125 |
|
_ -> |
126 |
:-( |
Set |
127 |
|
end, |
128 |
14 |
compute_unique(mnesia:dirty_next(session, Key), NewSet). |