mongodb 3.5.8 → 3.5.9
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 +17 -0
- package/lib/change_stream.js +14 -6
- package/lib/cmap/connection.js +6 -4
- package/lib/core/connection/apm.js +1 -1
- package/lib/core/connection/pool.js +2 -1
- package/lib/core/sdam/monitor.js +51 -58
- package/lib/core/sdam/server.js +4 -0
- package/lib/core/sdam/server_description.js +2 -1
- package/lib/core/sdam/server_selection.js +7 -1
- package/lib/core/sessions.js +7 -6
- package/lib/core/topologies/replset.js +4 -4
- package/lib/core/utils.js +0 -12
- package/lib/core/wireprotocol/get_more.js +6 -1
- package/lib/mongo_client.js +8 -0
- package/lib/operations/connect.js +5 -0
- package/lib/topologies/native_topology.js +12 -2
- package/lib/utils.js +96 -1
- package/package.json +1 -1
package/HISTORY.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
<a name="3.5.9"></a>
|
|
6
|
+
## [3.5.9](https://github.com/mongodb/node-mongodb-native/compare/v3.5.8...v3.5.9) (2020-06-12)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* don't try to calculate sMax if there are no viable servers ([be51347](https://github.com/mongodb/node-mongodb-native/commit/be51347))
|
|
12
|
+
* use async interruptable interval for server monitoring ([1f855a4](https://github.com/mongodb/node-mongodb-native/commit/1f855a4))
|
|
13
|
+
* use duration of handshake if no previous roundTripTime exists ([ddfa41b](https://github.com/mongodb/node-mongodb-native/commit/ddfa41b))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* introduce an interruptable async interval timer ([9e12cd5](https://github.com/mongodb/node-mongodb-native/commit/9e12cd5))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
5
22
|
<a name="3.5.8"></a>
|
|
6
23
|
## [3.5.8](https://github.com/mongodb/node-mongodb-native/compare/v3.5.7...v3.5.8) (2020-05-28)
|
|
7
24
|
|
package/lib/change_stream.js
CHANGED
|
@@ -8,6 +8,8 @@ const Cursor = require('./cursor');
|
|
|
8
8
|
const relayEvents = require('./core/utils').relayEvents;
|
|
9
9
|
const maxWireVersion = require('./core/utils').maxWireVersion;
|
|
10
10
|
const maybePromise = require('./utils').maybePromise;
|
|
11
|
+
const now = require('./utils').now;
|
|
12
|
+
const calculateDurationInMs = require('./utils').calculateDurationInMs;
|
|
11
13
|
const AggregateOperation = require('./operations/aggregate');
|
|
12
14
|
|
|
13
15
|
const kResumeQueue = Symbol('resumeQueue');
|
|
@@ -459,15 +461,21 @@ function applyKnownOptions(target, source, optionNames) {
|
|
|
459
461
|
const SELECTION_TIMEOUT = 30000;
|
|
460
462
|
function waitForTopologyConnected(topology, options, callback) {
|
|
461
463
|
setTimeout(() => {
|
|
462
|
-
if (options && options.start == null)
|
|
463
|
-
|
|
464
|
+
if (options && options.start == null) {
|
|
465
|
+
options.start = now();
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const start = options.start || now();
|
|
464
469
|
const timeout = options.timeout || SELECTION_TIMEOUT;
|
|
465
470
|
const readPreference = options.readPreference;
|
|
471
|
+
if (topology.isConnected({ readPreference })) {
|
|
472
|
+
return callback();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (calculateDurationInMs(start) > timeout) {
|
|
476
|
+
return callback(new MongoError('Timed out waiting for connection'));
|
|
477
|
+
}
|
|
466
478
|
|
|
467
|
-
if (topology.isConnected({ readPreference })) return callback();
|
|
468
|
-
const hrElapsed = process.hrtime(start);
|
|
469
|
-
const elapsed = (hrElapsed[0] * 1e9 + hrElapsed[1]) / 1e6;
|
|
470
|
-
if (elapsed > timeout) return callback(new MongoError('Timed out waiting for connection'));
|
|
471
479
|
waitForTopologyConnected(topology, options, callback);
|
|
472
480
|
}, 500); // this is an arbitrary wait time to allow SDAM to transition
|
|
473
481
|
}
|
package/lib/cmap/connection.js
CHANGED
|
@@ -11,6 +11,8 @@ const wp = require('../core/wireprotocol');
|
|
|
11
11
|
const apm = require('../core/connection/apm');
|
|
12
12
|
const updateSessionFromResponse = require('../core/sessions').updateSessionFromResponse;
|
|
13
13
|
const uuidV4 = require('../core/utils').uuidV4;
|
|
14
|
+
const now = require('../utils').now;
|
|
15
|
+
const calculateDurationInMs = require('../utils').calculateDurationInMs;
|
|
14
16
|
|
|
15
17
|
const kStream = Symbol('stream');
|
|
16
18
|
const kQueue = Symbol('queue');
|
|
@@ -37,7 +39,7 @@ class Connection extends EventEmitter {
|
|
|
37
39
|
|
|
38
40
|
this[kDescription] = new StreamDescription(this.address, options);
|
|
39
41
|
this[kGeneration] = options.generation;
|
|
40
|
-
this[kLastUseTime] =
|
|
42
|
+
this[kLastUseTime] = now();
|
|
41
43
|
|
|
42
44
|
// retain a reference to an `AutoEncrypter` if present
|
|
43
45
|
if (options.autoEncrypter) {
|
|
@@ -108,7 +110,7 @@ class Connection extends EventEmitter {
|
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
get idleTime() {
|
|
111
|
-
return
|
|
113
|
+
return calculateDurationInMs(this[kLastUseTime]);
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
get clusterTime() {
|
|
@@ -120,7 +122,7 @@ class Connection extends EventEmitter {
|
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
markAvailable() {
|
|
123
|
-
this[kLastUseTime] =
|
|
125
|
+
this[kLastUseTime] = now();
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
destroy(options, callback) {
|
|
@@ -326,7 +328,7 @@ function write(command, options, callback) {
|
|
|
326
328
|
if (this.monitorCommands) {
|
|
327
329
|
this.emit('commandStarted', new apm.CommandStartedEvent(this, command));
|
|
328
330
|
|
|
329
|
-
operationDescription.started =
|
|
331
|
+
operationDescription.started = now();
|
|
330
332
|
operationDescription.cb = (err, reply) => {
|
|
331
333
|
if (err) {
|
|
332
334
|
this.emit(
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Msg = require('../connection/msg').Msg;
|
|
3
3
|
const KillCursor = require('../connection/commands').KillCursor;
|
|
4
4
|
const GetMore = require('../connection/commands').GetMore;
|
|
5
|
-
const calculateDurationInMs = require('
|
|
5
|
+
const calculateDurationInMs = require('../../utils').calculateDurationInMs;
|
|
6
6
|
|
|
7
7
|
/** Commands that we want to redact because of the sensitive nature of their contents */
|
|
8
8
|
const SENSITIVE_COMMANDS = new Set([
|
|
@@ -21,6 +21,7 @@ const connect = require('./connect');
|
|
|
21
21
|
const updateSessionFromResponse = require('../sessions').updateSessionFromResponse;
|
|
22
22
|
const eachAsync = require('../utils').eachAsync;
|
|
23
23
|
const makeStateMachine = require('../utils').makeStateMachine;
|
|
24
|
+
const now = require('../../utils').now;
|
|
24
25
|
|
|
25
26
|
const DISCONNECTED = 'disconnected';
|
|
26
27
|
const CONNECTING = 'connecting';
|
|
@@ -898,7 +899,7 @@ Pool.prototype.write = function(command, options, cb) {
|
|
|
898
899
|
if (self.options.monitorCommands) {
|
|
899
900
|
this.emit('commandStarted', new apm.CommandStartedEvent(this, command));
|
|
900
901
|
|
|
901
|
-
operation.started =
|
|
902
|
+
operation.started = now();
|
|
902
903
|
operation.cb = (err, reply) => {
|
|
903
904
|
if (err) {
|
|
904
905
|
self.emit(
|
package/lib/core/sdam/monitor.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const ServerType = require('./common').ServerType;
|
|
4
|
-
const calculateDurationInMs = require('../utils').calculateDurationInMs;
|
|
5
4
|
const EventEmitter = require('events');
|
|
6
5
|
const connect = require('../connection/connect');
|
|
7
6
|
const Connection = require('../../cmap/connection').Connection;
|
|
8
7
|
const common = require('./common');
|
|
9
8
|
const makeStateMachine = require('../utils').makeStateMachine;
|
|
10
9
|
const MongoError = require('../error').MongoError;
|
|
10
|
+
const makeInterruptableAsyncInterval = require('../../utils').makeInterruptableAsyncInterval;
|
|
11
|
+
const calculateDurationInMs = require('../../utils').calculateDurationInMs;
|
|
12
|
+
const now = require('../../utils').now;
|
|
11
13
|
|
|
12
14
|
const sdamEvents = require('./events');
|
|
13
15
|
const ServerHeartbeatStartedEvent = sdamEvents.ServerHeartbeatStartedEvent;
|
|
@@ -18,7 +20,6 @@ const kServer = Symbol('server');
|
|
|
18
20
|
const kMonitorId = Symbol('monitorId');
|
|
19
21
|
const kConnection = Symbol('connection');
|
|
20
22
|
const kCancellationToken = Symbol('cancellationToken');
|
|
21
|
-
const kLastCheckTime = Symbol('lastCheckTime');
|
|
22
23
|
|
|
23
24
|
const STATE_CLOSED = common.STATE_CLOSED;
|
|
24
25
|
const STATE_CLOSING = common.STATE_CLOSING;
|
|
@@ -33,6 +34,10 @@ const stateTransition = makeStateMachine({
|
|
|
33
34
|
|
|
34
35
|
const INVALID_REQUEST_CHECK_STATES = new Set([STATE_CLOSING, STATE_CLOSED, STATE_MONITORING]);
|
|
35
36
|
|
|
37
|
+
function isInCloseState(monitor) {
|
|
38
|
+
return monitor.s.state === STATE_CLOSED || monitor.s.state === STATE_CLOSING;
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
class Monitor extends EventEmitter {
|
|
37
42
|
constructor(server, options) {
|
|
38
43
|
super(options);
|
|
@@ -41,6 +46,7 @@ class Monitor extends EventEmitter {
|
|
|
41
46
|
this[kConnection] = undefined;
|
|
42
47
|
this[kCancellationToken] = new EventEmitter();
|
|
43
48
|
this[kCancellationToken].setMaxListeners(Infinity);
|
|
49
|
+
this[kMonitorId] = null;
|
|
44
50
|
this.s = {
|
|
45
51
|
state: STATE_CLOSED
|
|
46
52
|
};
|
|
@@ -89,7 +95,14 @@ class Monitor extends EventEmitter {
|
|
|
89
95
|
return;
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
// start
|
|
99
|
+
const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
|
|
100
|
+
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
|
101
|
+
this[kMonitorId] = makeInterruptableAsyncInterval(monitorServer(this), {
|
|
102
|
+
interval: heartbeatFrequencyMS,
|
|
103
|
+
minInterval: minHeartbeatFrequencyMS,
|
|
104
|
+
immediate: true
|
|
105
|
+
});
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
requestCheck() {
|
|
@@ -97,31 +110,19 @@ class Monitor extends EventEmitter {
|
|
|
97
110
|
return;
|
|
98
111
|
}
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
|
102
|
-
const remainingTime = heartbeatFrequencyMS - calculateDurationInMs(this[kLastCheckTime]);
|
|
103
|
-
if (remainingTime > minHeartbeatFrequencyMS && this[kMonitorId]) {
|
|
104
|
-
clearTimeout(this[kMonitorId]);
|
|
105
|
-
rescheduleMonitoring(this, minHeartbeatFrequencyMS);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (this[kMonitorId]) {
|
|
110
|
-
clearTimeout(this[kMonitorId]);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
monitorServer(this);
|
|
113
|
+
this[kMonitorId].wake();
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
close() {
|
|
117
|
-
if (this
|
|
117
|
+
if (isInCloseState(this)) {
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
stateTransition(this, STATE_CLOSING);
|
|
122
122
|
this[kCancellationToken].emit('cancel');
|
|
123
123
|
if (this[kMonitorId]) {
|
|
124
|
-
|
|
124
|
+
this[kMonitorId].stop();
|
|
125
|
+
this[kMonitorId] = null;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
if (this[kConnection]) {
|
|
@@ -138,7 +139,7 @@ function checkServer(monitor, callback) {
|
|
|
138
139
|
monitor[kConnection] = undefined;
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
const start =
|
|
142
|
+
const start = now();
|
|
142
143
|
monitor.emit('serverHeartbeatStarted', new ServerHeartbeatStartedEvent(monitor.address));
|
|
143
144
|
|
|
144
145
|
function failureHandler(err) {
|
|
@@ -186,7 +187,7 @@ function checkServer(monitor, callback) {
|
|
|
186
187
|
return;
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
if (monitor
|
|
190
|
+
if (isInCloseState(monitor)) {
|
|
190
191
|
conn.destroy({ force: true });
|
|
191
192
|
failureHandler(new MongoError('monitor was destroyed'));
|
|
192
193
|
return;
|
|
@@ -198,52 +199,44 @@ function checkServer(monitor, callback) {
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
function monitorServer(monitor) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
return callback => {
|
|
203
|
+
stateTransition(monitor, STATE_MONITORING);
|
|
204
|
+
function done() {
|
|
205
|
+
if (!isInCloseState(monitor)) {
|
|
206
|
+
stateTransition(monitor, STATE_IDLE);
|
|
207
|
+
}
|
|
205
208
|
|
|
206
|
-
|
|
207
|
-
if (e0 == null) {
|
|
208
|
-
rescheduleMonitoring(monitor);
|
|
209
|
-
return;
|
|
209
|
+
callback();
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
monitor.emit('resetServer', e0);
|
|
215
|
-
rescheduleMonitoring(monitor);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
212
|
+
// TODO: the next line is a legacy event, remove in v4
|
|
213
|
+
process.nextTick(() => monitor.emit('monitoring', monitor[kServer]));
|
|
218
214
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
monitor.emit('resetConnectionPool');
|
|
223
|
-
|
|
224
|
-
checkServer(monitor, e1 => {
|
|
225
|
-
if (e1) {
|
|
226
|
-
monitor.emit('resetServer', e1);
|
|
215
|
+
checkServer(monitor, e0 => {
|
|
216
|
+
if (e0 == null) {
|
|
217
|
+
return done();
|
|
227
218
|
}
|
|
228
219
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
220
|
+
// otherwise an error occured on initial discovery, also bail
|
|
221
|
+
if (monitor[kServer].description.type === ServerType.Unknown) {
|
|
222
|
+
monitor.emit('resetServer', e0);
|
|
223
|
+
return done();
|
|
224
|
+
}
|
|
233
225
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
226
|
+
// According to the SDAM specification's "Network error during server check" section, if
|
|
227
|
+
// an ismaster call fails we reset the server's pool. If a server was once connected,
|
|
228
|
+
// change its type to `Unknown` only after retrying once.
|
|
229
|
+
monitor.emit('resetConnectionPool');
|
|
239
230
|
|
|
240
|
-
|
|
231
|
+
checkServer(monitor, e1 => {
|
|
232
|
+
if (e1) {
|
|
233
|
+
monitor.emit('resetServer', e1);
|
|
234
|
+
}
|
|
241
235
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}, ms || heartbeatFrequencyMS);
|
|
236
|
+
done();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
};
|
|
247
240
|
}
|
|
248
241
|
|
|
249
242
|
module.exports = {
|
package/lib/core/sdam/server.js
CHANGED
|
@@ -415,6 +415,10 @@ Object.defineProperty(Server.prototype, 'clusterTime', {
|
|
|
415
415
|
});
|
|
416
416
|
|
|
417
417
|
function calculateRoundTripTime(oldRtt, duration) {
|
|
418
|
+
if (oldRtt === -1) {
|
|
419
|
+
return duration;
|
|
420
|
+
}
|
|
421
|
+
|
|
418
422
|
const alpha = 0.2;
|
|
419
423
|
return alpha * duration + (1 - alpha) * oldRtt;
|
|
420
424
|
}
|
|
@@ -4,6 +4,7 @@ const arrayStrictEqual = require('../utils').arrayStrictEqual;
|
|
|
4
4
|
const tagsStrictEqual = require('../utils').tagsStrictEqual;
|
|
5
5
|
const errorStrictEqual = require('../utils').errorStrictEqual;
|
|
6
6
|
const ServerType = require('./common').ServerType;
|
|
7
|
+
const now = require('../../utils').now;
|
|
7
8
|
|
|
8
9
|
const WRITABLE_SERVER_TYPES = new Set([
|
|
9
10
|
ServerType.RSPrimary,
|
|
@@ -70,7 +71,7 @@ class ServerDescription {
|
|
|
70
71
|
this.address = address;
|
|
71
72
|
this.error = options.error;
|
|
72
73
|
this.roundTripTime = options.roundTripTime || -1;
|
|
73
|
-
this.lastUpdateTime =
|
|
74
|
+
this.lastUpdateTime = now();
|
|
74
75
|
this.lastWriteDate = ismaster.lastWrite ? ismaster.lastWrite.lastWriteDate : null;
|
|
75
76
|
this.opTime = ismaster.lastWrite ? ismaster.lastWrite.opTime : null;
|
|
76
77
|
this.type = parseServerType(ismaster);
|
|
@@ -60,7 +60,13 @@ function maxStalenessReducer(readPreference, topologyDescription, servers) {
|
|
|
60
60
|
if (staleness <= readPreference.maxStalenessSeconds) result.push(server);
|
|
61
61
|
return result;
|
|
62
62
|
}, []);
|
|
63
|
-
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (topologyDescription.type === TopologyType.ReplicaSetNoPrimary) {
|
|
66
|
+
if (servers.length === 0) {
|
|
67
|
+
return servers;
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
const sMax = servers.reduce((max, s) => (s.lastWriteDate > max.lastWriteDate ? s : max));
|
|
65
71
|
return servers.reduce((result, server) => {
|
|
66
72
|
const stalenessMS =
|
package/lib/core/sessions.js
CHANGED
|
@@ -17,7 +17,8 @@ const isTransactionCommand = require('./transactions').isTransactionCommand;
|
|
|
17
17
|
const resolveClusterTime = require('./topologies/shared').resolveClusterTime;
|
|
18
18
|
const isSharded = require('./wireprotocol/shared').isSharded;
|
|
19
19
|
const maxWireVersion = require('./utils').maxWireVersion;
|
|
20
|
-
|
|
20
|
+
const now = require('./../utils').now;
|
|
21
|
+
const calculateDurationInMs = require('./../utils').calculateDurationInMs;
|
|
21
22
|
const minWireVersionForShardedTransactions = 8;
|
|
22
23
|
|
|
23
24
|
function assertAlive(session, callback) {
|
|
@@ -285,7 +286,7 @@ class ClientSession extends EventEmitter {
|
|
|
285
286
|
* @param {TransactionOptions} [options] Optional settings for the transaction
|
|
286
287
|
*/
|
|
287
288
|
withTransaction(fn, options) {
|
|
288
|
-
const startTime =
|
|
289
|
+
const startTime = now();
|
|
289
290
|
return attemptTransaction(this, startTime, fn, options);
|
|
290
291
|
}
|
|
291
292
|
}
|
|
@@ -301,7 +302,7 @@ const NON_DETERMINISTIC_WRITE_CONCERN_ERRORS = new Set([
|
|
|
301
302
|
]);
|
|
302
303
|
|
|
303
304
|
function hasNotTimedOut(startTime, max) {
|
|
304
|
-
return
|
|
305
|
+
return calculateDurationInMs(startTime) < max;
|
|
305
306
|
}
|
|
306
307
|
|
|
307
308
|
function isUnknownTransactionCommitResult(err) {
|
|
@@ -558,7 +559,7 @@ function supportsRecoveryToken(session) {
|
|
|
558
559
|
class ServerSession {
|
|
559
560
|
constructor() {
|
|
560
561
|
this.id = { id: new Binary(uuidV4(), Binary.SUBTYPE_UUID) };
|
|
561
|
-
this.lastUse =
|
|
562
|
+
this.lastUse = now();
|
|
562
563
|
this.txnNumber = 0;
|
|
563
564
|
this.isDirty = false;
|
|
564
565
|
}
|
|
@@ -573,7 +574,7 @@ class ServerSession {
|
|
|
573
574
|
// Take the difference of the lastUse timestamp and now, which will result in a value in
|
|
574
575
|
// milliseconds, and then convert milliseconds to minutes to compare to `sessionTimeoutMinutes`
|
|
575
576
|
const idleTimeMinutes = Math.round(
|
|
576
|
-
(((
|
|
577
|
+
((calculateDurationInMs(this.lastUse) % 86400000) % 3600000) / 60000
|
|
577
578
|
);
|
|
578
579
|
|
|
579
580
|
return idleTimeMinutes > sessionTimeoutMinutes - 1;
|
|
@@ -708,7 +709,7 @@ function applySession(session, command, options) {
|
|
|
708
709
|
}
|
|
709
710
|
|
|
710
711
|
const serverSession = session.serverSession;
|
|
711
|
-
serverSession.lastUse =
|
|
712
|
+
serverSession.lastUse = now();
|
|
712
713
|
command.lsid = serverSession.id;
|
|
713
714
|
|
|
714
715
|
// first apply non-transaction-specific sessions data
|
|
@@ -17,9 +17,10 @@ const isRetryableWritesSupported = require('./shared').isRetryableWritesSupporte
|
|
|
17
17
|
const relayEvents = require('../utils').relayEvents;
|
|
18
18
|
const isRetryableError = require('../error').isRetryableError;
|
|
19
19
|
const BSON = retrieveBSON();
|
|
20
|
-
const calculateDurationInMs = require('../utils').calculateDurationInMs;
|
|
21
20
|
const getMMAPError = require('./shared').getMMAPError;
|
|
22
21
|
const makeClientMetadata = require('../utils').makeClientMetadata;
|
|
22
|
+
const now = require('../../utils').now;
|
|
23
|
+
const calculateDurationInMs = require('../../utils').calculateDurationInMs;
|
|
23
24
|
|
|
24
25
|
//
|
|
25
26
|
// States
|
|
@@ -426,8 +427,7 @@ var pingServer = function(self, server, cb) {
|
|
|
426
427
|
var latencyMS = new Date().getTime() - start;
|
|
427
428
|
|
|
428
429
|
// Set the last updatedTime
|
|
429
|
-
|
|
430
|
-
server.lastUpdateTime = (hrtime[0] * 1e9 + hrtime[1]) / 1e6;
|
|
430
|
+
server.lastUpdateTime = now();
|
|
431
431
|
|
|
432
432
|
// We had an error, remove it from the state
|
|
433
433
|
if (err) {
|
|
@@ -1127,7 +1127,7 @@ ReplSet.prototype.selectServer = function(selector, options, callback) {
|
|
|
1127
1127
|
}
|
|
1128
1128
|
|
|
1129
1129
|
let lastError;
|
|
1130
|
-
const start =
|
|
1130
|
+
const start = now();
|
|
1131
1131
|
const _selectServer = () => {
|
|
1132
1132
|
if (calculateDurationInMs(start) >= SERVER_SELECTION_TIMEOUT_MS) {
|
|
1133
1133
|
if (lastError != null) {
|
package/lib/core/utils.js
CHANGED
|
@@ -13,17 +13,6 @@ const uuidV4 = () => {
|
|
|
13
13
|
return result;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* Returns the duration calculated from two high resolution timers in milliseconds
|
|
18
|
-
*
|
|
19
|
-
* @param {Object} started A high resolution timestamp created from `process.hrtime()`
|
|
20
|
-
* @returns {Number} The duration in milliseconds
|
|
21
|
-
*/
|
|
22
|
-
const calculateDurationInMs = started => {
|
|
23
|
-
const hrtime = process.hrtime(started);
|
|
24
|
-
return (hrtime[0] * 1e9 + hrtime[1]) / 1e6;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
16
|
/**
|
|
28
17
|
* Relays events for a given listener and emitter
|
|
29
18
|
*
|
|
@@ -261,7 +250,6 @@ const noop = () => {};
|
|
|
261
250
|
|
|
262
251
|
module.exports = {
|
|
263
252
|
uuidV4,
|
|
264
|
-
calculateDurationInMs,
|
|
265
253
|
relayEvents,
|
|
266
254
|
collationNotSupported,
|
|
267
255
|
retrieveEJSON,
|
|
@@ -62,8 +62,13 @@ function getMore(server, ns, cursorState, batchSize, options, callback) {
|
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
const cursorId =
|
|
66
|
+
cursorState.cursorId instanceof Long
|
|
67
|
+
? cursorState.cursorId
|
|
68
|
+
: Long.fromNumber(cursorState.cursorId);
|
|
69
|
+
|
|
65
70
|
const getMoreCmd = {
|
|
66
|
-
getMore:
|
|
71
|
+
getMore: cursorId,
|
|
67
72
|
collection: collectionNamespace(ns),
|
|
68
73
|
batchSize: Math.abs(batchSize)
|
|
69
74
|
};
|
package/lib/mongo_client.js
CHANGED
|
@@ -146,6 +146,10 @@ const validOptions = require('./operations/connect').validOptions;
|
|
|
146
146
|
* @param {Number} [options.localThresholdMS=15] **Only applies to the unified topology** The size of the latency window for selecting among multiple suitable servers
|
|
147
147
|
* @param {Number} [options.serverSelectionTimeoutMS=30000] **Only applies to the unified topology** How long to block for server selection before throwing an error
|
|
148
148
|
* @param {Number} [options.heartbeatFrequencyMS=10000] **Only applies to the unified topology** The frequency with which topology updates are scheduled
|
|
149
|
+
* @param {number} [options.maxPoolSize=10] **Only applies to the unified topology** The maximum number of connections that may be associated with a pool at a given time. This includes in use and available connections.
|
|
150
|
+
* @param {number} [options.minPoolSize=0] **Only applies to the unified topology** The minimum number of connections that MUST exist at any moment in a single connection pool.
|
|
151
|
+
* @param {number} [options.maxIdleTimeMS] **Only applies to the unified topology** The maximum amount of time a connection should remain idle in the connection pool before being marked idle. The default is infinity.
|
|
152
|
+
* @param {number} [options.waitQueueTimeoutMS=0] **Only applies to the unified topology** The maximum amount of time operation execution should wait for a connection to become available. The default is 0 which means there is no limit.
|
|
149
153
|
* @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
|
|
150
154
|
* @param {DriverInfoOptions} [options.driverInfo] Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver
|
|
151
155
|
* @param {MongoClient~connectCallback} [callback] The command result callback
|
|
@@ -415,6 +419,10 @@ MongoClient.prototype.isConnected = function(options) {
|
|
|
415
419
|
* @param {Number} [options.localThresholdMS=15] **Only applies to the unified topology** The size of the latency window for selecting among multiple suitable servers
|
|
416
420
|
* @param {Number} [options.serverSelectionTimeoutMS=30000] **Only applies to the unified topology** How long to block for server selection before throwing an error
|
|
417
421
|
* @param {Number} [options.heartbeatFrequencyMS=10000] **Only applies to the unified topology** The frequency with which topology updates are scheduled
|
|
422
|
+
* @param {number} [options.maxPoolSize=10] **Only applies to the unified topology** The maximum number of connections that may be associated with a pool at a given time. This includes in use and available connections.
|
|
423
|
+
* @param {number} [options.minPoolSize=0] **Only applies to the unified topology** The minimum number of connections that MUST exist at any moment in a single connection pool.
|
|
424
|
+
* @param {number} [options.maxIdleTimeMS] **Only applies to the unified topology** The maximum amount of time a connection should remain idle in the connection pool before being marked idle. The default is infinity.
|
|
425
|
+
* @param {number} [options.waitQueueTimeoutMS=0] **Only applies to the unified topology** The maximum amount of time operation execution should wait for a connection to become available. The default is 0 which means there is no limit.
|
|
418
426
|
* @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
|
|
419
427
|
* @param {DriverInfoOptions} [options.driverInfo] Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver
|
|
420
428
|
* @param {MongoClient~connectCallback} [callback] The command result callback
|
|
@@ -15,8 +15,18 @@ class NativeTopology extends Topology {
|
|
|
15
15
|
cursorFactory: Cursor,
|
|
16
16
|
reconnect: false,
|
|
17
17
|
emitError: typeof options.emitError === 'boolean' ? options.emitError : true,
|
|
18
|
-
maxPoolSize:
|
|
19
|
-
|
|
18
|
+
maxPoolSize:
|
|
19
|
+
typeof options.maxPoolSize === 'number'
|
|
20
|
+
? options.maxPoolSize
|
|
21
|
+
: typeof options.poolSize === 'number'
|
|
22
|
+
? options.poolSize
|
|
23
|
+
: 10,
|
|
24
|
+
minPoolSize:
|
|
25
|
+
typeof options.minPoolSize === 'number'
|
|
26
|
+
? options.minPoolSize
|
|
27
|
+
: typeof options.minSize === 'number'
|
|
28
|
+
? options.minSize
|
|
29
|
+
: 0,
|
|
20
30
|
monitorCommands:
|
|
21
31
|
typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false
|
|
22
32
|
}
|
package/lib/utils.js
CHANGED
|
@@ -734,6 +734,98 @@ function maybePromise(parent, callback, fn) {
|
|
|
734
734
|
return result;
|
|
735
735
|
}
|
|
736
736
|
|
|
737
|
+
function now() {
|
|
738
|
+
const hrtime = process.hrtime();
|
|
739
|
+
return Math.floor(hrtime[0] * 1000 + hrtime[1] / 1000000);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function calculateDurationInMs(started) {
|
|
743
|
+
if (typeof started !== 'number') {
|
|
744
|
+
throw TypeError('numeric value required to calculate duration');
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const elapsed = now() - started;
|
|
748
|
+
return elapsed < 0 ? 0 : elapsed;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Creates an interval timer which is able to be woken up sooner than
|
|
753
|
+
* the interval. The timer will also debounce multiple calls to wake
|
|
754
|
+
* ensuring that the function is only ever called once within a minimum
|
|
755
|
+
* interval window.
|
|
756
|
+
*
|
|
757
|
+
* @param {function} fn An async function to run on an interval, must accept a `callback` as its only parameter
|
|
758
|
+
* @param {object} [options] Optional settings
|
|
759
|
+
* @param {number} [options.interval] The interval at which to run the provided function
|
|
760
|
+
* @param {number} [options.minInterval] The minimum time which must pass between invocations of the provided function
|
|
761
|
+
* @param {boolean} [options.immediate] Execute the function immediately when the interval is started
|
|
762
|
+
*/
|
|
763
|
+
function makeInterruptableAsyncInterval(fn, options) {
|
|
764
|
+
let timerId;
|
|
765
|
+
let lastCallTime;
|
|
766
|
+
let lastWakeTime;
|
|
767
|
+
let stopped = false;
|
|
768
|
+
|
|
769
|
+
options = options || {};
|
|
770
|
+
const interval = options.interval || 1000;
|
|
771
|
+
const minInterval = options.minInterval || 500;
|
|
772
|
+
const immediate = typeof options.immediate === 'boolean' ? options.immediate : false;
|
|
773
|
+
|
|
774
|
+
function wake() {
|
|
775
|
+
const currentTime = now();
|
|
776
|
+
const timeSinceLastWake = currentTime - lastWakeTime;
|
|
777
|
+
const timeSinceLastCall = currentTime - lastCallTime;
|
|
778
|
+
const timeUntilNextCall = Math.max(interval - timeSinceLastCall, 0);
|
|
779
|
+
lastWakeTime = currentTime;
|
|
780
|
+
|
|
781
|
+
// debounce multiple calls to wake within the `minInterval`
|
|
782
|
+
if (timeSinceLastWake < minInterval) {
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// reschedule a call as soon as possible, ensuring the call never happens
|
|
787
|
+
// faster than the `minInterval`
|
|
788
|
+
if (timeUntilNextCall > minInterval) {
|
|
789
|
+
reschedule(minInterval);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function stop() {
|
|
794
|
+
stopped = true;
|
|
795
|
+
if (timerId) {
|
|
796
|
+
clearTimeout(timerId);
|
|
797
|
+
timerId = null;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
lastCallTime = 0;
|
|
801
|
+
lastWakeTime = 0;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function reschedule(ms) {
|
|
805
|
+
if (stopped) return;
|
|
806
|
+
clearTimeout(timerId);
|
|
807
|
+
timerId = setTimeout(executeAndReschedule, ms || interval);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function executeAndReschedule() {
|
|
811
|
+
lastWakeTime = 0;
|
|
812
|
+
lastCallTime = now();
|
|
813
|
+
fn(err => {
|
|
814
|
+
if (err) throw err;
|
|
815
|
+
reschedule(interval);
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (immediate) {
|
|
820
|
+
executeAndReschedule();
|
|
821
|
+
} else {
|
|
822
|
+
lastCallTime = now();
|
|
823
|
+
reschedule();
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return { wake, stop };
|
|
827
|
+
}
|
|
828
|
+
|
|
737
829
|
module.exports = {
|
|
738
830
|
filterOptions,
|
|
739
831
|
mergeOptions,
|
|
@@ -764,5 +856,8 @@ module.exports = {
|
|
|
764
856
|
resolveReadPreference,
|
|
765
857
|
emitDeprecationWarning,
|
|
766
858
|
makeCounter,
|
|
767
|
-
maybePromise
|
|
859
|
+
maybePromise,
|
|
860
|
+
now,
|
|
861
|
+
calculateDurationInMs,
|
|
862
|
+
makeInterruptableAsyncInterval
|
|
768
863
|
};
|