mongodb 3.6.11 → 3.7.2
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/index.js +4 -0
- package/lib/cmap/connection.js +34 -7
- package/lib/cmap/connection_pool.js +4 -2
- package/lib/collection.js +1 -1
- package/lib/core/auth/mongo_credentials.js +4 -1
- package/lib/core/auth/mongodb_aws.js +16 -15
- package/lib/core/connection/connect.js +22 -5
- package/lib/core/connection/connection.js +2 -0
- package/lib/core/connection/pool.js +1 -0
- package/lib/core/connection/utils.js +35 -2
- package/lib/core/error.js +46 -46
- package/lib/core/index.js +9 -0
- package/lib/core/sdam/monitor.js +16 -2
- package/lib/core/sdam/server.js +2 -0
- package/lib/core/sdam/server_description.js +4 -0
- package/lib/core/sdam/topology.js +10 -2
- package/lib/core/sessions.js +11 -8
- package/lib/core/topologies/replset_state.js +5 -3
- package/lib/core/topologies/shared.js +4 -1
- package/lib/core/transactions.js +5 -1
- package/lib/core/uri_parser.js +14 -0
- package/lib/core/wireprotocol/command.js +24 -0
- package/lib/core/wireprotocol/kill_cursors.js +7 -2
- package/lib/core/wireprotocol/query.js +9 -5
- package/lib/cursor.js +4 -0
- package/lib/db.js +1 -0
- package/lib/error.js +21 -20
- package/lib/error_codes.js +36 -0
- package/lib/explain.js +5 -12
- package/lib/gridfs-stream/index.js +39 -24
- package/lib/gridfs-stream/upload.js +53 -46
- package/lib/mongo_client.js +30 -5
- package/lib/operations/connect.js +1 -0
- package/lib/operations/db_ops.js +10 -7
- package/lib/operations/estimated_document_count.js +47 -18
- package/lib/topologies/native_topology.js +4 -0
- package/lib/url_parser.js +8 -0
- package/lib/utils.js +10 -1
- package/package.json +3 -3
- package/HISTORY.md +0 -2908
package/index.js
CHANGED
|
@@ -17,6 +17,9 @@ connect.MongoWriteConcernError = core.MongoWriteConcernError;
|
|
|
17
17
|
connect.MongoBulkWriteError = require('./lib/bulk/common').BulkWriteError;
|
|
18
18
|
connect.BulkWriteError = connect.MongoBulkWriteError;
|
|
19
19
|
|
|
20
|
+
// Expose server versions
|
|
21
|
+
connect.ServerApiVersion = core.ServerApiVersion;
|
|
22
|
+
|
|
20
23
|
// Actual driver classes exported
|
|
21
24
|
connect.Admin = require('./lib/admin');
|
|
22
25
|
connect.MongoClient = require('./lib/mongo_client');
|
|
@@ -47,6 +50,7 @@ connect.Int32 = core.BSON.Int32;
|
|
|
47
50
|
connect.Long = core.BSON.Long;
|
|
48
51
|
connect.MinKey = core.BSON.MinKey;
|
|
49
52
|
connect.MaxKey = core.BSON.MaxKey;
|
|
53
|
+
/** @deprecated Please use `ObjectId` */
|
|
50
54
|
connect.ObjectID = core.BSON.ObjectID;
|
|
51
55
|
connect.ObjectId = core.BSON.ObjectID;
|
|
52
56
|
connect.Symbol = core.BSON.Symbol;
|
package/lib/cmap/connection.js
CHANGED
|
@@ -37,6 +37,8 @@ class Connection extends EventEmitter {
|
|
|
37
37
|
this.port = options.port || 27017;
|
|
38
38
|
this.monitorCommands =
|
|
39
39
|
typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false;
|
|
40
|
+
this.serverApi = options.serverApi;
|
|
41
|
+
|
|
40
42
|
this.closed = false;
|
|
41
43
|
this.destroyed = false;
|
|
42
44
|
|
|
@@ -170,33 +172,58 @@ class Connection extends EventEmitter {
|
|
|
170
172
|
});
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
applyApiVersion(options) {
|
|
176
|
+
if (this.serverApi) {
|
|
177
|
+
options.serverApi = this.serverApi;
|
|
178
|
+
}
|
|
179
|
+
return options;
|
|
180
|
+
}
|
|
181
|
+
|
|
173
182
|
// Wire protocol methods
|
|
174
183
|
command(ns, cmd, options, callback) {
|
|
175
|
-
|
|
184
|
+
if (typeof options === 'function') {
|
|
185
|
+
callback = options;
|
|
186
|
+
options = {};
|
|
187
|
+
}
|
|
188
|
+
wp.command(makeServerTrampoline(this), ns, cmd, this.applyApiVersion(options), callback);
|
|
176
189
|
}
|
|
177
190
|
|
|
178
191
|
query(ns, cmd, cursorState, options, callback) {
|
|
179
|
-
wp.query(
|
|
192
|
+
wp.query(
|
|
193
|
+
makeServerTrampoline(this),
|
|
194
|
+
ns,
|
|
195
|
+
cmd,
|
|
196
|
+
cursorState,
|
|
197
|
+
this.applyApiVersion(options),
|
|
198
|
+
callback
|
|
199
|
+
);
|
|
180
200
|
}
|
|
181
201
|
|
|
182
202
|
getMore(ns, cursorState, batchSize, options, callback) {
|
|
183
|
-
wp.getMore(
|
|
203
|
+
wp.getMore(
|
|
204
|
+
makeServerTrampoline(this),
|
|
205
|
+
ns,
|
|
206
|
+
cursorState,
|
|
207
|
+
batchSize,
|
|
208
|
+
this.applyApiVersion(options),
|
|
209
|
+
callback
|
|
210
|
+
);
|
|
184
211
|
}
|
|
185
212
|
|
|
186
213
|
killCursors(ns, cursorState, callback) {
|
|
187
|
-
wp.killCursors(makeServerTrampoline(this), ns, cursorState, callback);
|
|
214
|
+
wp.killCursors(makeServerTrampoline(this), ns, cursorState, this.applyApiVersion({}), callback);
|
|
188
215
|
}
|
|
189
216
|
|
|
190
217
|
insert(ns, ops, options, callback) {
|
|
191
|
-
wp.insert(makeServerTrampoline(this), ns, ops, options, callback);
|
|
218
|
+
wp.insert(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
update(ns, ops, options, callback) {
|
|
195
|
-
wp.update(makeServerTrampoline(this), ns, ops, options, callback);
|
|
222
|
+
wp.update(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
|
|
196
223
|
}
|
|
197
224
|
|
|
198
225
|
remove(ns, ops, options, callback) {
|
|
199
|
-
wp.remove(makeServerTrampoline(this), ns, ops, options, callback);
|
|
226
|
+
wp.remove(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
|
|
200
227
|
}
|
|
201
228
|
}
|
|
202
229
|
|
|
@@ -41,6 +41,7 @@ const VALID_POOL_OPTIONS = new Set([
|
|
|
41
41
|
'ssl',
|
|
42
42
|
'bson',
|
|
43
43
|
'connectionType',
|
|
44
|
+
'serverApi',
|
|
44
45
|
'monitorCommands',
|
|
45
46
|
'socketTimeout',
|
|
46
47
|
'credentials',
|
|
@@ -157,12 +158,13 @@ class ConnectionPool extends EventEmitter {
|
|
|
157
158
|
waitQueueTimeoutMS:
|
|
158
159
|
typeof options.waitQueueTimeoutMS === 'number' ? options.waitQueueTimeoutMS : 0,
|
|
159
160
|
autoEncrypter: options.autoEncrypter,
|
|
160
|
-
metadata: options.metadata
|
|
161
|
+
metadata: options.metadata,
|
|
162
|
+
useUnifiedTopology: options.useUnifiedTopology
|
|
161
163
|
});
|
|
162
164
|
|
|
163
165
|
if (options.minSize > options.maxSize) {
|
|
164
166
|
throw new TypeError(
|
|
165
|
-
'Connection pool minimum size must not be greater than
|
|
167
|
+
'Connection pool minimum size must not be greater than maximum pool size'
|
|
166
168
|
);
|
|
167
169
|
}
|
|
168
170
|
|
package/lib/collection.js
CHANGED
|
@@ -1532,7 +1532,7 @@ Collection.prototype.count = deprecate(function(query, options, callback) {
|
|
|
1532
1532
|
|
|
1533
1533
|
return executeOperation(
|
|
1534
1534
|
this.s.topology,
|
|
1535
|
-
new EstimatedDocumentCountOperation(this, query, options),
|
|
1535
|
+
new EstimatedDocumentCountOperation(this, Object.assign({ query }, options)),
|
|
1536
1536
|
callback
|
|
1537
1537
|
);
|
|
1538
1538
|
}, 'collection.count is deprecated, and will be removed in a future version.' +
|
|
@@ -58,7 +58,10 @@ class MongoCredentials {
|
|
|
58
58
|
this.password = process.env.AWS_SECRET_ACCESS_KEY;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
if (
|
|
61
|
+
if (
|
|
62
|
+
this.mechanismProperties.AWS_SESSION_TOKEN == null &&
|
|
63
|
+
process.env.AWS_SESSION_TOKEN != null
|
|
64
|
+
) {
|
|
62
65
|
this.mechanismProperties.AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN;
|
|
63
66
|
}
|
|
64
67
|
}
|
|
@@ -51,12 +51,21 @@ class MongoDBAWS extends AuthProvider {
|
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const username = credentials.username;
|
|
55
|
-
const password = credentials.password;
|
|
56
54
|
const db = credentials.source;
|
|
57
|
-
const token = credentials.mechanismProperties.AWS_SESSION_TOKEN;
|
|
58
55
|
const bson = this.bson;
|
|
59
56
|
|
|
57
|
+
const accessKeyId = credentials.username;
|
|
58
|
+
const secretAccessKey = credentials.password;
|
|
59
|
+
const sessionToken = credentials.mechanismProperties.AWS_SESSION_TOKEN;
|
|
60
|
+
|
|
61
|
+
// If all three defined, include sessionToken, else include username and pass, else no credentials
|
|
62
|
+
const awsCredentials =
|
|
63
|
+
accessKeyId && secretAccessKey && sessionToken
|
|
64
|
+
? { accessKeyId, secretAccessKey, sessionToken }
|
|
65
|
+
: accessKeyId && secretAccessKey
|
|
66
|
+
? { accessKeyId, secretAccessKey }
|
|
67
|
+
: undefined;
|
|
68
|
+
|
|
60
69
|
crypto.randomBytes(32, (err, nonce) => {
|
|
61
70
|
if (err) {
|
|
62
71
|
callback(err);
|
|
@@ -109,18 +118,14 @@ class MongoDBAWS extends AuthProvider {
|
|
|
109
118
|
path: '/',
|
|
110
119
|
body
|
|
111
120
|
},
|
|
112
|
-
|
|
113
|
-
accessKeyId: username,
|
|
114
|
-
secretAccessKey: password,
|
|
115
|
-
token
|
|
116
|
-
}
|
|
121
|
+
awsCredentials
|
|
117
122
|
);
|
|
118
123
|
|
|
119
124
|
const authorization = options.headers.Authorization;
|
|
120
125
|
const date = options.headers['X-Amz-Date'];
|
|
121
126
|
const payload = { a: authorization, d: date };
|
|
122
|
-
if (
|
|
123
|
-
payload.t =
|
|
127
|
+
if (sessionToken) {
|
|
128
|
+
payload.t = sessionToken;
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
const saslContinue = {
|
|
@@ -164,6 +169,7 @@ function makeTempCredentials(credentials, callback) {
|
|
|
164
169
|
if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
|
|
165
170
|
request(
|
|
166
171
|
`${AWS_RELATIVE_URI}${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}`,
|
|
172
|
+
undefined,
|
|
167
173
|
(err, res) => {
|
|
168
174
|
if (err) return callback(err);
|
|
169
175
|
done(res);
|
|
@@ -215,11 +221,6 @@ function deriveRegion(host) {
|
|
|
215
221
|
}
|
|
216
222
|
|
|
217
223
|
function request(uri, options, callback) {
|
|
218
|
-
if (typeof options === 'function') {
|
|
219
|
-
callback = options;
|
|
220
|
-
options = {};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
224
|
options = Object.assign(
|
|
224
225
|
{
|
|
225
226
|
method: 'GET',
|
|
@@ -95,6 +95,8 @@ function performInitialHandshake(conn, options, _callback) {
|
|
|
95
95
|
handshakeOptions.socketTimeout = options.connectTimeoutMS || options.connectionTimeout;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
handshakeDoc.helloOk = !!options.useUnifiedTopology;
|
|
99
|
+
|
|
98
100
|
const start = new Date().getTime();
|
|
99
101
|
conn.command('admin.$cmd', handshakeDoc, handshakeOptions, (err, result) => {
|
|
100
102
|
if (err) {
|
|
@@ -108,6 +110,15 @@ function performInitialHandshake(conn, options, _callback) {
|
|
|
108
110
|
return;
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
if ('isWritablePrimary' in response) {
|
|
114
|
+
// Provide pre-hello-style response document.
|
|
115
|
+
response.ismaster = response.isWritablePrimary;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (options.useUnifiedTopology && response.helloOk) {
|
|
119
|
+
conn.helloOk = true;
|
|
120
|
+
}
|
|
121
|
+
|
|
111
122
|
const supportedServerErr = checkSupportedServer(response, options);
|
|
112
123
|
if (supportedServerErr) {
|
|
113
124
|
callback(supportedServerErr);
|
|
@@ -158,11 +169,12 @@ function performInitialHandshake(conn, options, _callback) {
|
|
|
158
169
|
|
|
159
170
|
function prepareHandshakeDocument(authContext, callback) {
|
|
160
171
|
const options = authContext.options;
|
|
172
|
+
const serverApi = authContext.connection.serverApi;
|
|
161
173
|
const compressors =
|
|
162
174
|
options.compression && options.compression.compressors ? options.compression.compressors : [];
|
|
163
175
|
|
|
164
176
|
const handshakeDoc = {
|
|
165
|
-
ismaster: true,
|
|
177
|
+
[serverApi ? 'hello' : 'ismaster']: true,
|
|
166
178
|
client: options.metadata || makeClientMetadata(options),
|
|
167
179
|
compression: compressors
|
|
168
180
|
};
|
|
@@ -266,12 +278,17 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
|
|
266
278
|
: typeof options.connectTimeoutMS === 'number'
|
|
267
279
|
? options.connectTimeoutMS
|
|
268
280
|
: 30000;
|
|
269
|
-
const
|
|
281
|
+
const socketTimeoutMS =
|
|
282
|
+
typeof options.socketTimeoutMS === 'number'
|
|
283
|
+
? options.socketTimeoutMS
|
|
284
|
+
: typeof options.socketTimeout === 'number'
|
|
285
|
+
? options.socketTimeout
|
|
286
|
+
: 0;
|
|
270
287
|
const rejectUnauthorized =
|
|
271
288
|
typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;
|
|
272
289
|
|
|
273
|
-
if (keepAliveInitialDelay >
|
|
274
|
-
keepAliveInitialDelay = Math.round(
|
|
290
|
+
if (keepAliveInitialDelay > socketTimeoutMS) {
|
|
291
|
+
keepAliveInitialDelay = Math.round(socketTimeoutMS / 2);
|
|
275
292
|
}
|
|
276
293
|
|
|
277
294
|
let socket;
|
|
@@ -324,7 +341,7 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
|
|
324
341
|
return callback(socket.authorizationError);
|
|
325
342
|
}
|
|
326
343
|
|
|
327
|
-
socket.setTimeout(
|
|
344
|
+
socket.setTimeout(socketTimeoutMS);
|
|
328
345
|
callback(null, socket);
|
|
329
346
|
}
|
|
330
347
|
|
|
@@ -91,6 +91,7 @@ class Connection extends EventEmitter {
|
|
|
91
91
|
this.bson = options.bson;
|
|
92
92
|
this.tag = options.tag;
|
|
93
93
|
this.maxBsonMessageSize = options.maxBsonMessageSize || DEFAULT_MAX_BSON_MESSAGE_SIZE;
|
|
94
|
+
this.helloOk = undefined;
|
|
94
95
|
|
|
95
96
|
this.port = options.port || 27017;
|
|
96
97
|
this.host = options.host || 'localhost';
|
|
@@ -190,6 +191,7 @@ class Connection extends EventEmitter {
|
|
|
190
191
|
* Unref this connection
|
|
191
192
|
* @method
|
|
192
193
|
* @return {boolean}
|
|
194
|
+
* @deprecated This function is deprecated and will be removed in the next major version.
|
|
193
195
|
*/
|
|
194
196
|
unref() {
|
|
195
197
|
if (this.socket == null) {
|
|
@@ -604,6 +604,7 @@ Pool.prototype.logout = function(dbName, callback) {
|
|
|
604
604
|
/**
|
|
605
605
|
* Unref the pool
|
|
606
606
|
* @method
|
|
607
|
+
* @deprecated This function is deprecated and will be removed in the next major version.
|
|
607
608
|
*/
|
|
608
609
|
Pool.prototype.unref = function() {
|
|
609
610
|
// Get all the known connections
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const parsePackageVersion = require('../../utils').parsePackageVersion;
|
|
4
|
+
const MongoError = require('../error').MongoError;
|
|
5
|
+
|
|
3
6
|
const require_optional = require('optional-require')(require);
|
|
4
7
|
|
|
5
8
|
function debugOptions(debugFields, options) {
|
|
@@ -16,7 +19,15 @@ function retrieveBSON() {
|
|
|
16
19
|
BSON.native = false;
|
|
17
20
|
|
|
18
21
|
const optionalBSON = require_optional('bson-ext');
|
|
22
|
+
const bsonExtVersion = parsePackageVersion(
|
|
23
|
+
require_optional('bson-ext/package.json') || { version: '0.0.0' }
|
|
24
|
+
);
|
|
19
25
|
if (optionalBSON) {
|
|
26
|
+
if (bsonExtVersion.major >= 4) {
|
|
27
|
+
throw new MongoError(
|
|
28
|
+
'bson-ext version 4 and above does not work with the 3.x version of the mongodb driver'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
20
31
|
optionalBSON.native = true;
|
|
21
32
|
return optionalBSON;
|
|
22
33
|
}
|
|
@@ -31,21 +42,43 @@ function noSnappyWarning() {
|
|
|
31
42
|
);
|
|
32
43
|
}
|
|
33
44
|
|
|
45
|
+
const PKG_VERSION = Symbol('kPkgVersion');
|
|
46
|
+
|
|
34
47
|
// Facilitate loading Snappy optionally
|
|
35
48
|
function retrieveSnappy() {
|
|
36
|
-
|
|
49
|
+
const snappy = require_optional('snappy');
|
|
37
50
|
if (!snappy) {
|
|
38
|
-
|
|
51
|
+
return {
|
|
39
52
|
compress: noSnappyWarning,
|
|
40
53
|
uncompress: noSnappyWarning,
|
|
41
54
|
compressSync: noSnappyWarning,
|
|
42
55
|
uncompressSync: noSnappyWarning
|
|
43
56
|
};
|
|
44
57
|
}
|
|
58
|
+
|
|
59
|
+
const snappyPkg = require_optional('snappy/package.json') || { version: '0.0.0' };
|
|
60
|
+
const version = parsePackageVersion(snappyPkg);
|
|
61
|
+
snappy[PKG_VERSION] = version;
|
|
62
|
+
if (version.major >= 7) {
|
|
63
|
+
const compressOriginal = snappy.compress;
|
|
64
|
+
const uncompressOriginal = snappy.uncompress;
|
|
65
|
+
snappy.compress = (data, callback) => {
|
|
66
|
+
compressOriginal(data)
|
|
67
|
+
.then(res => callback(undefined, res))
|
|
68
|
+
.catch(error => callback(error));
|
|
69
|
+
};
|
|
70
|
+
snappy.uncompress = (data, callback) => {
|
|
71
|
+
uncompressOriginal(data)
|
|
72
|
+
.then(res => callback(undefined, res))
|
|
73
|
+
.catch(error => callback(error));
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
45
77
|
return snappy;
|
|
46
78
|
}
|
|
47
79
|
|
|
48
80
|
module.exports = {
|
|
81
|
+
PKG_VERSION,
|
|
49
82
|
debugOptions,
|
|
50
83
|
retrieveBSON,
|
|
51
84
|
retrieveSnappy
|
package/lib/core/error.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const MONGODB_ERROR_CODES = require('../error_codes').MONGODB_ERROR_CODES;
|
|
4
|
+
|
|
3
5
|
const kErrorLabels = Symbol('errorLabels');
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -216,32 +218,32 @@ class MongoWriteConcernError extends MongoError {
|
|
|
216
218
|
|
|
217
219
|
// see: https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst#terms
|
|
218
220
|
const RETRYABLE_ERROR_CODES = new Set([
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
221
|
+
MONGODB_ERROR_CODES.HostUnreachable,
|
|
222
|
+
MONGODB_ERROR_CODES.HostNotFound,
|
|
223
|
+
MONGODB_ERROR_CODES.NetworkTimeout,
|
|
224
|
+
MONGODB_ERROR_CODES.ShutdownInProgress,
|
|
225
|
+
MONGODB_ERROR_CODES.PrimarySteppedDown,
|
|
226
|
+
MONGODB_ERROR_CODES.SocketException,
|
|
227
|
+
MONGODB_ERROR_CODES.NotMaster,
|
|
228
|
+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
|
|
229
|
+
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
|
|
230
|
+
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
|
|
231
|
+
MONGODB_ERROR_CODES.NotMasterOrSecondary
|
|
230
232
|
]);
|
|
231
233
|
|
|
232
234
|
const RETRYABLE_WRITE_ERROR_CODES = new Set([
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
235
|
+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
|
|
236
|
+
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
|
|
237
|
+
MONGODB_ERROR_CODES.NotMaster,
|
|
238
|
+
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
|
|
239
|
+
MONGODB_ERROR_CODES.NotMasterOrSecondary,
|
|
240
|
+
MONGODB_ERROR_CODES.PrimarySteppedDown,
|
|
241
|
+
MONGODB_ERROR_CODES.ShutdownInProgress,
|
|
242
|
+
MONGODB_ERROR_CODES.HostNotFound,
|
|
243
|
+
MONGODB_ERROR_CODES.HostUnreachable,
|
|
244
|
+
MONGODB_ERROR_CODES.NetworkTimeout,
|
|
245
|
+
MONGODB_ERROR_CODES.SocketException,
|
|
246
|
+
MONGODB_ERROR_CODES.ExceededTimeLimit
|
|
245
247
|
]);
|
|
246
248
|
|
|
247
249
|
function isRetryableWriteError(error) {
|
|
@@ -271,41 +273,44 @@ function isRetryableError(error) {
|
|
|
271
273
|
}
|
|
272
274
|
|
|
273
275
|
const SDAM_RECOVERING_CODES = new Set([
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
MONGODB_ERROR_CODES.ShutdownInProgress,
|
|
277
|
+
MONGODB_ERROR_CODES.PrimarySteppedDown,
|
|
278
|
+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
|
|
279
|
+
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
|
|
280
|
+
MONGODB_ERROR_CODES.NotMasterOrSecondary
|
|
279
281
|
]);
|
|
280
282
|
|
|
281
283
|
const SDAM_NOTMASTER_CODES = new Set([
|
|
282
|
-
|
|
283
|
-
|
|
284
|
+
MONGODB_ERROR_CODES.NotMaster,
|
|
285
|
+
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
|
|
286
|
+
MONGODB_ERROR_CODES.LegacyNotPrimary
|
|
284
287
|
]);
|
|
285
288
|
|
|
286
289
|
const SDAM_NODE_SHUTTING_DOWN_ERROR_CODES = new Set([
|
|
287
|
-
|
|
288
|
-
|
|
290
|
+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
|
|
291
|
+
MONGODB_ERROR_CODES.ShutdownInProgress
|
|
289
292
|
]);
|
|
290
293
|
|
|
291
294
|
function isRecoveringError(err) {
|
|
292
|
-
if (err.code
|
|
293
|
-
|
|
295
|
+
if (typeof err.code === 'number') {
|
|
296
|
+
// If any error code exists, we ignore the error.message
|
|
297
|
+
return SDAM_RECOVERING_CODES.has(err.code);
|
|
294
298
|
}
|
|
295
299
|
|
|
296
|
-
return
|
|
300
|
+
return /not master or secondary/.test(err.message) || /node is recovering/.test(err.message);
|
|
297
301
|
}
|
|
298
302
|
|
|
299
303
|
function isNotMasterError(err) {
|
|
300
|
-
if (err.code
|
|
301
|
-
|
|
304
|
+
if (typeof err.code === 'number') {
|
|
305
|
+
// If any error code exists, we ignore the error.message
|
|
306
|
+
return SDAM_NOTMASTER_CODES.has(err.code);
|
|
302
307
|
}
|
|
303
308
|
|
|
304
309
|
if (isRecoveringError(err)) {
|
|
305
310
|
return false;
|
|
306
311
|
}
|
|
307
312
|
|
|
308
|
-
return
|
|
313
|
+
return /not master/.test(err.message);
|
|
309
314
|
}
|
|
310
315
|
|
|
311
316
|
function isNodeShuttingDownError(err) {
|
|
@@ -316,10 +321,9 @@ function isNodeShuttingDownError(err) {
|
|
|
316
321
|
* Determines whether SDAM can recover from a given error. If it cannot
|
|
317
322
|
* then the pool will be cleared, and server state will completely reset
|
|
318
323
|
* locally.
|
|
319
|
-
*
|
|
320
|
-
* @ignore
|
|
321
324
|
* @see https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-master-and-node-is-recovering
|
|
322
|
-
* @param {MongoError
|
|
325
|
+
* @param {MongoError} error
|
|
326
|
+
* @returns {boolean}
|
|
323
327
|
*/
|
|
324
328
|
function isSDAMUnrecoverableError(error) {
|
|
325
329
|
// NOTE: null check is here for a strictly pre-CMAP world, a timeout or
|
|
@@ -328,11 +332,7 @@ function isSDAMUnrecoverableError(error) {
|
|
|
328
332
|
return true;
|
|
329
333
|
}
|
|
330
334
|
|
|
331
|
-
|
|
332
|
-
return true;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return false;
|
|
335
|
+
return isRecoveringError(error) || isNotMasterError(error);
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
module.exports = {
|
package/lib/core/index.js
CHANGED
|
@@ -15,7 +15,16 @@ try {
|
|
|
15
15
|
}
|
|
16
16
|
} catch (err) {} // eslint-disable-line
|
|
17
17
|
|
|
18
|
+
/** An enumeration of valid server API versions */
|
|
19
|
+
const ServerApiVersion = Object.freeze({
|
|
20
|
+
v1: '1'
|
|
21
|
+
});
|
|
22
|
+
const ValidServerApiVersions = Object.keys(ServerApiVersion).map(key => ServerApiVersion[key]);
|
|
23
|
+
|
|
18
24
|
module.exports = {
|
|
25
|
+
// Versioned API
|
|
26
|
+
ServerApiVersion,
|
|
27
|
+
ValidServerApiVersions,
|
|
19
28
|
// Errors
|
|
20
29
|
MongoError: require('./error').MongoError,
|
|
21
30
|
MongoNetworkError: require('./error').MongoNetworkError,
|
package/lib/core/sdam/monitor.js
CHANGED
|
@@ -65,7 +65,8 @@ class Monitor extends EventEmitter {
|
|
|
65
65
|
heartbeatFrequencyMS:
|
|
66
66
|
typeof options.heartbeatFrequencyMS === 'number' ? options.heartbeatFrequencyMS : 10000,
|
|
67
67
|
minHeartbeatFrequencyMS:
|
|
68
|
-
typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500
|
|
68
|
+
typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500,
|
|
69
|
+
useUnifiedTopology: options.useUnifiedTopology
|
|
69
70
|
});
|
|
70
71
|
|
|
71
72
|
// TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
|
|
@@ -204,8 +205,16 @@ function checkServer(monitor, callback) {
|
|
|
204
205
|
const maxAwaitTimeMS = monitor.options.heartbeatFrequencyMS;
|
|
205
206
|
const topologyVersion = monitor[kServer].description.topologyVersion;
|
|
206
207
|
const isAwaitable = topologyVersion != null;
|
|
208
|
+
const serverApi = monitor[kConnection].serverApi;
|
|
209
|
+
const helloOk = monitor[kConnection].helloOk;
|
|
210
|
+
|
|
211
|
+
const cmd = {
|
|
212
|
+
[serverApi || helloOk ? 'hello' : 'ismaster']: true
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// written this way omit helloOk from the command if its false-y (do not want -> helloOk: null)
|
|
216
|
+
if (helloOk) cmd.helloOk = helloOk;
|
|
207
217
|
|
|
208
|
-
const cmd = { ismaster: true };
|
|
209
218
|
const options = { socketTimeout: connectTimeoutMS };
|
|
210
219
|
|
|
211
220
|
if (isAwaitable) {
|
|
@@ -229,6 +238,11 @@ function checkServer(monitor, callback) {
|
|
|
229
238
|
const isMaster = result.result;
|
|
230
239
|
const rttPinger = monitor[kRTTPinger];
|
|
231
240
|
|
|
241
|
+
if ('isWritablePrimary' in isMaster) {
|
|
242
|
+
// Provide pre-hello-style response document.
|
|
243
|
+
isMaster.ismaster = isMaster.isWritablePrimary;
|
|
244
|
+
}
|
|
245
|
+
|
|
232
246
|
const duration =
|
|
233
247
|
isAwaitable && rttPinger ? rttPinger.roundTripTime : calculateDurationInMs(start);
|
|
234
248
|
|
package/lib/core/sdam/server.js
CHANGED
|
@@ -114,6 +114,7 @@ class Server extends EventEmitter {
|
|
|
114
114
|
credentials: options.credentials,
|
|
115
115
|
topology
|
|
116
116
|
};
|
|
117
|
+
this.serverApi = options.serverApi;
|
|
117
118
|
|
|
118
119
|
// create the connection pool
|
|
119
120
|
// NOTE: this used to happen in `connect`, we supported overriding pool options there
|
|
@@ -251,6 +252,7 @@ class Server extends EventEmitter {
|
|
|
251
252
|
if (typeof options === 'function') {
|
|
252
253
|
(callback = options), (options = {}), (options = options || {});
|
|
253
254
|
}
|
|
255
|
+
options.serverApi = this.serverApi;
|
|
254
256
|
|
|
255
257
|
if (this.s.state === STATE_CLOSING || this.s.state === STATE_CLOSED) {
|
|
256
258
|
callback(new MongoError('server is closed'));
|
|
@@ -70,6 +70,10 @@ class ServerDescription {
|
|
|
70
70
|
ismaster
|
|
71
71
|
);
|
|
72
72
|
|
|
73
|
+
if (ismaster.isWritablePrimary != null) {
|
|
74
|
+
ismaster.ismaster = ismaster.isWritablePrimary;
|
|
75
|
+
}
|
|
76
|
+
|
|
73
77
|
this.address = address;
|
|
74
78
|
this.error = options.error;
|
|
75
79
|
this.roundTripTime = options.roundTripTime || -1;
|
|
@@ -200,6 +200,7 @@ class Topology extends EventEmitter {
|
|
|
200
200
|
// timer management
|
|
201
201
|
connectionTimers: new Set()
|
|
202
202
|
};
|
|
203
|
+
this.serverApi = options.serverApi;
|
|
203
204
|
|
|
204
205
|
if (options.srvHost) {
|
|
205
206
|
this.s.srvPoller =
|
|
@@ -494,7 +495,11 @@ class Topology extends EventEmitter {
|
|
|
494
495
|
this.command(
|
|
495
496
|
'admin.$cmd',
|
|
496
497
|
{ endSessions: sessions },
|
|
497
|
-
{
|
|
498
|
+
{
|
|
499
|
+
readPreference: ReadPreference.primaryPreferred,
|
|
500
|
+
noResponse: true,
|
|
501
|
+
serverApi: this.serverApi
|
|
502
|
+
},
|
|
498
503
|
() => {
|
|
499
504
|
// intentionally ignored, per spec
|
|
500
505
|
if (typeof callback === 'function') callback();
|
|
@@ -742,8 +747,11 @@ class Topology extends EventEmitter {
|
|
|
742
747
|
return this.s.state === STATE_CLOSED;
|
|
743
748
|
}
|
|
744
749
|
|
|
750
|
+
/**
|
|
751
|
+
* @deprecated This function is deprecated and will be removed in the next major version.
|
|
752
|
+
*/
|
|
745
753
|
unref() {
|
|
746
|
-
emitWarning('
|
|
754
|
+
emitWarning('`unref` is a noop and will be removed in the next major version');
|
|
747
755
|
}
|
|
748
756
|
|
|
749
757
|
// NOTE: There are many places in code where we explicitly check the last isMaster
|
package/lib/core/sessions.js
CHANGED
|
@@ -472,17 +472,20 @@ function endTransaction(session, commandName, callback) {
|
|
|
472
472
|
if (commandName === 'commitTransaction') {
|
|
473
473
|
session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
|
|
474
474
|
|
|
475
|
-
if (
|
|
476
|
-
|
|
477
|
-
|
|
475
|
+
if (e) {
|
|
476
|
+
if (
|
|
477
|
+
e instanceof MongoNetworkError ||
|
|
478
478
|
e instanceof MongoWriteConcernError ||
|
|
479
479
|
isRetryableError(e) ||
|
|
480
|
-
isMaxTimeMSExpiredError(e)
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
480
|
+
isMaxTimeMSExpiredError(e)
|
|
481
|
+
) {
|
|
482
|
+
if (isUnknownTransactionCommitResult(e)) {
|
|
483
|
+
e.addErrorLabel('UnknownTransactionCommitResult');
|
|
484
484
|
|
|
485
|
-
|
|
485
|
+
// per txns spec, must unpin session in this case
|
|
486
|
+
session.transaction.unpinServer();
|
|
487
|
+
}
|
|
488
|
+
} else if (e.hasErrorLabel('TransientTransactionError')) {
|
|
486
489
|
session.transaction.unpinServer();
|
|
487
490
|
}
|
|
488
491
|
}
|