@@ -4,10 +4,18 @@ const ServerDescription = require('./server_description').ServerDescription;
4
4
const TopologyDescription = require ( './topology_description' ) . TopologyDescription ;
5
5
const TopologyType = require ( './topology_description' ) . TopologyType ;
6
6
const monitoring = require ( './monitoring' ) ;
7
+ const calculateDurationInMs = require ( '../utils' ) . calculateDurationInMs ;
8
+ const MongoTimeoutError = require ( '../error' ) . MongoTimeoutError ;
9
+ const MongoError = require ( '../error' ) . MongoError ;
7
10
8
11
// Global state
9
12
let globalTopologyCounter = 0 ;
10
13
14
+ // Constants
15
+ const DEFAULT_LOCAL_THRESHOLD_MS = 15 ;
16
+ const DEFAULT_HEARTBEAT_FREQUENCY = 10000 ;
17
+ const DEFAULT_SERVER_SELECTION_TIMEOUT = 30000 ;
18
+
11
19
/**
12
20
* A container of server instances representing a connection to a MongoDB topology.
13
21
*
@@ -27,11 +35,22 @@ class Topology extends EventEmitter {
27
35
*
28
36
* @param {Array|String } seedlist a string list, or array of Server instances to connect to
29
37
* @param {Object } [options] Optional settings
38
+ * @param {Number } [options.localThresholdMS=15] The size of the latency window for selecting among multiple suitable servers
39
+ * @param {Number } [options.serverSelectionTimeoutMS=30000] How long to block for server selection before throwing an error
40
+ * @param {Number } [options.heartbeatFrequencyMS=10000] The frequency with which topology updates are scheduled
30
41
*/
31
42
constructor ( seedlist , options ) {
32
43
super ( ) ;
33
44
seedlist = seedlist || [ ] ;
34
- options = options || { } ;
45
+ options = Object . assign (
46
+ { } ,
47
+ {
48
+ localThresholdMS : DEFAULT_LOCAL_THRESHOLD_MS ,
49
+ serverSelectionTimeoutMS : DEFAULT_SERVER_SELECTION_TIMEOUT ,
50
+ heartbeatFrequencyMS : DEFAULT_HEARTBEAT_FREQUENCY
51
+ } ,
52
+ options
53
+ ) ;
35
54
36
55
const topologyType =
37
56
seedlist . length === 1 && ! options . replicaset
@@ -62,7 +81,11 @@ class Topology extends EventEmitter {
62
81
null ,
63
82
null ,
64
83
options
65
- )
84
+ ) ,
85
+ serverSelectionTimeoutMS :
86
+ options . serverSelectionTimeoutMS || DEFAULT_SERVER_SELECTION_TIMEOUT ,
87
+ heartbeatFrequencyMS : options . heartbeatFrequencyMS || DEFAULT_HEARTBEAT_FREQUENCY ,
88
+ ServerClass : options . ServerClass || null /* eventually our Server class, but null for now */
66
89
} ;
67
90
}
68
91
@@ -111,17 +134,33 @@ class Topology extends EventEmitter {
111
134
/**
112
135
* Selects a server according to the selection predicate provided
113
136
*
114
- * @param {function } [predicate ] An optional predicate to select servers by, defaults to a random selection within a latency window
137
+ * @param {function } [selector ] An optional selector to select servers by, defaults to a random selection within a latency window
115
138
* @return {Server } An instance of a `Server` meeting the criteria of the predicate provided
116
139
*/
117
- selectServer ( /* predicate */ ) {
118
- return ;
140
+ selectServer ( selector , options , callback ) {
141
+ if ( typeof options === 'function' ) ( callback = options ) , ( options = { } ) ;
142
+ options = Object . assign (
143
+ { } ,
144
+ { serverSelectionTimeoutMS : this . s . serverSelectionTimeoutMS } ,
145
+ options
146
+ ) ;
147
+
148
+ selectServers (
149
+ this ,
150
+ selector ,
151
+ options . serverSelectionTimeoutMS ,
152
+ process . hrtime ( ) ,
153
+ ( err , servers ) => {
154
+ if ( err ) return callback ( err , null ) ;
155
+ callback ( null , randomSelection ( servers ) ) ;
156
+ }
157
+ ) ;
119
158
}
120
159
121
160
/**
122
- * Update the topology with a ServerDescription
161
+ * Update the internal TopologyDescription with a ServerDescription
123
162
*
124
- * @param {object } serverDescription the server to update
163
+ * @param {object } serverDescription The server to update in the internal list of server descriptions
125
164
*/
126
165
update ( serverDescription ) {
127
166
// these will be used for monitoring events later
@@ -153,6 +192,44 @@ class Topology extends EventEmitter {
153
192
}
154
193
}
155
194
195
+ function randomSelection ( array ) {
196
+ return array [ Math . floor ( Math . random ( ) * array . length ) ] ;
197
+ }
198
+
199
+ class FakeServer {
200
+ constructor ( description ) {
201
+ this . description = description ;
202
+ }
203
+ }
204
+
205
+ /**
206
+ *
207
+ * @param {* } topology
208
+ * @param {* } selector
209
+ * @param {* } options
210
+ * @param {* } callback
211
+ */
212
+ function selectServers ( topology , selector , timeout , start , callback ) {
213
+ if ( ! topology . description . compatible ) {
214
+ return callback ( new MongoError ( topology . description . compatibilityError ) ) ;
215
+ }
216
+
217
+ const serverDescriptions = Array . from ( topology . description . servers . values ( ) ) ;
218
+ let descriptions = selector ( topology . description , serverDescriptions ) ;
219
+ if ( descriptions . length ) {
220
+ // TODO: obviously return the actual server in the future
221
+ const servers = descriptions . map ( d => new FakeServer ( d ) ) ;
222
+ return callback ( null , servers ) ;
223
+ }
224
+
225
+ const duration = calculateDurationInMs ( process . hrtime ( start ) ) ;
226
+ if ( duration > timeout ) {
227
+ return callback ( new MongoTimeoutError ( `Server selection timed out after ${ timeout } ms` ) ) ;
228
+ }
229
+
230
+ // TODO: loop this, add monitoring
231
+ }
232
+
156
233
/**
157
234
* A server opening SDAM monitoring event
158
235
*
0 commit comments