Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
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 fa…
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 fa…
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 fa…
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 fa…
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 fa…
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 fa…
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 fa…
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 fa…
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 fa…
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.