mongodb 3.3.3 → 3.4.0
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.
- package/HISTORY.md +90 -0
- package/index.js +4 -0
- package/lib/bulk/common.js +26 -5
- package/lib/bulk/ordered.js +9 -4
- package/lib/bulk/unordered.js +9 -4
- package/lib/collection.js +123 -35
- package/lib/core/auth/scram.js +42 -6
- package/lib/core/cmap/connection.js +220 -0
- package/lib/core/cmap/message_stream.js +181 -0
- package/lib/core/connection/apm.js +14 -16
- package/lib/core/connection/connect.js +56 -39
- package/lib/core/connection/connection.js +17 -1
- package/lib/core/connection/logger.js +9 -4
- package/lib/core/connection/msg.js +1 -1
- package/lib/core/connection/pool.js +123 -86
- package/lib/core/cursor.js +1 -1
- package/lib/core/error.js +17 -6
- package/lib/core/index.js +1 -1
- package/lib/core/sdam/common.js +59 -0
- package/lib/core/sdam/monitoring.js +16 -10
- package/lib/core/sdam/server.js +79 -36
- package/lib/core/sdam/server_description.js +30 -14
- package/lib/core/sdam/{server_selectors.js → server_selection.js} +100 -7
- package/lib/core/sdam/srv_polling.js +1 -1
- package/lib/core/sdam/topology.js +165 -192
- package/lib/core/sdam/topology_description.js +14 -23
- package/lib/core/sessions.js +5 -8
- package/lib/core/topologies/replset.js +26 -15
- package/lib/core/topologies/server.js +8 -12
- package/lib/core/topologies/shared.js +26 -16
- package/lib/core/uri_parser.js +49 -1
- package/lib/core/utils.js +32 -1
- package/lib/core/wireprotocol/command.js +25 -3
- package/lib/core/wireprotocol/compression.js +13 -13
- package/lib/core/wireprotocol/shared.js +1 -1
- package/lib/db.js +3 -1
- package/lib/gridfs/grid_store.js +15 -8
- package/lib/gridfs-stream/download.js +5 -4
- package/lib/gridfs-stream/index.js +3 -2
- package/lib/gridfs-stream/upload.js +3 -3
- package/lib/mongo_client.js +44 -34
- package/lib/operations/close.js +6 -2
- package/lib/operations/command_v2.js +1 -3
- package/lib/operations/common_functions.js +5 -2
- package/lib/operations/connect.js +48 -3
- package/lib/operations/create_collection.js +1 -2
- package/lib/operations/db_ops.js +2 -4
- package/lib/operations/execute_operation.js +2 -1
- package/lib/operations/map_reduce.js +2 -1
- package/lib/utils.js +2 -1
- package/package.json +8 -5
|
@@ -12,14 +12,19 @@ var pid = process.pid;
|
|
|
12
12
|
// current logger
|
|
13
13
|
var currentLogger = null;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @callback Logger~loggerCallback
|
|
17
|
+
* @param {string} msg message being logged
|
|
18
|
+
* @param {object} state an object containing more metadata about the logging message
|
|
19
|
+
*/
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Creates a new Logger instance
|
|
17
23
|
* @class
|
|
18
24
|
* @param {string} className The Class name associated with the logging instance
|
|
19
25
|
* @param {object} [options=null] Optional settings.
|
|
20
|
-
* @param {
|
|
26
|
+
* @param {Logger~loggerCallback} [options.logger=null] Custom logger function;
|
|
21
27
|
* @param {string} [options.loggerLevel=error] Override default global log level.
|
|
22
|
-
* @return {Logger} a Logger instance.
|
|
23
28
|
*/
|
|
24
29
|
var Logger = function(className, options) {
|
|
25
30
|
if (!(this instanceof Logger)) return new Logger(className, options);
|
|
@@ -195,7 +200,7 @@ Logger.reset = function() {
|
|
|
195
200
|
/**
|
|
196
201
|
* Get the current logger function
|
|
197
202
|
* @method
|
|
198
|
-
* @return {
|
|
203
|
+
* @return {Logger~loggerCallback}
|
|
199
204
|
*/
|
|
200
205
|
Logger.currentLogger = function() {
|
|
201
206
|
return currentLogger;
|
|
@@ -204,7 +209,7 @@ Logger.currentLogger = function() {
|
|
|
204
209
|
/**
|
|
205
210
|
* Set the current logger function
|
|
206
211
|
* @method
|
|
207
|
-
* @param {
|
|
212
|
+
* @param {Logger~loggerCallback} logger Logger function.
|
|
208
213
|
* @return {null}
|
|
209
214
|
*/
|
|
210
215
|
Logger.setCurrentLogger = function(logger) {
|
|
@@ -20,12 +20,22 @@ const Buffer = require('safe-buffer').Buffer;
|
|
|
20
20
|
const connect = require('./connect');
|
|
21
21
|
const updateSessionFromResponse = require('../sessions').updateSessionFromResponse;
|
|
22
22
|
const eachAsync = require('../utils').eachAsync;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
const makeStateMachine = require('../utils').makeStateMachine;
|
|
24
|
+
|
|
25
|
+
const DISCONNECTED = 'disconnected';
|
|
26
|
+
const CONNECTING = 'connecting';
|
|
27
|
+
const CONNECTED = 'connected';
|
|
28
|
+
const DRAINING = 'draining';
|
|
29
|
+
const DESTROYING = 'destroying';
|
|
30
|
+
const DESTROYED = 'destroyed';
|
|
31
|
+
const stateTransition = makeStateMachine({
|
|
32
|
+
[DISCONNECTED]: [CONNECTING, DRAINING, DISCONNECTED],
|
|
33
|
+
[CONNECTING]: [CONNECTING, CONNECTED, DRAINING, DISCONNECTED],
|
|
34
|
+
[CONNECTED]: [CONNECTED, DISCONNECTED, DRAINING],
|
|
35
|
+
[DRAINING]: [DRAINING, DESTROYING, DESTROYED],
|
|
36
|
+
[DESTROYING]: [DESTROYING, DESTROYED],
|
|
37
|
+
[DESTROYED]: [DESTROYED]
|
|
38
|
+
});
|
|
29
39
|
|
|
30
40
|
const CONNECTION_EVENTS = new Set([
|
|
31
41
|
'error',
|
|
@@ -80,6 +90,14 @@ var Pool = function(topology, options) {
|
|
|
80
90
|
// Store topology for later use
|
|
81
91
|
this.topology = topology;
|
|
82
92
|
|
|
93
|
+
this.s = {
|
|
94
|
+
state: DISCONNECTED,
|
|
95
|
+
cancellationToken: new EventEmitter()
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// we don't care how many connections are listening for cancellation
|
|
99
|
+
this.s.cancellationToken.setMaxListeners(Infinity);
|
|
100
|
+
|
|
83
101
|
// Add the options
|
|
84
102
|
this.options = Object.assign(
|
|
85
103
|
{
|
|
@@ -138,8 +156,6 @@ var Pool = function(topology, options) {
|
|
|
138
156
|
|
|
139
157
|
// Logger instance
|
|
140
158
|
this.logger = Logger('Pool', options);
|
|
141
|
-
// Pool state
|
|
142
|
-
this.state = DISCONNECTED;
|
|
143
159
|
// Connections
|
|
144
160
|
this.availableConnections = [];
|
|
145
161
|
this.inUseConnections = [];
|
|
@@ -208,6 +224,13 @@ Object.defineProperty(Pool.prototype, 'socketTimeout', {
|
|
|
208
224
|
}
|
|
209
225
|
});
|
|
210
226
|
|
|
227
|
+
Object.defineProperty(Pool.prototype, 'state', {
|
|
228
|
+
enumerable: true,
|
|
229
|
+
get: function() {
|
|
230
|
+
return this.s.state;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
211
234
|
// clears all pool state
|
|
212
235
|
function resetPoolState(pool) {
|
|
213
236
|
pool.inUseConnections = [];
|
|
@@ -220,47 +243,36 @@ function resetPoolState(pool) {
|
|
|
220
243
|
pool.reconnectId = null;
|
|
221
244
|
}
|
|
222
245
|
|
|
223
|
-
function stateTransition(self, newState) {
|
|
224
|
-
var legalTransitions = {
|
|
225
|
-
disconnected: [CONNECTING, DESTROYING, DISCONNECTED],
|
|
226
|
-
connecting: [CONNECTING, DESTROYING, CONNECTED, DISCONNECTED],
|
|
227
|
-
connected: [CONNECTED, DISCONNECTED, DESTROYING],
|
|
228
|
-
destroying: [DESTROYING, DESTROYED],
|
|
229
|
-
destroyed: [DESTROYED]
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
// Get current state
|
|
233
|
-
var legalStates = legalTransitions[self.state];
|
|
234
|
-
if (legalStates && legalStates.indexOf(newState) !== -1) {
|
|
235
|
-
self.emit('stateChanged', self.state, newState);
|
|
236
|
-
self.state = newState;
|
|
237
|
-
} else {
|
|
238
|
-
self.logger.error(
|
|
239
|
-
f(
|
|
240
|
-
'Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
|
|
241
|
-
self.id,
|
|
242
|
-
self.state,
|
|
243
|
-
newState,
|
|
244
|
-
legalStates
|
|
245
|
-
)
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
246
|
function connectionFailureHandler(pool, event, err, conn) {
|
|
251
247
|
if (conn) {
|
|
252
|
-
if (conn._connectionFailHandled)
|
|
248
|
+
if (conn._connectionFailHandled) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
253
252
|
conn._connectionFailHandled = true;
|
|
254
253
|
conn.destroy();
|
|
255
254
|
|
|
256
255
|
// Remove the connection
|
|
257
256
|
removeConnection(pool, conn);
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
258
|
+
if (
|
|
259
|
+
pool.state !== DRAINING &&
|
|
260
|
+
pool.state !== DESTROYED &&
|
|
261
|
+
pool.options.legacyCompatMode === false
|
|
262
|
+
) {
|
|
263
|
+
// since an error/close/timeout means pool invalidation in a
|
|
264
|
+
// pre-CMAP world, we will issue a custom `drain` event here to
|
|
265
|
+
// signal that the server should be recycled
|
|
266
|
+
stateTransition(pool, DRAINING);
|
|
267
|
+
pool.emit('drain', err);
|
|
268
|
+
|
|
269
|
+
// wait to flush work items so this server isn't selected again immediately
|
|
270
|
+
process.nextTick(() => conn.flush(err));
|
|
271
|
+
return;
|
|
263
272
|
}
|
|
273
|
+
|
|
274
|
+
// flush remaining work items
|
|
275
|
+
conn.flush(err);
|
|
264
276
|
}
|
|
265
277
|
|
|
266
278
|
// Did we catch a timeout, increment the numberOfConsecutiveTimeouts
|
|
@@ -280,8 +292,10 @@ function connectionFailureHandler(pool, event, err, conn) {
|
|
|
280
292
|
|
|
281
293
|
// No more socket available propegate the event
|
|
282
294
|
if (pool.socketCount() === 0) {
|
|
283
|
-
if (pool.state !== DESTROYED && pool.state !== DESTROYING) {
|
|
284
|
-
|
|
295
|
+
if (pool.state !== DESTROYED && pool.state !== DESTROYING && pool.state !== DRAINING) {
|
|
296
|
+
if (pool.options.reconnect) {
|
|
297
|
+
stateTransition(pool, DISCONNECTED);
|
|
298
|
+
}
|
|
285
299
|
}
|
|
286
300
|
|
|
287
301
|
// Do not emit error events, they are always close events
|
|
@@ -320,9 +334,7 @@ function attemptReconnect(pool, callback) {
|
|
|
320
334
|
pool.destroy();
|
|
321
335
|
|
|
322
336
|
const error = new MongoTimeoutError(
|
|
323
|
-
`failed to reconnect after ${pool.options.reconnectTries} attempts with interval ${
|
|
324
|
-
pool.options.reconnectInterval
|
|
325
|
-
} ms`,
|
|
337
|
+
`failed to reconnect after ${pool.options.reconnectTries} attempts with interval ${pool.options.reconnectInterval} ms`,
|
|
326
338
|
pool.reconnectError
|
|
327
339
|
);
|
|
328
340
|
|
|
@@ -436,7 +448,7 @@ function messageHandler(self) {
|
|
|
436
448
|
updateSessionFromResponse(session, document);
|
|
437
449
|
}
|
|
438
450
|
|
|
439
|
-
if (document.$clusterTime) {
|
|
451
|
+
if (self.topology && document.$clusterTime) {
|
|
440
452
|
self.topology.clusterTime = document.$clusterTime;
|
|
441
453
|
}
|
|
442
454
|
}
|
|
@@ -547,7 +559,7 @@ Pool.prototype.isDisconnected = function() {
|
|
|
547
559
|
/**
|
|
548
560
|
* Connect pool
|
|
549
561
|
*/
|
|
550
|
-
Pool.prototype.connect = function() {
|
|
562
|
+
Pool.prototype.connect = function(callback) {
|
|
551
563
|
if (this.state !== DISCONNECTED) {
|
|
552
564
|
throw new MongoError('connection in unlawful state ' + this.state);
|
|
553
565
|
}
|
|
@@ -555,6 +567,12 @@ Pool.prototype.connect = function() {
|
|
|
555
567
|
stateTransition(this, CONNECTING);
|
|
556
568
|
createConnection(this, (err, conn) => {
|
|
557
569
|
if (err) {
|
|
570
|
+
if (typeof callback === 'function') {
|
|
571
|
+
this.destroy();
|
|
572
|
+
callback(err);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
|
|
558
576
|
if (this.state === CONNECTING) {
|
|
559
577
|
this.emit('error', err);
|
|
560
578
|
}
|
|
@@ -564,7 +582,6 @@ Pool.prototype.connect = function() {
|
|
|
564
582
|
}
|
|
565
583
|
|
|
566
584
|
stateTransition(this, CONNECTED);
|
|
567
|
-
this.emit('connect', this, conn);
|
|
568
585
|
|
|
569
586
|
// create min connections
|
|
570
587
|
if (this.minSize) {
|
|
@@ -572,6 +589,12 @@ Pool.prototype.connect = function() {
|
|
|
572
589
|
createConnection(this);
|
|
573
590
|
}
|
|
574
591
|
}
|
|
592
|
+
|
|
593
|
+
if (typeof callback === 'function') {
|
|
594
|
+
callback(null, conn);
|
|
595
|
+
} else {
|
|
596
|
+
this.emit('connect', this, conn);
|
|
597
|
+
}
|
|
575
598
|
});
|
|
576
599
|
};
|
|
577
600
|
|
|
@@ -606,6 +629,11 @@ Pool.prototype.unref = function() {
|
|
|
606
629
|
|
|
607
630
|
// Destroy the connections
|
|
608
631
|
function destroy(self, connections, options, callback) {
|
|
632
|
+
stateTransition(self, DESTROYING);
|
|
633
|
+
|
|
634
|
+
// indicate that in-flight connections should cancel
|
|
635
|
+
self.s.cancellationToken.emit('cancel');
|
|
636
|
+
|
|
609
637
|
eachAsync(
|
|
610
638
|
connections,
|
|
611
639
|
(conn, cb) => {
|
|
@@ -636,14 +664,19 @@ function destroy(self, connections, options, callback) {
|
|
|
636
664
|
*/
|
|
637
665
|
Pool.prototype.destroy = function(force, callback) {
|
|
638
666
|
var self = this;
|
|
667
|
+
if (typeof force === 'function') {
|
|
668
|
+
callback = force;
|
|
669
|
+
force = false;
|
|
670
|
+
}
|
|
671
|
+
|
|
639
672
|
// Do not try again if the pool is already dead
|
|
640
673
|
if (this.state === DESTROYED || self.state === DESTROYING) {
|
|
641
674
|
if (typeof callback === 'function') callback(null, null);
|
|
642
675
|
return;
|
|
643
676
|
}
|
|
644
677
|
|
|
645
|
-
// Set state to
|
|
646
|
-
stateTransition(this,
|
|
678
|
+
// Set state to draining
|
|
679
|
+
stateTransition(this, DRAINING);
|
|
647
680
|
|
|
648
681
|
// Are we force closing
|
|
649
682
|
if (force) {
|
|
@@ -670,6 +703,14 @@ Pool.prototype.destroy = function(force, callback) {
|
|
|
670
703
|
|
|
671
704
|
// Wait for the operations to drain before we close the pool
|
|
672
705
|
function checkStatus() {
|
|
706
|
+
if (self.state === DESTROYED || self.state === DESTROYING) {
|
|
707
|
+
if (typeof callback === 'function') {
|
|
708
|
+
callback();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
|
|
673
714
|
flushMonitoringOperations(self.queue);
|
|
674
715
|
|
|
675
716
|
if (self.queue.length === 0) {
|
|
@@ -686,7 +727,6 @@ Pool.prototype.destroy = function(force, callback) {
|
|
|
686
727
|
}
|
|
687
728
|
|
|
688
729
|
destroy(self, connections, { force: false }, callback);
|
|
689
|
-
// } else if (self.queue.length > 0 && !this.reconnectId) {
|
|
690
730
|
} else {
|
|
691
731
|
// Ensure we empty the queue
|
|
692
732
|
_execute(self)();
|
|
@@ -705,6 +745,18 @@ Pool.prototype.destroy = function(force, callback) {
|
|
|
705
745
|
* @param {function} [callback]
|
|
706
746
|
*/
|
|
707
747
|
Pool.prototype.reset = function(callback) {
|
|
748
|
+
if (this.s.state !== CONNECTED) {
|
|
749
|
+
if (typeof callback === 'function') {
|
|
750
|
+
callback(new MongoError('pool is not connected, reset aborted'));
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// signal in-flight connections should be cancelled
|
|
757
|
+
this.s.cancellationToken.emit('cancel');
|
|
758
|
+
|
|
759
|
+
// destroy existing connections
|
|
708
760
|
const connections = this.availableConnections.concat(this.inUseConnections);
|
|
709
761
|
eachAsync(
|
|
710
762
|
connections,
|
|
@@ -725,12 +777,12 @@ Pool.prototype.reset = function(callback) {
|
|
|
725
777
|
|
|
726
778
|
resetPoolState(this);
|
|
727
779
|
|
|
728
|
-
// create
|
|
729
|
-
createConnection(this)
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
780
|
+
// create a new connection, this will ultimately trigger execution
|
|
781
|
+
createConnection(this, () => {
|
|
782
|
+
if (typeof callback === 'function') {
|
|
783
|
+
callback(null, null);
|
|
784
|
+
}
|
|
785
|
+
});
|
|
734
786
|
}
|
|
735
787
|
);
|
|
736
788
|
};
|
|
@@ -798,17 +850,12 @@ Pool.prototype.write = function(command, options, cb) {
|
|
|
798
850
|
|
|
799
851
|
// Pool was destroyed error out
|
|
800
852
|
if (this.state === DESTROYED || this.state === DESTROYING) {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
cb(new MongoError('pool destroyed'));
|
|
805
|
-
} catch (err) {
|
|
806
|
-
process.nextTick(function() {
|
|
807
|
-
throw err;
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
}
|
|
853
|
+
cb(new MongoError('pool destroyed'));
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
811
856
|
|
|
857
|
+
if (this.state === DRAINING) {
|
|
858
|
+
cb(new MongoError('pool is draining, new operations prohibited'));
|
|
812
859
|
return;
|
|
813
860
|
}
|
|
814
861
|
|
|
@@ -856,10 +903,6 @@ Pool.prototype.write = function(command, options, cb) {
|
|
|
856
903
|
// Optional per operation socketTimeout
|
|
857
904
|
operation.socketTimeout = options.socketTimeout;
|
|
858
905
|
operation.monitoring = options.monitoring;
|
|
859
|
-
// Custom socket Timeout
|
|
860
|
-
if (options.socketTimeout) {
|
|
861
|
-
operation.socketTimeout = options.socketTimeout;
|
|
862
|
-
}
|
|
863
906
|
|
|
864
907
|
// Get the requestId
|
|
865
908
|
operation.requestId = command.requestId;
|
|
@@ -922,7 +965,7 @@ Pool.prototype.write = function(command, options, cb) {
|
|
|
922
965
|
function canCompress(command) {
|
|
923
966
|
const commandDoc = command instanceof Msg ? command.command : command.query;
|
|
924
967
|
const commandName = Object.keys(commandDoc)[0];
|
|
925
|
-
return uncompressibleCommands.
|
|
968
|
+
return !uncompressibleCommands.has(commandName);
|
|
926
969
|
}
|
|
927
970
|
|
|
928
971
|
// Remove connection method
|
|
@@ -950,7 +993,7 @@ function createConnection(pool, callback) {
|
|
|
950
993
|
}
|
|
951
994
|
|
|
952
995
|
pool.connectingConnections++;
|
|
953
|
-
connect(pool.options, (err, connection) => {
|
|
996
|
+
connect(pool.options, pool.s.cancellationToken, (err, connection) => {
|
|
954
997
|
pool.connectingConnections--;
|
|
955
998
|
|
|
956
999
|
if (err) {
|
|
@@ -958,15 +1001,6 @@ function createConnection(pool, callback) {
|
|
|
958
1001
|
pool.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
|
|
959
1002
|
}
|
|
960
1003
|
|
|
961
|
-
if (pool.options.legacyCompatMode === false) {
|
|
962
|
-
// The unified topology uses the reported `error` from a pool to track what error
|
|
963
|
-
// reason is returned to the user during selection timeout. We only want to emit
|
|
964
|
-
// this if the pool is active because the listeners are removed on destruction.
|
|
965
|
-
if (pool.state !== DESTROYED && pool.state !== DESTROYING) {
|
|
966
|
-
pool.emit('error', err);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
1004
|
// check if reconnect is enabled, and attempt retry if so
|
|
971
1005
|
if (!pool.reconnectId && pool.options.reconnect) {
|
|
972
1006
|
if (pool.state === CONNECTING && pool.options.legacyCompatMode) {
|
|
@@ -1057,6 +1091,12 @@ function _execute(self) {
|
|
|
1057
1091
|
if (self.availableConnections.length === 0) {
|
|
1058
1092
|
// Flush any monitoring operations
|
|
1059
1093
|
flushMonitoringOperations(self.queue);
|
|
1094
|
+
|
|
1095
|
+
// Try to create a new connection to execute stuck operation
|
|
1096
|
+
if (totalConnections < self.options.size && self.queue.length > 0) {
|
|
1097
|
+
createConnection(self);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1060
1100
|
break;
|
|
1061
1101
|
}
|
|
1062
1102
|
|
|
@@ -1121,10 +1161,7 @@ function _execute(self) {
|
|
|
1121
1161
|
}
|
|
1122
1162
|
|
|
1123
1163
|
// Re-execute the operation
|
|
1124
|
-
setTimeout(
|
|
1125
|
-
_execute(self)();
|
|
1126
|
-
}, 10);
|
|
1127
|
-
|
|
1164
|
+
setTimeout(() => _execute(self)(), 10);
|
|
1128
1165
|
break;
|
|
1129
1166
|
}
|
|
1130
1167
|
}
|
package/lib/core/cursor.js
CHANGED
package/lib/core/error.js
CHANGED
|
@@ -44,25 +44,29 @@ class MongoError extends Error {
|
|
|
44
44
|
return new MongoError(options);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Checks the error to see if it has an error label
|
|
49
|
+
* @param {string} label The error label to check for
|
|
50
|
+
* @returns {boolean} returns true if the error has the provided error label
|
|
51
|
+
*/
|
|
47
52
|
hasErrorLabel(label) {
|
|
48
53
|
return this.errorLabels && this.errorLabels.indexOf(label) !== -1;
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
/**
|
|
53
|
-
*
|
|
58
|
+
* An error indicating an issue with the network, including TCP
|
|
59
|
+
* errors and timeouts.
|
|
54
60
|
*
|
|
55
61
|
* @param {Error|string|object} message The error message
|
|
56
62
|
* @property {string} message The error message
|
|
57
63
|
* @property {string} stack The error call stack
|
|
64
|
+
* @extends MongoError
|
|
58
65
|
*/
|
|
59
66
|
class MongoNetworkError extends MongoError {
|
|
60
67
|
constructor(message) {
|
|
61
68
|
super(message);
|
|
62
69
|
this.name = 'MongoNetworkError';
|
|
63
|
-
|
|
64
|
-
// This is added as part of the transactions specification
|
|
65
|
-
this.errorLabels = ['TransientTransactionError'];
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
|
|
@@ -71,6 +75,7 @@ class MongoNetworkError extends MongoError {
|
|
|
71
75
|
*
|
|
72
76
|
* @param {Error|string|object} message The error message
|
|
73
77
|
* @property {string} message The error message
|
|
78
|
+
* @extends MongoError
|
|
74
79
|
*/
|
|
75
80
|
class MongoParseError extends MongoError {
|
|
76
81
|
constructor(message) {
|
|
@@ -80,12 +85,13 @@ class MongoParseError extends MongoError {
|
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
/**
|
|
83
|
-
* An error signifying a timeout event
|
|
88
|
+
* An error signifying a client-side timeout event
|
|
84
89
|
*
|
|
85
90
|
* @param {Error|string|object} message The error message
|
|
86
91
|
* @param {string|object} [reason] The reason the timeout occured
|
|
87
92
|
* @property {string} message The error message
|
|
88
93
|
* @property {string} [reason] An optional reason context for the timeout, generally an error saved during flow of monitoring and selecting servers
|
|
94
|
+
* @extends MongoError
|
|
89
95
|
*/
|
|
90
96
|
class MongoTimeoutError extends MongoError {
|
|
91
97
|
constructor(message, reason) {
|
|
@@ -117,6 +123,7 @@ function makeWriteConcernResultObject(input) {
|
|
|
117
123
|
* @param {object} result The result document (provided if ok: 1)
|
|
118
124
|
* @property {string} message The error message
|
|
119
125
|
* @property {object} [result] The result document (provided if ok: 1)
|
|
126
|
+
* @extends MongoError
|
|
120
127
|
*/
|
|
121
128
|
class MongoWriteConcernError extends MongoError {
|
|
122
129
|
constructor(message, result) {
|
|
@@ -147,6 +154,7 @@ const RETRYABLE_ERROR_CODES = new Set([
|
|
|
147
154
|
/**
|
|
148
155
|
* Determines whether an error is something the driver should attempt to retry
|
|
149
156
|
*
|
|
157
|
+
* @ignore
|
|
150
158
|
* @param {MongoError|Error} error
|
|
151
159
|
*/
|
|
152
160
|
function isRetryableError(error) {
|
|
@@ -205,12 +213,15 @@ function isNodeShuttingDownError(err) {
|
|
|
205
213
|
* then the pool will be cleared, and server state will completely reset
|
|
206
214
|
* locally.
|
|
207
215
|
*
|
|
216
|
+
* @ignore
|
|
208
217
|
* @see https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-master-and-node-is-recovering
|
|
209
218
|
* @param {MongoError|Error} error
|
|
210
219
|
* @param {Server} server
|
|
211
220
|
*/
|
|
212
221
|
function isSDAMUnrecoverableError(error, server) {
|
|
213
|
-
|
|
222
|
+
// NOTE: null check is here for a strictly pre-CMAP world, a timeout or
|
|
223
|
+
// close event are considered unrecoverable
|
|
224
|
+
if (error instanceof MongoParseError || error == null) {
|
|
214
225
|
return true;
|
|
215
226
|
}
|
|
216
227
|
|
package/lib/core/index.js
CHANGED
|
@@ -33,7 +33,7 @@ module.exports = {
|
|
|
33
33
|
Sessions: require('./sessions'),
|
|
34
34
|
BSON: BSON,
|
|
35
35
|
EJSON: EJSON,
|
|
36
|
-
Topology: require('./sdam/topology'),
|
|
36
|
+
Topology: require('./sdam/topology').Topology,
|
|
37
37
|
// Raw operations
|
|
38
38
|
Query: require('./connection/commands').Query,
|
|
39
39
|
// Auth mechanisms
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// shared state names
|
|
4
|
+
const STATE_CLOSING = 'closing';
|
|
5
|
+
const STATE_CLOSED = 'closed';
|
|
6
|
+
const STATE_CONNECTING = 'connecting';
|
|
7
|
+
const STATE_CONNECTED = 'connected';
|
|
8
|
+
|
|
9
|
+
// An enumeration of topology types we know about
|
|
10
|
+
const TopologyType = {
|
|
11
|
+
Single: 'Single',
|
|
12
|
+
ReplicaSetNoPrimary: 'ReplicaSetNoPrimary',
|
|
13
|
+
ReplicaSetWithPrimary: 'ReplicaSetWithPrimary',
|
|
14
|
+
Sharded: 'Sharded',
|
|
15
|
+
Unknown: 'Unknown'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// An enumeration of server types we know about
|
|
19
|
+
const ServerType = {
|
|
20
|
+
Standalone: 'Standalone',
|
|
21
|
+
Mongos: 'Mongos',
|
|
22
|
+
PossiblePrimary: 'PossiblePrimary',
|
|
23
|
+
RSPrimary: 'RSPrimary',
|
|
24
|
+
RSSecondary: 'RSSecondary',
|
|
25
|
+
RSArbiter: 'RSArbiter',
|
|
26
|
+
RSOther: 'RSOther',
|
|
27
|
+
RSGhost: 'RSGhost',
|
|
28
|
+
Unknown: 'Unknown'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const TOPOLOGY_DEFAULTS = {
|
|
32
|
+
useUnifiedTopology: true,
|
|
33
|
+
localThresholdMS: 15,
|
|
34
|
+
serverSelectionTimeoutMS: 30000,
|
|
35
|
+
heartbeatFrequencyMS: 10000,
|
|
36
|
+
minHeartbeatFrequencyMS: 500
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function drainTimerQueue(queue) {
|
|
40
|
+
queue.forEach(clearTimeout);
|
|
41
|
+
queue.clear();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function clearAndRemoveTimerFrom(timer, timers) {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
return timers.delete(timer);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
STATE_CLOSING,
|
|
51
|
+
STATE_CLOSED,
|
|
52
|
+
STATE_CONNECTING,
|
|
53
|
+
STATE_CONNECTED,
|
|
54
|
+
TOPOLOGY_DEFAULTS,
|
|
55
|
+
TopologyType,
|
|
56
|
+
ServerType,
|
|
57
|
+
drainTimerQueue,
|
|
58
|
+
clearAndRemoveTimerFrom
|
|
59
|
+
};
|
|
@@ -4,8 +4,8 @@ const ServerDescription = require('./server_description').ServerDescription;
|
|
|
4
4
|
const calculateDurationInMs = require('../utils').calculateDurationInMs;
|
|
5
5
|
|
|
6
6
|
// pulled from `Server` implementation
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const STATE_CLOSED = 'closed';
|
|
8
|
+
const STATE_CLOSING = 'closing';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Published when server description changes, but does NOT include changes to the RTT.
|
|
@@ -135,6 +135,14 @@ function monitorServer(server, options) {
|
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
const rescheduleMonitoring = () => {
|
|
139
|
+
server.s.monitoring = false;
|
|
140
|
+
server.s.monitorId = setTimeout(() => {
|
|
141
|
+
server.s.monitorId = undefined;
|
|
142
|
+
server.monitor();
|
|
143
|
+
}, heartbeatFrequencyMS);
|
|
144
|
+
};
|
|
145
|
+
|
|
138
146
|
// executes a single check of a server
|
|
139
147
|
const checkServer = callback => {
|
|
140
148
|
let start = process.hrtime();
|
|
@@ -164,6 +172,9 @@ function monitorServer(server, options) {
|
|
|
164
172
|
return callback(err, null);
|
|
165
173
|
}
|
|
166
174
|
|
|
175
|
+
// save round trip time
|
|
176
|
+
server.description.roundTripTime = duration;
|
|
177
|
+
|
|
167
178
|
const isMaster = result.result;
|
|
168
179
|
server.emit(
|
|
169
180
|
'serverHeartbeatSucceeded',
|
|
@@ -176,16 +187,13 @@ function monitorServer(server, options) {
|
|
|
176
187
|
};
|
|
177
188
|
|
|
178
189
|
const successHandler = isMaster => {
|
|
179
|
-
server.s.monitoring = false;
|
|
180
|
-
|
|
181
190
|
// emit an event indicating that our description has changed
|
|
182
191
|
server.emit('descriptionReceived', new ServerDescription(server.description.address, isMaster));
|
|
183
|
-
if (server.s.state ===
|
|
192
|
+
if (server.s.state === STATE_CLOSED || server.s.state === STATE_CLOSING) {
|
|
184
193
|
return;
|
|
185
194
|
}
|
|
186
195
|
|
|
187
|
-
|
|
188
|
-
server.s.monitorId = setTimeout(() => monitorServer(server), heartbeatFrequencyMS);
|
|
196
|
+
rescheduleMonitoring();
|
|
189
197
|
};
|
|
190
198
|
|
|
191
199
|
// run the actual monitoring loop
|
|
@@ -203,15 +211,13 @@ function monitorServer(server, options) {
|
|
|
203
211
|
// otherwise re-attempt monitoring once
|
|
204
212
|
checkServer((error, isMaster) => {
|
|
205
213
|
if (error) {
|
|
206
|
-
server.s.monitoring = false;
|
|
207
|
-
|
|
208
214
|
// we revert to an `Unknown` by emitting a default description with no isMaster
|
|
209
215
|
server.emit(
|
|
210
216
|
'descriptionReceived',
|
|
211
217
|
new ServerDescription(server.description.address, null, { error })
|
|
212
218
|
);
|
|
213
219
|
|
|
214
|
-
|
|
220
|
+
rescheduleMonitoring();
|
|
215
221
|
return;
|
|
216
222
|
}
|
|
217
223
|
|