mongodb 3.2.5 → 3.3.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/HISTORY.md +0 -10
  2. package/index.js +4 -4
  3. package/lib/admin.js +56 -56
  4. package/lib/aggregation_cursor.js +7 -3
  5. package/lib/bulk/common.js +18 -13
  6. package/lib/change_stream.js +196 -89
  7. package/lib/collection.js +217 -169
  8. package/lib/command_cursor.js +17 -7
  9. package/lib/core/auth/auth_provider.js +158 -0
  10. package/lib/core/auth/defaultAuthProviders.js +29 -0
  11. package/lib/core/auth/gssapi.js +241 -0
  12. package/lib/core/auth/mongo_credentials.js +81 -0
  13. package/lib/core/auth/mongocr.js +51 -0
  14. package/lib/core/auth/plain.js +35 -0
  15. package/lib/core/auth/scram.js +293 -0
  16. package/lib/core/auth/sspi.js +131 -0
  17. package/lib/core/auth/x509.js +26 -0
  18. package/lib/core/connection/apm.js +236 -0
  19. package/lib/core/connection/command_result.js +36 -0
  20. package/lib/core/connection/commands.js +507 -0
  21. package/lib/core/connection/connect.js +370 -0
  22. package/lib/core/connection/connection.js +624 -0
  23. package/lib/core/connection/logger.js +246 -0
  24. package/lib/core/connection/msg.js +219 -0
  25. package/lib/core/connection/pool.js +1285 -0
  26. package/lib/core/connection/utils.js +57 -0
  27. package/lib/core/cursor.js +752 -0
  28. package/lib/core/error.js +186 -0
  29. package/lib/core/index.js +50 -0
  30. package/lib/core/sdam/monitoring.js +228 -0
  31. package/lib/core/sdam/server.js +467 -0
  32. package/lib/core/sdam/server_description.js +163 -0
  33. package/lib/core/sdam/server_selectors.js +244 -0
  34. package/lib/core/sdam/srv_polling.js +135 -0
  35. package/lib/core/sdam/topology.js +1151 -0
  36. package/lib/core/sdam/topology_description.js +408 -0
  37. package/lib/core/sessions.js +711 -0
  38. package/lib/core/tools/smoke_plugin.js +61 -0
  39. package/lib/core/topologies/mongos.js +1337 -0
  40. package/lib/core/topologies/read_preference.js +202 -0
  41. package/lib/core/topologies/replset.js +1507 -0
  42. package/lib/core/topologies/replset_state.js +1121 -0
  43. package/lib/core/topologies/server.js +984 -0
  44. package/lib/core/topologies/shared.js +453 -0
  45. package/lib/core/transactions.js +167 -0
  46. package/lib/core/uri_parser.js +631 -0
  47. package/lib/core/utils.js +165 -0
  48. package/lib/core/wireprotocol/command.js +170 -0
  49. package/lib/core/wireprotocol/compression.js +73 -0
  50. package/lib/core/wireprotocol/constants.js +13 -0
  51. package/lib/core/wireprotocol/get_more.js +86 -0
  52. package/lib/core/wireprotocol/index.js +18 -0
  53. package/lib/core/wireprotocol/kill_cursors.js +70 -0
  54. package/lib/core/wireprotocol/query.js +224 -0
  55. package/lib/core/wireprotocol/shared.js +115 -0
  56. package/lib/core/wireprotocol/write_command.js +50 -0
  57. package/lib/cursor.js +40 -46
  58. package/lib/db.js +141 -95
  59. package/lib/dynamic_loaders.js +32 -0
  60. package/lib/error.js +12 -10
  61. package/lib/gridfs/chunk.js +2 -2
  62. package/lib/gridfs/grid_store.js +31 -25
  63. package/lib/gridfs-stream/index.js +4 -4
  64. package/lib/gridfs-stream/upload.js +1 -1
  65. package/lib/mongo_client.js +37 -15
  66. package/lib/operations/add_user.js +96 -0
  67. package/lib/operations/aggregate.js +24 -13
  68. package/lib/operations/aggregate_operation.js +127 -0
  69. package/lib/operations/bulk_write.js +104 -0
  70. package/lib/operations/close.js +47 -0
  71. package/lib/operations/collection_ops.js +28 -287
  72. package/lib/operations/collections.js +55 -0
  73. package/lib/operations/command.js +120 -0
  74. package/lib/operations/command_v2.js +43 -0
  75. package/lib/operations/common_functions.js +372 -0
  76. package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
  77. package/lib/operations/count.js +72 -0
  78. package/lib/operations/count_documents.js +46 -0
  79. package/lib/operations/create_collection.js +118 -0
  80. package/lib/operations/create_index.js +92 -0
  81. package/lib/operations/create_indexes.js +61 -0
  82. package/lib/operations/cursor_ops.js +3 -4
  83. package/lib/operations/db_ops.js +15 -12
  84. package/lib/operations/delete_many.js +25 -0
  85. package/lib/operations/delete_one.js +25 -0
  86. package/lib/operations/distinct.js +85 -0
  87. package/lib/operations/drop.js +53 -0
  88. package/lib/operations/drop_index.js +42 -0
  89. package/lib/operations/drop_indexes.js +23 -0
  90. package/lib/operations/estimated_document_count.js +33 -0
  91. package/lib/operations/execute_db_admin_command.js +34 -0
  92. package/lib/operations/execute_operation.js +165 -0
  93. package/lib/operations/explain.js +23 -0
  94. package/lib/operations/find_and_modify.js +98 -0
  95. package/lib/operations/find_one.js +33 -0
  96. package/lib/operations/find_one_and_delete.js +16 -0
  97. package/lib/operations/find_one_and_replace.js +18 -0
  98. package/lib/operations/find_one_and_update.js +19 -0
  99. package/lib/operations/geo_haystack_search.js +79 -0
  100. package/lib/operations/has_next.js +40 -0
  101. package/lib/operations/index_exists.js +39 -0
  102. package/lib/operations/index_information.js +23 -0
  103. package/lib/operations/indexes.js +22 -0
  104. package/lib/operations/insert_many.js +63 -0
  105. package/lib/operations/insert_one.js +75 -0
  106. package/lib/operations/is_capped.js +19 -0
  107. package/lib/operations/list_indexes.js +66 -0
  108. package/lib/operations/map_reduce.js +189 -0
  109. package/lib/operations/next.js +32 -0
  110. package/lib/operations/operation.js +63 -0
  111. package/lib/operations/options_operation.js +32 -0
  112. package/lib/operations/profiling_level.js +31 -0
  113. package/lib/operations/re_index.js +28 -0
  114. package/lib/operations/remove_user.js +52 -0
  115. package/lib/operations/rename.js +61 -0
  116. package/lib/operations/replace_one.js +47 -0
  117. package/lib/operations/set_profiling_level.js +48 -0
  118. package/lib/operations/stats.js +45 -0
  119. package/lib/operations/to_array.js +68 -0
  120. package/lib/operations/update_many.js +29 -0
  121. package/lib/operations/update_one.js +44 -0
  122. package/lib/operations/validate_collection.js +40 -0
  123. package/lib/read_concern.js +55 -0
  124. package/lib/topologies/mongos.js +3 -3
  125. package/lib/topologies/native_topology.js +22 -2
  126. package/lib/topologies/replset.js +3 -3
  127. package/lib/topologies/server.js +4 -4
  128. package/lib/topologies/topology_base.js +6 -6
  129. package/lib/url_parser.js +4 -3
  130. package/lib/utils.js +46 -59
  131. package/lib/write_concern.js +66 -0
  132. package/package.json +15 -6
  133. package/lib/.DS_Store +0 -0
@@ -0,0 +1,1285 @@
1
+ 'use strict';
2
+
3
+ const inherits = require('util').inherits;
4
+ const EventEmitter = require('events').EventEmitter;
5
+ const MongoError = require('../error').MongoError;
6
+ const MongoNetworkError = require('../error').MongoNetworkError;
7
+ const MongoWriteConcernError = require('../error').MongoWriteConcernError;
8
+ const Logger = require('./logger');
9
+ const f = require('util').format;
10
+ const Msg = require('./msg').Msg;
11
+ const CommandResult = require('./command_result');
12
+ const MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE;
13
+ const COMPRESSION_DETAILS_SIZE = require('../wireprotocol/shared').COMPRESSION_DETAILS_SIZE;
14
+ const opcodes = require('../wireprotocol/shared').opcodes;
15
+ const compress = require('../wireprotocol/compression').compress;
16
+ const compressorIDs = require('../wireprotocol/compression').compressorIDs;
17
+ const uncompressibleCommands = require('../wireprotocol/compression').uncompressibleCommands;
18
+ const apm = require('./apm');
19
+ const Buffer = require('safe-buffer').Buffer;
20
+ const connect = require('./connect');
21
+ const updateSessionFromResponse = require('../sessions').updateSessionFromResponse;
22
+ const eachAsync = require('../utils').eachAsync;
23
+
24
+ var DISCONNECTED = 'disconnected';
25
+ var CONNECTING = 'connecting';
26
+ var CONNECTED = 'connected';
27
+ var DESTROYING = 'destroying';
28
+ var DESTROYED = 'destroyed';
29
+
30
+ const CONNECTION_EVENTS = new Set([
31
+ 'error',
32
+ 'close',
33
+ 'timeout',
34
+ 'parseError',
35
+ 'connect',
36
+ 'message'
37
+ ]);
38
+
39
+ var _id = 0;
40
+
41
+ /**
42
+ * Creates a new Pool instance
43
+ * @class
44
+ * @param {string} options.host The server host
45
+ * @param {number} options.port The server port
46
+ * @param {number} [options.size=5] Max server connection pool size
47
+ * @param {number} [options.minSize=0] Minimum server connection pool size
48
+ * @param {boolean} [options.reconnect=true] Server will attempt to reconnect on loss of connection
49
+ * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
50
+ * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
51
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
52
+ * @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
53
+ * @param {boolean} [options.noDelay=true] TCP Connection no delay
54
+ * @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
55
+ * @param {number} [options.socketTimeout=360000] TCP Socket timeout setting
56
+ * @param {number} [options.monitoringSocketTimeout=30000] TCP Socket timeout setting for replicaset monitoring socket
57
+ * @param {boolean} [options.ssl=false] Use SSL for connection
58
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
59
+ * @param {Buffer} [options.ca] SSL Certificate store binary buffer
60
+ * @param {Buffer} [options.crl] SSL Certificate revocation store binary buffer
61
+ * @param {Buffer} [options.cert] SSL Certificate binary buffer
62
+ * @param {Buffer} [options.key] SSL Key file binary buffer
63
+ * @param {string} [options.passPhrase] SSL Certificate pass phrase
64
+ * @param {boolean} [options.rejectUnauthorized=false] Reject unauthorized server certificates
65
+ * @param {boolean} [options.promoteLongs=true] Convert Long values from the db into Numbers if they fit into 53 bits
66
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
67
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
68
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
69
+ * @fires Pool#connect
70
+ * @fires Pool#close
71
+ * @fires Pool#error
72
+ * @fires Pool#timeout
73
+ * @fires Pool#parseError
74
+ * @return {Pool} A cursor instance
75
+ */
76
+ var Pool = function(topology, options) {
77
+ // Add event listener
78
+ EventEmitter.call(this);
79
+
80
+ // Store topology for later use
81
+ this.topology = topology;
82
+
83
+ // Add the options
84
+ this.options = Object.assign(
85
+ {
86
+ // Host and port settings
87
+ host: 'localhost',
88
+ port: 27017,
89
+ // Pool default max size
90
+ size: 5,
91
+ // Pool default min size
92
+ minSize: 0,
93
+ // socket settings
94
+ connectionTimeout: 30000,
95
+ socketTimeout: 360000,
96
+ keepAlive: true,
97
+ keepAliveInitialDelay: 300000,
98
+ noDelay: true,
99
+ // SSL Settings
100
+ ssl: false,
101
+ checkServerIdentity: true,
102
+ ca: null,
103
+ crl: null,
104
+ cert: null,
105
+ key: null,
106
+ passPhrase: null,
107
+ rejectUnauthorized: false,
108
+ promoteLongs: true,
109
+ promoteValues: true,
110
+ promoteBuffers: false,
111
+ // Reconnection options
112
+ reconnect: true,
113
+ reconnectInterval: 1000,
114
+ reconnectTries: 30,
115
+ // Enable domains
116
+ domainsEnabled: false
117
+ },
118
+ options
119
+ );
120
+
121
+ // Identification information
122
+ this.id = _id++;
123
+ // Current reconnect retries
124
+ this.retriesLeft = this.options.reconnectTries;
125
+ this.reconnectId = null;
126
+ // No bson parser passed in
127
+ if (
128
+ !options.bson ||
129
+ (options.bson &&
130
+ (typeof options.bson.serialize !== 'function' ||
131
+ typeof options.bson.deserialize !== 'function'))
132
+ ) {
133
+ throw new Error('must pass in valid bson parser');
134
+ }
135
+
136
+ // Logger instance
137
+ this.logger = Logger('Pool', options);
138
+ // Pool state
139
+ this.state = DISCONNECTED;
140
+ // Connections
141
+ this.availableConnections = [];
142
+ this.inUseConnections = [];
143
+ this.connectingConnections = 0;
144
+ // Currently executing
145
+ this.executing = false;
146
+ // Operation work queue
147
+ this.queue = [];
148
+
149
+ // Contains the reconnect connection
150
+ this.reconnectConnection = null;
151
+
152
+ // Number of consecutive timeouts caught
153
+ this.numberOfConsecutiveTimeouts = 0;
154
+ // Current pool Index
155
+ this.connectionIndex = 0;
156
+
157
+ // event handlers
158
+ const pool = this;
159
+ this._messageHandler = messageHandler(this);
160
+ this._connectionCloseHandler = function(err) {
161
+ const connection = this;
162
+ connectionFailureHandler(pool, 'close', err, connection);
163
+ };
164
+
165
+ this._connectionErrorHandler = function(err) {
166
+ const connection = this;
167
+ connectionFailureHandler(pool, 'error', err, connection);
168
+ };
169
+
170
+ this._connectionTimeoutHandler = function(err) {
171
+ const connection = this;
172
+ connectionFailureHandler(pool, 'timeout', err, connection);
173
+ };
174
+
175
+ this._connectionParseErrorHandler = function(err) {
176
+ const connection = this;
177
+ connectionFailureHandler(pool, 'parseError', err, connection);
178
+ };
179
+ };
180
+
181
+ inherits(Pool, EventEmitter);
182
+
183
+ Object.defineProperty(Pool.prototype, 'size', {
184
+ enumerable: true,
185
+ get: function() {
186
+ return this.options.size;
187
+ }
188
+ });
189
+
190
+ Object.defineProperty(Pool.prototype, 'minSize', {
191
+ enumerable: true,
192
+ get: function() {
193
+ return this.options.minSize;
194
+ }
195
+ });
196
+
197
+ Object.defineProperty(Pool.prototype, 'connectionTimeout', {
198
+ enumerable: true,
199
+ get: function() {
200
+ return this.options.connectionTimeout;
201
+ }
202
+ });
203
+
204
+ Object.defineProperty(Pool.prototype, 'socketTimeout', {
205
+ enumerable: true,
206
+ get: function() {
207
+ return this.options.socketTimeout;
208
+ }
209
+ });
210
+
211
+ // clears all pool state
212
+ function resetPoolState(pool) {
213
+ pool.inUseConnections = [];
214
+ pool.availableConnections = [];
215
+ pool.connectingConnections = 0;
216
+ pool.executing = false;
217
+ pool.reconnectConnection = null;
218
+ pool.numberOfConsecutiveTimeouts = 0;
219
+ pool.connectionIndex = 0;
220
+ pool.retriesLeft = pool.options.reconnectTries;
221
+ pool.reconnectId = null;
222
+ }
223
+
224
+ function stateTransition(self, newState) {
225
+ var legalTransitions = {
226
+ disconnected: [CONNECTING, DESTROYING, DISCONNECTED],
227
+ connecting: [CONNECTING, DESTROYING, CONNECTED, DISCONNECTED],
228
+ connected: [CONNECTED, DISCONNECTED, DESTROYING],
229
+ destroying: [DESTROYING, DESTROYED],
230
+ destroyed: [DESTROYED]
231
+ };
232
+
233
+ // Get current state
234
+ var legalStates = legalTransitions[self.state];
235
+ if (legalStates && legalStates.indexOf(newState) !== -1) {
236
+ self.emit('stateChanged', self.state, newState);
237
+ self.state = newState;
238
+ } else {
239
+ self.logger.error(
240
+ f(
241
+ 'Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
242
+ self.id,
243
+ self.state,
244
+ newState,
245
+ legalStates
246
+ )
247
+ );
248
+ }
249
+ }
250
+
251
+ function connectionFailureHandler(pool, event, err, conn) {
252
+ if (conn) {
253
+ if (conn._connectionFailHandled) return;
254
+ conn._connectionFailHandled = true;
255
+ conn.destroy();
256
+
257
+ // Remove the connection
258
+ removeConnection(pool, conn);
259
+
260
+ // Flush all work Items on this connection
261
+ while (conn.workItems.length > 0) {
262
+ const workItem = conn.workItems.shift();
263
+ if (workItem.cb) workItem.cb(err);
264
+ }
265
+ }
266
+
267
+ // Did we catch a timeout, increment the numberOfConsecutiveTimeouts
268
+ if (event === 'timeout') {
269
+ pool.numberOfConsecutiveTimeouts = pool.numberOfConsecutiveTimeouts + 1;
270
+
271
+ // Have we timed out more than reconnectTries in a row ?
272
+ // Force close the pool as we are trying to connect to tcp sink hole
273
+ if (pool.numberOfConsecutiveTimeouts > pool.options.reconnectTries) {
274
+ pool.numberOfConsecutiveTimeouts = 0;
275
+ // Destroy all connections and pool
276
+ pool.destroy(true);
277
+ // Emit close event
278
+ return pool.emit('close', pool);
279
+ }
280
+ }
281
+
282
+ // No more socket available propegate the event
283
+ if (pool.socketCount() === 0) {
284
+ if (pool.state !== DESTROYED && pool.state !== DESTROYING) {
285
+ stateTransition(pool, DISCONNECTED);
286
+ }
287
+
288
+ // Do not emit error events, they are always close events
289
+ // do not trigger the low level error handler in node
290
+ event = event === 'error' ? 'close' : event;
291
+ pool.emit(event, err);
292
+ }
293
+
294
+ // Start reconnection attempts
295
+ if (!pool.reconnectId && pool.options.reconnect) {
296
+ pool.reconnectId = setTimeout(attemptReconnect(pool), pool.options.reconnectInterval);
297
+ }
298
+
299
+ // Do we need to do anything to maintain the minimum pool size
300
+ const totalConnections = totalConnectionCount(pool);
301
+ if (totalConnections < pool.minSize) {
302
+ _createConnection(pool);
303
+ }
304
+ }
305
+
306
+ function attemptReconnect(self) {
307
+ return function() {
308
+ self.emit('attemptReconnect', self);
309
+ if (self.state === DESTROYED || self.state === DESTROYING) return;
310
+
311
+ // We are connected do not try again
312
+ if (self.isConnected()) {
313
+ self.reconnectId = null;
314
+ return;
315
+ }
316
+
317
+ self.connectingConnections++;
318
+ connect(self.options, (err, connection) => {
319
+ self.connectingConnections--;
320
+
321
+ if (err) {
322
+ if (self.logger.isDebug()) {
323
+ self.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
324
+ }
325
+
326
+ self.retriesLeft = self.retriesLeft - 1;
327
+ if (self.retriesLeft <= 0) {
328
+ self.destroy();
329
+ self.emit(
330
+ 'reconnectFailed',
331
+ new MongoNetworkError(
332
+ f(
333
+ 'failed to reconnect after %s attempts with interval %s ms',
334
+ self.options.reconnectTries,
335
+ self.options.reconnectInterval
336
+ )
337
+ )
338
+ );
339
+ } else {
340
+ self.reconnectId = setTimeout(attemptReconnect(self), self.options.reconnectInterval);
341
+ }
342
+
343
+ return;
344
+ }
345
+
346
+ if (self.state === DESTROYED || self.state === DESTROYING) {
347
+ return connection.destroy();
348
+ }
349
+
350
+ self.reconnectId = null;
351
+ handlers.forEach(event => connection.removeAllListeners(event));
352
+ connection.on('error', self._connectionErrorHandler);
353
+ connection.on('close', self._connectionCloseHandler);
354
+ connection.on('timeout', self._connectionTimeoutHandler);
355
+ connection.on('parseError', self._connectionParseErrorHandler);
356
+ connection.on('message', self._messageHandler);
357
+
358
+ self.retriesLeft = self.options.reconnectTries;
359
+ self.availableConnections.push(connection);
360
+ self.reconnectConnection = null;
361
+ self.emit('reconnect', self);
362
+ _execute(self)();
363
+ });
364
+ };
365
+ }
366
+
367
+ function moveConnectionBetween(connection, from, to) {
368
+ var index = from.indexOf(connection);
369
+ // Move the connection from connecting to available
370
+ if (index !== -1) {
371
+ from.splice(index, 1);
372
+ to.push(connection);
373
+ }
374
+ }
375
+
376
+ function messageHandler(self) {
377
+ return function(message, connection) {
378
+ // workItem to execute
379
+ var workItem = null;
380
+
381
+ // Locate the workItem
382
+ for (var i = 0; i < connection.workItems.length; i++) {
383
+ if (connection.workItems[i].requestId === message.responseTo) {
384
+ // Get the callback
385
+ workItem = connection.workItems[i];
386
+ // Remove from list of workItems
387
+ connection.workItems.splice(i, 1);
388
+ }
389
+ }
390
+
391
+ if (workItem && workItem.monitoring) {
392
+ moveConnectionBetween(connection, self.inUseConnections, self.availableConnections);
393
+ }
394
+
395
+ // Reset timeout counter
396
+ self.numberOfConsecutiveTimeouts = 0;
397
+
398
+ // Reset the connection timeout if we modified it for
399
+ // this operation
400
+ if (workItem && workItem.socketTimeout) {
401
+ connection.resetSocketTimeout();
402
+ }
403
+
404
+ // Log if debug enabled
405
+ if (self.logger.isDebug()) {
406
+ self.logger.debug(
407
+ f(
408
+ 'message [%s] received from %s:%s',
409
+ message.raw.toString('hex'),
410
+ self.options.host,
411
+ self.options.port
412
+ )
413
+ );
414
+ }
415
+
416
+ function handleOperationCallback(self, cb, err, result) {
417
+ // No domain enabled
418
+ if (!self.options.domainsEnabled) {
419
+ return process.nextTick(function() {
420
+ return cb(err, result);
421
+ });
422
+ }
423
+
424
+ // Domain enabled just call the callback
425
+ cb(err, result);
426
+ }
427
+
428
+ // Keep executing, ensure current message handler does not stop execution
429
+ if (!self.executing) {
430
+ process.nextTick(function() {
431
+ _execute(self)();
432
+ });
433
+ }
434
+
435
+ // Time to dispatch the message if we have a callback
436
+ if (workItem && !workItem.immediateRelease) {
437
+ try {
438
+ // Parse the message according to the provided options
439
+ message.parse(workItem);
440
+ } catch (err) {
441
+ return handleOperationCallback(self, workItem.cb, new MongoError(err));
442
+ }
443
+
444
+ if (message.documents[0]) {
445
+ const document = message.documents[0];
446
+ const session = workItem.session;
447
+ if (session) {
448
+ updateSessionFromResponse(session, document);
449
+ }
450
+
451
+ if (document.$clusterTime) {
452
+ self.topology.clusterTime = document.$clusterTime;
453
+ }
454
+ }
455
+
456
+ // Establish if we have an error
457
+ if (workItem.command && message.documents[0]) {
458
+ const responseDoc = message.documents[0];
459
+
460
+ if (responseDoc.writeConcernError) {
461
+ const err = new MongoWriteConcernError(responseDoc.writeConcernError, responseDoc);
462
+ return handleOperationCallback(self, workItem.cb, err);
463
+ }
464
+
465
+ if (responseDoc.ok === 0 || responseDoc.$err || responseDoc.errmsg || responseDoc.code) {
466
+ return handleOperationCallback(self, workItem.cb, new MongoError(responseDoc));
467
+ }
468
+ }
469
+
470
+ // Add the connection details
471
+ message.hashedName = connection.hashedName;
472
+
473
+ // Return the documents
474
+ handleOperationCallback(
475
+ self,
476
+ workItem.cb,
477
+ null,
478
+ new CommandResult(workItem.fullResult ? message : message.documents[0], connection, message)
479
+ );
480
+ }
481
+ };
482
+ }
483
+
484
+ /**
485
+ * Return the total socket count in the pool.
486
+ * @method
487
+ * @return {Number} The number of socket available.
488
+ */
489
+ Pool.prototype.socketCount = function() {
490
+ return this.availableConnections.length + this.inUseConnections.length;
491
+ // + this.connectingConnections.length;
492
+ };
493
+
494
+ function totalConnectionCount(pool) {
495
+ return (
496
+ pool.availableConnections.length + pool.inUseConnections.length + pool.connectingConnections
497
+ );
498
+ }
499
+
500
+ /**
501
+ * Return all pool connections
502
+ * @method
503
+ * @return {Connection[]} The pool connections
504
+ */
505
+ Pool.prototype.allConnections = function() {
506
+ return this.availableConnections.concat(this.inUseConnections);
507
+ };
508
+
509
+ /**
510
+ * Get a pool connection (round-robin)
511
+ * @method
512
+ * @return {Connection}
513
+ */
514
+ Pool.prototype.get = function() {
515
+ return this.allConnections()[0];
516
+ };
517
+
518
+ /**
519
+ * Is the pool connected
520
+ * @method
521
+ * @return {boolean}
522
+ */
523
+ Pool.prototype.isConnected = function() {
524
+ // We are in a destroyed state
525
+ if (this.state === DESTROYED || this.state === DESTROYING) {
526
+ return false;
527
+ }
528
+
529
+ // Get connections
530
+ var connections = this.availableConnections.concat(this.inUseConnections);
531
+
532
+ // Check if we have any connected connections
533
+ for (var i = 0; i < connections.length; i++) {
534
+ if (connections[i].isConnected()) return true;
535
+ }
536
+
537
+ // Not connected
538
+ return false;
539
+ };
540
+
541
+ /**
542
+ * Was the pool destroyed
543
+ * @method
544
+ * @return {boolean}
545
+ */
546
+ Pool.prototype.isDestroyed = function() {
547
+ return this.state === DESTROYED || this.state === DESTROYING;
548
+ };
549
+
550
+ /**
551
+ * Is the pool in a disconnected state
552
+ * @method
553
+ * @return {boolean}
554
+ */
555
+ Pool.prototype.isDisconnected = function() {
556
+ return this.state === DISCONNECTED;
557
+ };
558
+
559
+ /**
560
+ * Connect pool
561
+ */
562
+ Pool.prototype.connect = function() {
563
+ if (this.state !== DISCONNECTED) {
564
+ throw new MongoError('connection in unlawful state ' + this.state);
565
+ }
566
+
567
+ const self = this;
568
+ stateTransition(this, CONNECTING);
569
+
570
+ self.connectingConnections++;
571
+ connect(self.options, (err, connection) => {
572
+ self.connectingConnections--;
573
+
574
+ if (err) {
575
+ if (self.logger.isDebug()) {
576
+ self.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
577
+ }
578
+
579
+ if (self.state === CONNECTING) {
580
+ self.emit('error', err);
581
+ }
582
+
583
+ return;
584
+ }
585
+
586
+ if (self.state === DESTROYED || self.state === DESTROYING) {
587
+ return self.destroy();
588
+ }
589
+
590
+ // attach event handlers
591
+ connection.on('error', self._connectionErrorHandler);
592
+ connection.on('close', self._connectionCloseHandler);
593
+ connection.on('timeout', self._connectionTimeoutHandler);
594
+ connection.on('parseError', self._connectionParseErrorHandler);
595
+ connection.on('message', self._messageHandler);
596
+
597
+ // If we are in a topology, delegate the auth to it
598
+ // This is to avoid issues where we would auth against an
599
+ // arbiter
600
+ if (self.options.inTopology) {
601
+ stateTransition(self, CONNECTED);
602
+ self.availableConnections.push(connection);
603
+ return self.emit('connect', self, connection);
604
+ }
605
+
606
+ if (self.state === DESTROYED || self.state === DESTROYING) {
607
+ return self.destroy();
608
+ }
609
+
610
+ if (err) {
611
+ self.destroy();
612
+ return self.emit('error', err);
613
+ }
614
+
615
+ stateTransition(self, CONNECTED);
616
+ self.availableConnections.push(connection);
617
+
618
+ if (self.minSize) {
619
+ for (let i = 0; i < self.minSize; i++) {
620
+ _createConnection(self);
621
+ }
622
+ }
623
+
624
+ self.emit('connect', self, connection);
625
+ });
626
+ };
627
+
628
+ /**
629
+ * Authenticate using a specified mechanism
630
+ * @param {authResultCallback} callback A callback function
631
+ */
632
+ Pool.prototype.auth = function(credentials, callback) {
633
+ if (typeof callback === 'function') callback(null, null);
634
+ };
635
+
636
+ /**
637
+ * Logout all users against a database
638
+ * @param {authResultCallback} callback A callback function
639
+ */
640
+ Pool.prototype.logout = function(dbName, callback) {
641
+ if (typeof callback === 'function') callback(null, null);
642
+ };
643
+
644
+ /**
645
+ * Unref the pool
646
+ * @method
647
+ */
648
+ Pool.prototype.unref = function() {
649
+ // Get all the known connections
650
+ var connections = this.availableConnections.concat(this.inUseConnections);
651
+
652
+ connections.forEach(function(c) {
653
+ c.unref();
654
+ });
655
+ };
656
+
657
+ // Destroy the connections
658
+ function destroy(self, connections, options, callback) {
659
+ eachAsync(
660
+ connections,
661
+ (conn, cb) => {
662
+ for (const eventName of CONNECTION_EVENTS) {
663
+ conn.removeAllListeners(eventName);
664
+ }
665
+
666
+ conn.destroy(options, cb);
667
+ },
668
+ err => {
669
+ if (err) {
670
+ if (typeof callback === 'function') callback(err, null);
671
+ return;
672
+ }
673
+
674
+ resetPoolState(self);
675
+ self.queue = [];
676
+
677
+ stateTransition(self, DESTROYED);
678
+ if (typeof callback === 'function') callback(null, null);
679
+ }
680
+ );
681
+ }
682
+
683
+ /**
684
+ * Destroy pool
685
+ * @method
686
+ */
687
+ Pool.prototype.destroy = function(force, callback) {
688
+ var self = this;
689
+ // Do not try again if the pool is already dead
690
+ if (this.state === DESTROYED || self.state === DESTROYING) {
691
+ if (typeof callback === 'function') callback(null, null);
692
+ return;
693
+ }
694
+
695
+ // Set state to destroyed
696
+ stateTransition(this, DESTROYING);
697
+
698
+ // Are we force closing
699
+ if (force) {
700
+ // Get all the known connections
701
+ var connections = self.availableConnections.concat(self.inUseConnections);
702
+
703
+ // Flush any remaining work items with
704
+ // an error
705
+ while (self.queue.length > 0) {
706
+ var workItem = self.queue.shift();
707
+ if (typeof workItem.cb === 'function') {
708
+ workItem.cb(new MongoError('Pool was force destroyed'));
709
+ }
710
+ }
711
+
712
+ // Destroy the topology
713
+ return destroy(self, connections, { force: true }, callback);
714
+ }
715
+
716
+ // Clear out the reconnect if set
717
+ if (this.reconnectId) {
718
+ clearTimeout(this.reconnectId);
719
+ }
720
+
721
+ // If we have a reconnect connection running, close
722
+ // immediately
723
+ if (this.reconnectConnection) {
724
+ this.reconnectConnection.destroy();
725
+ }
726
+
727
+ // Wait for the operations to drain before we close the pool
728
+ function checkStatus() {
729
+ flushMonitoringOperations(self.queue);
730
+
731
+ if (self.queue.length === 0) {
732
+ // Get all the known connections
733
+ var connections = self.availableConnections.concat(self.inUseConnections);
734
+
735
+ // Check if we have any in flight operations
736
+ for (var i = 0; i < connections.length; i++) {
737
+ // There is an operation still in flight, reschedule a
738
+ // check waiting for it to drain
739
+ if (connections[i].workItems.length > 0) {
740
+ return setTimeout(checkStatus, 1);
741
+ }
742
+ }
743
+
744
+ destroy(self, connections, { force: false }, callback);
745
+ // } else if (self.queue.length > 0 && !this.reconnectId) {
746
+ } else {
747
+ // Ensure we empty the queue
748
+ _execute(self)();
749
+ // Set timeout
750
+ setTimeout(checkStatus, 1);
751
+ }
752
+ }
753
+
754
+ // Initiate drain of operations
755
+ checkStatus();
756
+ };
757
+
758
+ /**
759
+ * Reset all connections of this pool
760
+ *
761
+ * @param {function} [callback]
762
+ */
763
+ Pool.prototype.reset = function(callback) {
764
+ const connections = this.availableConnections.concat(this.inUseConnections);
765
+ eachAsync(
766
+ connections,
767
+ (conn, cb) => {
768
+ for (const eventName of CONNECTION_EVENTS) {
769
+ conn.removeAllListeners(eventName);
770
+ }
771
+
772
+ conn.destroy({ force: true }, cb);
773
+ },
774
+ err => {
775
+ if (err) {
776
+ if (typeof callback === 'function') {
777
+ callback(err, null);
778
+ return;
779
+ }
780
+ }
781
+
782
+ resetPoolState(this);
783
+
784
+ // create an initial connection, and kick off execution again
785
+ _createConnection(this);
786
+
787
+ if (typeof callback === 'function') {
788
+ callback(null, null);
789
+ }
790
+ }
791
+ );
792
+ };
793
+
794
+ // Prepare the buffer that Pool.prototype.write() uses to send to the server
795
+ function serializeCommand(self, command, callback) {
796
+ const originalCommandBuffer = command.toBin();
797
+
798
+ // Check whether we and the server have agreed to use a compressor
799
+ const shouldCompress = !!self.options.agreedCompressor;
800
+ if (!shouldCompress || !canCompress(command)) {
801
+ return callback(null, originalCommandBuffer);
802
+ }
803
+
804
+ // Transform originalCommandBuffer into OP_COMPRESSED
805
+ const concatenatedOriginalCommandBuffer = Buffer.concat(originalCommandBuffer);
806
+ const messageToBeCompressed = concatenatedOriginalCommandBuffer.slice(MESSAGE_HEADER_SIZE);
807
+
808
+ // Extract information needed for OP_COMPRESSED from the uncompressed message
809
+ const originalCommandOpCode = concatenatedOriginalCommandBuffer.readInt32LE(12);
810
+
811
+ // Compress the message body
812
+ compress(self, messageToBeCompressed, function(err, compressedMessage) {
813
+ if (err) return callback(err, null);
814
+
815
+ // Create the msgHeader of OP_COMPRESSED
816
+ const msgHeader = Buffer.alloc(MESSAGE_HEADER_SIZE);
817
+ msgHeader.writeInt32LE(
818
+ MESSAGE_HEADER_SIZE + COMPRESSION_DETAILS_SIZE + compressedMessage.length,
819
+ 0
820
+ ); // messageLength
821
+ msgHeader.writeInt32LE(command.requestId, 4); // requestID
822
+ msgHeader.writeInt32LE(0, 8); // responseTo (zero)
823
+ msgHeader.writeInt32LE(opcodes.OP_COMPRESSED, 12); // opCode
824
+
825
+ // Create the compression details of OP_COMPRESSED
826
+ const compressionDetails = Buffer.alloc(COMPRESSION_DETAILS_SIZE);
827
+ compressionDetails.writeInt32LE(originalCommandOpCode, 0); // originalOpcode
828
+ compressionDetails.writeInt32LE(messageToBeCompressed.length, 4); // Size of the uncompressed compressedMessage, excluding the MsgHeader
829
+ compressionDetails.writeUInt8(compressorIDs[self.options.agreedCompressor], 8); // compressorID
830
+
831
+ return callback(null, [msgHeader, compressionDetails, compressedMessage]);
832
+ });
833
+ }
834
+
835
+ /**
836
+ * Write a message to MongoDB
837
+ * @method
838
+ * @return {Connection}
839
+ */
840
+ Pool.prototype.write = function(command, options, cb) {
841
+ var self = this;
842
+ // Ensure we have a callback
843
+ if (typeof options === 'function') {
844
+ cb = options;
845
+ }
846
+
847
+ // Always have options
848
+ options = options || {};
849
+
850
+ // We need to have a callback function unless the message returns no response
851
+ if (!(typeof cb === 'function') && !options.noResponse) {
852
+ throw new MongoError('write method must provide a callback');
853
+ }
854
+
855
+ // Pool was destroyed error out
856
+ if (this.state === DESTROYED || this.state === DESTROYING) {
857
+ // Callback with an error
858
+ if (cb) {
859
+ try {
860
+ cb(new MongoError('pool destroyed'));
861
+ } catch (err) {
862
+ process.nextTick(function() {
863
+ throw err;
864
+ });
865
+ }
866
+ }
867
+
868
+ return;
869
+ }
870
+
871
+ if (this.options.domainsEnabled && process.domain && typeof cb === 'function') {
872
+ // if we have a domain bind to it
873
+ var oldCb = cb;
874
+ cb = process.domain.bind(function() {
875
+ // v8 - argumentsToArray one-liner
876
+ var args = new Array(arguments.length);
877
+ for (var i = 0; i < arguments.length; i++) {
878
+ args[i] = arguments[i];
879
+ }
880
+ // bounce off event loop so domain switch takes place
881
+ process.nextTick(function() {
882
+ oldCb.apply(null, args);
883
+ });
884
+ });
885
+ }
886
+
887
+ // Do we have an operation
888
+ var operation = {
889
+ cb: cb,
890
+ raw: false,
891
+ promoteLongs: true,
892
+ promoteValues: true,
893
+ promoteBuffers: false,
894
+ fullResult: false
895
+ };
896
+
897
+ // Set the options for the parsing
898
+ operation.promoteLongs = typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true;
899
+ operation.promoteValues =
900
+ typeof options.promoteValues === 'boolean' ? options.promoteValues : true;
901
+ operation.promoteBuffers =
902
+ typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false;
903
+ operation.raw = typeof options.raw === 'boolean' ? options.raw : false;
904
+ operation.immediateRelease =
905
+ typeof options.immediateRelease === 'boolean' ? options.immediateRelease : false;
906
+ operation.documentsReturnedIn = options.documentsReturnedIn;
907
+ operation.command = typeof options.command === 'boolean' ? options.command : false;
908
+ operation.fullResult = typeof options.fullResult === 'boolean' ? options.fullResult : false;
909
+ operation.noResponse = typeof options.noResponse === 'boolean' ? options.noResponse : false;
910
+ operation.session = options.session || null;
911
+
912
+ // Optional per operation socketTimeout
913
+ operation.socketTimeout = options.socketTimeout;
914
+ operation.monitoring = options.monitoring;
915
+ // Custom socket Timeout
916
+ if (options.socketTimeout) {
917
+ operation.socketTimeout = options.socketTimeout;
918
+ }
919
+
920
+ // Get the requestId
921
+ operation.requestId = command.requestId;
922
+
923
+ // If command monitoring is enabled we need to modify the callback here
924
+ if (self.options.monitorCommands) {
925
+ this.emit('commandStarted', new apm.CommandStartedEvent(this, command));
926
+
927
+ operation.started = process.hrtime();
928
+ operation.cb = (err, reply) => {
929
+ if (err) {
930
+ self.emit(
931
+ 'commandFailed',
932
+ new apm.CommandFailedEvent(this, command, err, operation.started)
933
+ );
934
+ } else {
935
+ if (reply && reply.result && (reply.result.ok === 0 || reply.result.$err)) {
936
+ self.emit(
937
+ 'commandFailed',
938
+ new apm.CommandFailedEvent(this, command, reply.result, operation.started)
939
+ );
940
+ } else {
941
+ self.emit(
942
+ 'commandSucceeded',
943
+ new apm.CommandSucceededEvent(this, command, reply, operation.started)
944
+ );
945
+ }
946
+ }
947
+
948
+ if (typeof cb === 'function') cb(err, reply);
949
+ };
950
+ }
951
+
952
+ // Prepare the operation buffer
953
+ serializeCommand(self, command, (err, serializedBuffers) => {
954
+ if (err) throw err;
955
+
956
+ // Set the operation's buffer to the serialization of the commands
957
+ operation.buffer = serializedBuffers;
958
+
959
+ // If we have a monitoring operation schedule as the very first operation
960
+ // Otherwise add to back of queue
961
+ if (options.monitoring) {
962
+ self.queue.unshift(operation);
963
+ } else {
964
+ self.queue.push(operation);
965
+ }
966
+
967
+ // Attempt to execute the operation
968
+ if (!self.executing) {
969
+ process.nextTick(function() {
970
+ _execute(self)();
971
+ });
972
+ }
973
+ });
974
+ };
975
+
976
+ // Return whether a command contains an uncompressible command term
977
+ // Will return true if command contains no uncompressible command terms
978
+ function canCompress(command) {
979
+ const commandDoc = command instanceof Msg ? command.command : command.query;
980
+ const commandName = Object.keys(commandDoc)[0];
981
+ return uncompressibleCommands.indexOf(commandName) === -1;
982
+ }
983
+
984
+ // Remove connection method
985
+ function remove(connection, connections) {
986
+ for (var i = 0; i < connections.length; i++) {
987
+ if (connections[i] === connection) {
988
+ connections.splice(i, 1);
989
+ return true;
990
+ }
991
+ }
992
+ }
993
+
994
+ function removeConnection(self, connection) {
995
+ if (remove(connection, self.availableConnections)) return;
996
+ if (remove(connection, self.inUseConnections)) return;
997
+ }
998
+
999
+ const handlers = ['close', 'message', 'error', 'timeout', 'parseError', 'connect'];
1000
+ function _createConnection(self) {
1001
+ if (self.state === DESTROYED || self.state === DESTROYING) {
1002
+ return;
1003
+ }
1004
+
1005
+ self.connectingConnections++;
1006
+ connect(self.options, (err, connection) => {
1007
+ self.connectingConnections--;
1008
+
1009
+ if (err) {
1010
+ if (self.logger.isDebug()) {
1011
+ self.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
1012
+ }
1013
+
1014
+ if (!self.reconnectId && self.options.reconnect) {
1015
+ self.reconnectId = setTimeout(attemptReconnect(self), self.options.reconnectInterval);
1016
+ }
1017
+
1018
+ return;
1019
+ }
1020
+
1021
+ if (self.state === DESTROYED || self.state === DESTROYING) {
1022
+ removeConnection(self, connection);
1023
+ return connection.destroy();
1024
+ }
1025
+
1026
+ connection.on('error', self._connectionErrorHandler);
1027
+ connection.on('close', self._connectionCloseHandler);
1028
+ connection.on('timeout', self._connectionTimeoutHandler);
1029
+ connection.on('parseError', self._connectionParseErrorHandler);
1030
+ connection.on('message', self._messageHandler);
1031
+
1032
+ if (self.state === DESTROYED || self.state === DESTROYING) {
1033
+ return connection.destroy();
1034
+ }
1035
+
1036
+ // Remove the connection from the connectingConnections list
1037
+ removeConnection(self, connection);
1038
+
1039
+ // Handle error
1040
+ if (err) {
1041
+ return connection.destroy();
1042
+ }
1043
+
1044
+ // Push to available
1045
+ self.availableConnections.push(connection);
1046
+ // Execute any work waiting
1047
+ _execute(self)();
1048
+ });
1049
+ }
1050
+
1051
+ function flushMonitoringOperations(queue) {
1052
+ for (var i = 0; i < queue.length; i++) {
1053
+ if (queue[i].monitoring) {
1054
+ var workItem = queue[i];
1055
+ queue.splice(i, 1);
1056
+ workItem.cb(
1057
+ new MongoError({ message: 'no connection available for monitoring', driver: true })
1058
+ );
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ function _execute(self) {
1064
+ return function() {
1065
+ if (self.state === DESTROYED) return;
1066
+ // Already executing, skip
1067
+ if (self.executing) return;
1068
+ // Set pool as executing
1069
+ self.executing = true;
1070
+
1071
+ // New pool connections are in progress, wait them to finish
1072
+ // before executing any more operation to ensure distribution of
1073
+ // operations
1074
+ if (self.connectingConnections > 0) {
1075
+ self.executing = false;
1076
+ return;
1077
+ }
1078
+
1079
+ // As long as we have available connections
1080
+ // eslint-disable-next-line
1081
+ while (true) {
1082
+ // Total availble connections
1083
+ const totalConnections = totalConnectionCount(self);
1084
+
1085
+ // No available connections available, flush any monitoring ops
1086
+ if (self.availableConnections.length === 0) {
1087
+ // Flush any monitoring operations
1088
+ flushMonitoringOperations(self.queue);
1089
+ break;
1090
+ }
1091
+
1092
+ // No queue break
1093
+ if (self.queue.length === 0) {
1094
+ break;
1095
+ }
1096
+
1097
+ var connection = null;
1098
+ const connections = self.availableConnections.filter(conn => conn.workItems.length === 0);
1099
+
1100
+ // No connection found that has no work on it, just pick one for pipelining
1101
+ if (connections.length === 0) {
1102
+ connection =
1103
+ self.availableConnections[self.connectionIndex++ % self.availableConnections.length];
1104
+ } else {
1105
+ connection = connections[self.connectionIndex++ % connections.length];
1106
+ }
1107
+
1108
+ // Is the connection connected
1109
+ if (!connection.isConnected()) {
1110
+ // Remove the disconnected connection
1111
+ removeConnection(self, connection);
1112
+ // Flush any monitoring operations in the queue, failing fast
1113
+ flushMonitoringOperations(self.queue);
1114
+ break;
1115
+ }
1116
+
1117
+ // Get the next work item
1118
+ var workItem = self.queue.shift();
1119
+
1120
+ // If we are monitoring we need to use a connection that is not
1121
+ // running another operation to avoid socket timeout changes
1122
+ // affecting an existing operation
1123
+ if (workItem.monitoring) {
1124
+ var foundValidConnection = false;
1125
+
1126
+ for (let i = 0; i < self.availableConnections.length; i++) {
1127
+ // If the connection is connected
1128
+ // And there are no pending workItems on it
1129
+ // Then we can safely use it for monitoring.
1130
+ if (
1131
+ self.availableConnections[i].isConnected() &&
1132
+ self.availableConnections[i].workItems.length === 0
1133
+ ) {
1134
+ foundValidConnection = true;
1135
+ connection = self.availableConnections[i];
1136
+ break;
1137
+ }
1138
+ }
1139
+
1140
+ // No safe connection found, attempt to grow the connections
1141
+ // if possible and break from the loop
1142
+ if (!foundValidConnection) {
1143
+ // Put workItem back on the queue
1144
+ self.queue.unshift(workItem);
1145
+
1146
+ // Attempt to grow the pool if it's not yet maxsize
1147
+ if (totalConnections < self.options.size && self.queue.length > 0) {
1148
+ // Create a new connection
1149
+ _createConnection(self);
1150
+ }
1151
+
1152
+ // Re-execute the operation
1153
+ setTimeout(function() {
1154
+ _execute(self)();
1155
+ }, 10);
1156
+
1157
+ break;
1158
+ }
1159
+ }
1160
+
1161
+ // Don't execute operation until we have a full pool
1162
+ if (totalConnections < self.options.size) {
1163
+ // Connection has work items, then put it back on the queue
1164
+ // and create a new connection
1165
+ if (connection.workItems.length > 0) {
1166
+ // Lets put the workItem back on the list
1167
+ self.queue.unshift(workItem);
1168
+ // Create a new connection
1169
+ _createConnection(self);
1170
+ // Break from the loop
1171
+ break;
1172
+ }
1173
+ }
1174
+
1175
+ // Get actual binary commands
1176
+ var buffer = workItem.buffer;
1177
+
1178
+ // If we are monitoring take the connection of the availableConnections
1179
+ if (workItem.monitoring) {
1180
+ moveConnectionBetween(connection, self.availableConnections, self.inUseConnections);
1181
+ }
1182
+
1183
+ // Track the executing commands on the mongo server
1184
+ // as long as there is an expected response
1185
+ if (!workItem.noResponse) {
1186
+ connection.workItems.push(workItem);
1187
+ }
1188
+
1189
+ // We have a custom socketTimeout
1190
+ if (!workItem.immediateRelease && typeof workItem.socketTimeout === 'number') {
1191
+ connection.setSocketTimeout(workItem.socketTimeout);
1192
+ }
1193
+
1194
+ // Capture if write was successful
1195
+ var writeSuccessful = true;
1196
+
1197
+ // Put operation on the wire
1198
+ if (Array.isArray(buffer)) {
1199
+ for (let i = 0; i < buffer.length; i++) {
1200
+ writeSuccessful = connection.write(buffer[i]);
1201
+ }
1202
+ } else {
1203
+ writeSuccessful = connection.write(buffer);
1204
+ }
1205
+
1206
+ // if the command is designated noResponse, call the callback immeditely
1207
+ if (workItem.noResponse && typeof workItem.cb === 'function') {
1208
+ workItem.cb(null, null);
1209
+ }
1210
+
1211
+ if (writeSuccessful === false) {
1212
+ // If write not successful put back on queue
1213
+ self.queue.unshift(workItem);
1214
+ // Remove the disconnected connection
1215
+ removeConnection(self, connection);
1216
+ // Flush any monitoring operations in the queue, failing fast
1217
+ flushMonitoringOperations(self.queue);
1218
+ break;
1219
+ }
1220
+ }
1221
+
1222
+ self.executing = false;
1223
+ };
1224
+ }
1225
+
1226
+ // Make execution loop available for testing
1227
+ Pool._execute = _execute;
1228
+
1229
+ /**
1230
+ * A server connect event, used to verify that the connection is up and running
1231
+ *
1232
+ * @event Pool#connect
1233
+ * @type {Pool}
1234
+ */
1235
+
1236
+ /**
1237
+ * A server reconnect event, used to verify that pool reconnected.
1238
+ *
1239
+ * @event Pool#reconnect
1240
+ * @type {Pool}
1241
+ */
1242
+
1243
+ /**
1244
+ * The server connection closed, all pool connections closed
1245
+ *
1246
+ * @event Pool#close
1247
+ * @type {Pool}
1248
+ */
1249
+
1250
+ /**
1251
+ * The server connection caused an error, all pool connections closed
1252
+ *
1253
+ * @event Pool#error
1254
+ * @type {Pool}
1255
+ */
1256
+
1257
+ /**
1258
+ * The server connection timed out, all pool connections closed
1259
+ *
1260
+ * @event Pool#timeout
1261
+ * @type {Pool}
1262
+ */
1263
+
1264
+ /**
1265
+ * The driver experienced an invalid message, all pool connections closed
1266
+ *
1267
+ * @event Pool#parseError
1268
+ * @type {Pool}
1269
+ */
1270
+
1271
+ /**
1272
+ * The driver attempted to reconnect
1273
+ *
1274
+ * @event Pool#attemptReconnect
1275
+ * @type {Pool}
1276
+ */
1277
+
1278
+ /**
1279
+ * The driver exhausted all reconnect attempts
1280
+ *
1281
+ * @event Pool#reconnectFailed
1282
+ * @type {Pool}
1283
+ */
1284
+
1285
+ module.exports = Pool;