Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 158 lines (132 sloc) 7.676 kb
e7bc02e edoc annotations in src comments
Tony Hannan authored
1 %@doc Get connection to appropriate server in a replica set
3728f60 replica set support
Tony Hannan authored
2 -module (mongo_replset).
3
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
4 -export_type ([replset/0, rs_connection/0]).
cbd5489 connect with timeout
Tony Hannan authored
5 -export ([connect/1, connect/2, primary/1, secondary_ok/1, close/1, is_closed/1]). % API
3728f60 replica set support
Tony Hannan authored
6
7 -type maybe(A) :: {A} | {}.
8 -type err_or(A) :: {ok, A} | {error, reason()}.
9 -type reason() :: any().
10
11 % -spec find (fun ((A) -> boolean()), [A]) -> maybe(A).
12 % return first element in list that satisfies predicate
13 % find (Pred, []) -> {};
14 % find (Pred, [A | Tail]) -> case Pred (A) of true -> {A}; false -> find (Pred, Tail) end.
15
16 -spec until_success ([A], fun ((A) -> B)) -> B. % EIO, fun EIO
e7bc02e edoc annotations in src comments
Tony Hannan authored
17 %@doc Apply fun on each element until one doesn't fail. Fail if all fail or list is empty
3728f60 replica set support
Tony Hannan authored
18 until_success ([], _Fun) -> throw ([]);
19 until_success ([A | Tail], Fun) -> try Fun (A)
20 catch Reason -> try until_success (Tail, Fun)
21 catch Reasons -> throw ([Reason | Reasons]) end end.
22
23 -spec rotate (integer(), [A]) -> [A].
e7bc02e edoc annotations in src comments
Tony Hannan authored
24 %@doc Move first N element of list to back of list
3728f60 replica set support
Tony Hannan authored
25 rotate (N, List) ->
26 {Front, Back} = lists:split (N, List),
27 Back ++ Front.
28
29 -type host() :: mongo_connect:host().
30 -type connection() :: mongo_connect:connection().
31
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
32 -type replset() :: {rs_name(), [host()]}.
3728f60 replica set support
Tony Hannan authored
33 % Identify replset. Hosts is just seed list, not necessarily all hosts in replica set
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
34 -type rs_name() :: bson:utf8().
3728f60 replica set support
Tony Hannan authored
35
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
36 -spec connect (replset()) -> rs_connection(). % IO
e7bc02e edoc annotations in src comments
Tony Hannan authored
37 %@doc Create new cache of connections to replica set members starting with seed members. No connection attempted until primary or secondary_ok called.
cbd5489 connect with timeout
Tony Hannan authored
38 connect (ReplSet) -> connect (ReplSet, infinity).
39
40 -spec connect (replset(), timeout()) -> rs_connection(). % IO
116b695 Timeout on receive too
Tony Hannan authored
41 %@doc Create new cache of connections to replica set members starting with seed members. No connection attempted until primary or secondary_ok called. Timeout used for initial connection and every call.
cbd5489 connect with timeout
Tony Hannan authored
42 connect ({ReplName, Hosts}, TimeoutMS) ->
3728f60 replica set support
Tony Hannan authored
43 Dict = dict:from_list (lists:map (fun (Host) -> {mongo_connect:host_port (Host), {}} end, Hosts)),
cbd5489 connect with timeout
Tony Hannan authored
44 {rs_connection, ReplName, mvar:new (Dict), TimeoutMS}.
3728f60 replica set support
Tony Hannan authored
45
cbd5489 connect with timeout
Tony Hannan authored
46 -opaque rs_connection() :: {rs_connection, rs_name(), mvar:mvar(connections()), timeout()}.
47 % Maintains set of connections to some if not all of the replica set members. Opaque except to mongo:connect_mode
187d967 mongo:do accepts connnection() or rs_connection()
Tony Hannan authored
48 % Type not opaque to mongo:connection_mode/2
3728f60 replica set support
Tony Hannan authored
49 -type connections() :: dict:dictionary (host(), maybe(connection())).
50 % All hosts listed in last member_info fetched are keys in dict. Value is {} if no attempt to connect to that host yet
51
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
52 -spec primary (rs_connection()) -> err_or(connection()). % IO
e7bc02e edoc annotations in src comments
Tony Hannan authored
53 %@doc Return connection to current primary in replica set
3728f60 replica set support
Tony Hannan authored
54 primary (ReplConn) -> try
55 MemberInfo = fetch_member_info (ReplConn),
56 primary_conn (2, ReplConn, MemberInfo)
57 of Conn -> {ok, Conn}
58 catch Reason -> {error, Reason} end.
59
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
60 -spec secondary_ok (rs_connection()) -> err_or(connection()). % IO
e7bc02e edoc annotations in src comments
Tony Hannan authored
61 %@doc Return connection to a current secondary in replica set or primary if none
3728f60 replica set support
Tony Hannan authored
62 secondary_ok (ReplConn) -> try
63 {_Conn, Info} = fetch_member_info (ReplConn),
64 Hosts = lists:map (fun mongo_connect:read_host/1, bson:at (hosts, Info)),
65 R = random:uniform (length (Hosts)) - 1,
66 secondary_ok_conn (ReplConn, rotate (R, Hosts))
67 of Conn -> {ok, Conn}
68 catch Reason -> {error, Reason} end.
69
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
70 -spec close (rs_connection()) -> ok. % IO
e7bc02e edoc annotations in src comments
Tony Hannan authored
71 %@doc Close replset connection
cbd5489 connect with timeout
Tony Hannan authored
72 close ({rs_connection, _, VConns, _}) ->
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
73 CloseConn = fun (_, MCon, _) -> case MCon of {Con} -> mongo_connect:close (Con); {} -> ok end end,
74 mvar:with (VConns, fun (Dict) -> dict:fold (CloseConn, ok, Dict) end),
75 mvar:terminate (VConns).
76
77 -spec is_closed (rs_connection()) -> boolean(). % IO
e7bc02e edoc annotations in src comments
Tony Hannan authored
78 %@doc Has replset connection been closed?
cbd5489 connect with timeout
Tony Hannan authored
79 is_closed ({rs_connection, _, VConns, _}) -> mvar:is_terminated (VConns).
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
80
3728f60 replica set support
Tony Hannan authored
81 % EIO = IO that may throw error of any type
82
83 -type member_info() :: {connection(), bson:document()}.
84 % Result of isMaster query on a server connnection. Returned fields are: setName, ismaster, secondary, hosts, [primary]. primary only present when ismaster = false
85
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
86 -spec primary_conn (integer(), rs_connection(), member_info()) -> connection(). % EIO
e7bc02e edoc annotations in src comments
Tony Hannan authored
87 %@doc Return connection to primary designated in member_info. Only chase primary pointer N times.
3728f60 replica set support
Tony Hannan authored
88 primary_conn (0, _ReplConn, MemberInfo) -> throw ({false_primary, MemberInfo});
89 primary_conn (Tries, ReplConn, {Conn, Info}) -> case bson:at (ismaster, Info) of
90 true -> Conn;
91 false -> case bson:lookup (primary, Info) of
92 {HostString} ->
93 MemberInfo = connect_member (ReplConn, mongo_connect:read_host (HostString)),
94 primary_conn (Tries - 1, ReplConn, MemberInfo);
95 {} -> throw ({no_primary, {Conn, Info}}) end end.
96
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
97 -spec secondary_ok_conn (rs_connection(), [host()]) -> connection(). % EIO
e7bc02e edoc annotations in src comments
Tony Hannan authored
98 %@doc Return connection to a live secondaries in replica set, or primary if none
3728f60 replica set support
Tony Hannan authored
99 secondary_ok_conn (ReplConn, Hosts) -> try
100 until_success (Hosts, fun (Host) ->
101 {Conn, Info} = connect_member (ReplConn, Host),
102 case bson:at (secondary, Info) of true -> Conn; false -> throw (not_secondary) end end)
103 catch _ -> primary_conn (2, ReplConn, fetch_member_info (ReplConn)) end.
104
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
105 -spec fetch_member_info (rs_connection()) -> member_info(). % EIO
e7bc02e edoc annotations in src comments
Tony Hannan authored
106 %@doc Retrieve isMaster info from a current known member in replica set. Update known list of members from fetched info.
cbd5489 connect with timeout
Tony Hannan authored
107 fetch_member_info (ReplConn = {rs_connection, _ReplName, VConns, _}) ->
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
108 OldHosts_ = dict:fetch_keys (mvar:read (VConns)),
109 {Conn, Info} = until_success (OldHosts_, fun (Host) -> connect_member (ReplConn, Host) end),
110 OldHosts = sets:from_list (OldHosts_),
111 NewHosts = sets:from_list (lists:map (fun mongo_connect:read_host/1, bson:at (hosts, Info))),
6a61801 small replica-set fix
Tony Hannan authored
112 RemovedHosts = sets:subtract (OldHosts, NewHosts),
113 AddedHosts = sets:subtract (NewHosts, OldHosts),
3728f60 replica set support
Tony Hannan authored
114 mvar:modify_ (VConns, fun (Dict) ->
6a61801 small replica-set fix
Tony Hannan authored
115 Dict1 = sets:fold (fun remove_host/2, Dict, RemovedHosts),
116 Dict2 = sets:fold (fun add_host/2, Dict1, AddedHosts),
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
117 Dict2 end),
6a61801 small replica-set fix
Tony Hannan authored
118 case sets:is_element (mongo_connect:conn_host (Conn), RemovedHosts) of
119 false -> {Conn, Info};
120 true -> % Conn connected to member but under wrong name (eg. localhost instead of 127.0.0.1) so it was closed and removed because it did not match a host in isMaster info. Reconnect using correct name.
121 Hosts = dict:fetch_keys (mvar:read (VConns)),
122 until_success (Hosts, fun (Host) -> connect_member (ReplConn, Host) end) end.
3728f60 replica set support
Tony Hannan authored
123
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
124 add_host (Host, Dict) -> dict:store (Host, {}, Dict).
125
126 remove_host (Host, Dict) ->
127 MConn = dict:fetch (Host, Dict),
128 Dict1 = dict:erase (Host, Dict),
129 case MConn of {Conn} -> mongo_connect:close (Conn); {} -> ok end,
130 Dict1.
131
b802070 is_closed. mongo: replica sets & pools
Tony Hannan authored
132 -spec connect_member (rs_connection(), host()) -> member_info(). % EIO
e7bc02e edoc annotations in src comments
Tony Hannan authored
133 %@doc Connect to host and verify membership. Cache connection in rs_connection
cbd5489 connect with timeout
Tony Hannan authored
134 connect_member ({rs_connection, ReplName, VConns, TimeoutMS}, Host) ->
135 Conn = get_connection (VConns, Host, TimeoutMS),
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
136 Info = try get_member_info (Conn) catch _ ->
137 mongo_connect:close (Conn),
cbd5489 connect with timeout
Tony Hannan authored
138 Conn1 = get_connection (VConns, Host, TimeoutMS),
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
139 get_member_info (Conn1) end,
3728f60 replica set support
Tony Hannan authored
140 case bson:at (setName, Info) of
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
141 ReplName -> {Conn, Info};
142 _ ->
143 mongo_connect:close (Conn),
144 throw ({not_member, ReplName, Host, Info}) end.
145
cbd5489 connect with timeout
Tony Hannan authored
146 get_connection (VConns, Host, TimeoutMS) -> mvar:modify (VConns, fun (Dict) ->
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
147 case dict:find (Host, Dict) of
148 {ok, {Conn}} -> case mongo_connect:is_closed (Conn) of
149 false -> {Dict, Conn};
cbd5489 connect with timeout
Tony Hannan authored
150 true -> new_connection (Dict, Host, TimeoutMS) end;
151 _ -> new_connection (Dict, Host, TimeoutMS) end end).
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
152
cbd5489 connect with timeout
Tony Hannan authored
153 new_connection (Dict, Host, TimeoutMS) -> case mongo_connect:connect (Host, TimeoutMS) of
445ebaf connnection send/call catches closed connection as well as network failu...
Tony Hannan authored
154 {ok, Conn} -> {dict:store (Host, {Conn}, Dict), Conn};
155 {error, Reason} -> throw ({cant_connect, Reason}) end.
156
157 get_member_info (Conn) -> mongo_query:command ({admin, Conn}, {isMaster, 1}, true).
Something went wrong with that request. Please try again.