mongodb 4.4.1 → 4.6.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/README.md +3 -2
- package/lib/admin.js +5 -6
- package/lib/admin.js.map +1 -1
- package/lib/bulk/common.js +34 -7
- package/lib/bulk/common.js.map +1 -1
- package/lib/bulk/unordered.js.map +1 -1
- package/lib/change_stream.js +251 -245
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongocr.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +3 -0
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/plain.js.map +1 -1
- package/lib/cmap/auth/scram.js +1 -0
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/auth/x509.js.map +1 -1
- package/lib/cmap/commands.js +12 -11
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +8 -1
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +111 -145
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/errors.js.map +1 -1
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/stream_description.js +3 -0
- package/lib/cmap/stream_description.js.map +1 -1
- package/lib/collection.js +60 -29
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +32 -11
- package/lib/connection_string.js.map +1 -1
- package/lib/constants.js +8 -1
- package/lib/constants.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +64 -51
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +2 -2
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +9 -4
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/db.js +20 -13
- package/lib/db.js.map +1 -1
- package/lib/encrypter.js +21 -10
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +121 -59
- package/lib/error.js.map +1 -1
- package/lib/gridfs/download.js +2 -0
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js +42 -51
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +1 -1
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +7 -3
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +21 -27
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/add_user.js +8 -1
- package/lib/operations/add_user.js.map +1 -1
- package/lib/operations/aggregate.js +5 -0
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/bulk_write.js.map +1 -1
- package/lib/operations/collections.js.map +1 -1
- package/lib/operations/command.js +0 -3
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/common_functions.js +8 -1
- package/lib/operations/common_functions.js.map +1 -1
- package/lib/operations/count.js.map +1 -1
- package/lib/operations/count_documents.js.map +1 -1
- package/lib/operations/create_collection.js +51 -17
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/delete.js +5 -3
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/drop.js +67 -7
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/eval.js.map +1 -1
- package/lib/operations/execute_operation.js +71 -79
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +3 -52
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/find_and_modify.js +5 -0
- package/lib/operations/find_and_modify.js.map +1 -1
- package/lib/operations/get_more.js +5 -0
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +8 -9
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +8 -2
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/is_capped.js.map +1 -1
- package/lib/operations/list_collections.js +10 -42
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js +5 -0
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/map_reduce.js +1 -2
- package/lib/operations/map_reduce.js.map +1 -1
- package/lib/operations/operation.js +1 -3
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/options_operation.js.map +1 -1
- package/lib/operations/profiling_level.js.map +1 -1
- package/lib/operations/remove_user.js.map +1 -1
- package/lib/operations/rename.js +1 -1
- package/lib/operations/rename.js.map +1 -1
- package/lib/operations/run_command.js.map +1 -1
- package/lib/operations/set_profiling_level.js.map +1 -1
- package/lib/operations/stats.js.map +1 -1
- package/lib/operations/update.js +5 -0
- package/lib/operations/update.js.map +1 -1
- package/lib/operations/validate_collection.js.map +1 -1
- package/lib/read_concern.js +1 -0
- package/lib/read_concern.js.map +1 -1
- package/lib/sdam/common.js +1 -7
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/events.js +1 -1
- package/lib/sdam/events.js.map +1 -1
- package/lib/sdam/monitor.js +1 -2
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +108 -78
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +38 -55
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +3 -4
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +93 -68
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +21 -97
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +417 -92
- package/package.json +25 -29
- package/src/admin.ts +6 -10
- package/src/bulk/common.ts +45 -14
- package/src/bulk/unordered.ts +1 -1
- package/src/change_stream.ts +559 -425
- package/src/cmap/auth/gssapi.ts +1 -1
- package/src/cmap/auth/mongocr.ts +1 -1
- package/src/cmap/auth/mongodb_aws.ts +6 -1
- package/src/cmap/auth/plain.ts +1 -1
- package/src/cmap/auth/scram.ts +3 -2
- package/src/cmap/auth/x509.ts +6 -2
- package/src/cmap/commands.ts +26 -22
- package/src/cmap/connect.ts +15 -14
- package/src/cmap/connection.ts +163 -185
- package/src/cmap/errors.ts +2 -2
- package/src/cmap/message_stream.ts +2 -2
- package/src/cmap/stream_description.ts +4 -1
- package/src/collection.ts +66 -35
- package/src/connection_string.ts +46 -18
- package/src/constants.ts +6 -0
- package/src/cursor/abstract_cursor.ts +87 -65
- package/src/cursor/aggregation_cursor.ts +4 -4
- package/src/cursor/find_cursor.ts +16 -8
- package/src/db.ts +27 -24
- package/src/deps.ts +40 -0
- package/src/encrypter.ts +22 -11
- package/src/error.ts +170 -89
- package/src/gridfs/download.ts +3 -1
- package/src/gridfs/index.ts +51 -68
- package/src/gridfs/upload.ts +13 -13
- package/src/index.ts +21 -0
- package/src/mongo_client.ts +36 -50
- package/src/mongo_types.ts +1 -1
- package/src/operations/add_user.ts +14 -3
- package/src/operations/aggregate.ts +15 -5
- package/src/operations/bulk_write.ts +6 -2
- package/src/operations/collections.ts +6 -2
- package/src/operations/command.ts +23 -12
- package/src/operations/common_functions.ts +8 -1
- package/src/operations/count.ts +6 -2
- package/src/operations/count_documents.ts +5 -1
- package/src/operations/create_collection.ts +91 -23
- package/src/operations/delete.ts +19 -13
- package/src/operations/distinct.ts +6 -2
- package/src/operations/drop.ts +100 -10
- package/src/operations/estimated_document_count.ts +11 -3
- package/src/operations/eval.ts +6 -2
- package/src/operations/execute_operation.ts +103 -101
- package/src/operations/find.ts +9 -85
- package/src/operations/find_and_modify.ts +21 -2
- package/src/operations/get_more.ts +20 -6
- package/src/operations/indexes.ts +54 -36
- package/src/operations/insert.ts +28 -7
- package/src/operations/is_capped.ts +6 -2
- package/src/operations/list_collections.ts +24 -59
- package/src/operations/list_databases.ts +13 -3
- package/src/operations/map_reduce.ts +7 -6
- package/src/operations/operation.ts +10 -9
- package/src/operations/options_operation.ts +6 -2
- package/src/operations/profiling_level.ts +6 -2
- package/src/operations/remove_user.ts +6 -2
- package/src/operations/rename.ts +7 -3
- package/src/operations/run_command.ts +6 -2
- package/src/operations/set_profiling_level.ts +6 -2
- package/src/operations/stats.ts +12 -4
- package/src/operations/update.ts +19 -9
- package/src/operations/validate_collection.ts +6 -2
- package/src/read_concern.ts +1 -0
- package/src/sdam/common.ts +0 -6
- package/src/sdam/events.ts +2 -2
- package/src/sdam/monitor.ts +4 -5
- package/src/sdam/server.ts +125 -117
- package/src/sdam/topology.ts +39 -78
- package/src/sdam/topology_description.ts +3 -4
- package/src/sessions.ts +108 -78
- package/src/utils.ts +39 -119
- package/tsconfig.json +40 -0
- package/mongodb.ts34.d.ts +0 -5720
package/src/sdam/server.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { Document, Long } from '../bson';
|
|
2
|
-
import {
|
|
3
|
-
CommandOptions,
|
|
4
|
-
Connection,
|
|
5
|
-
DestroyOptions,
|
|
6
|
-
GetMoreOptions,
|
|
7
|
-
QueryOptions
|
|
8
|
-
} from '../cmap/connection';
|
|
2
|
+
import { CommandOptions, Connection, DestroyOptions, GetMoreOptions } from '../cmap/connection';
|
|
9
3
|
import {
|
|
10
4
|
ConnectionPool,
|
|
11
5
|
ConnectionPoolEvents,
|
|
@@ -27,14 +21,16 @@ import type { AutoEncrypter } from '../deps';
|
|
|
27
21
|
import {
|
|
28
22
|
isNetworkErrorBeforeHandshake,
|
|
29
23
|
isNodeShuttingDownError,
|
|
30
|
-
isRetryableWriteError,
|
|
31
24
|
isSDAMUnrecoverableError,
|
|
32
25
|
MongoCompatibilityError,
|
|
33
26
|
MongoError,
|
|
27
|
+
MongoErrorLabel,
|
|
34
28
|
MongoInvalidArgumentError,
|
|
35
29
|
MongoNetworkError,
|
|
36
30
|
MongoNetworkTimeoutError,
|
|
37
|
-
MongoServerClosedError
|
|
31
|
+
MongoServerClosedError,
|
|
32
|
+
MongoUnexpectedServerResponseError,
|
|
33
|
+
needsRetryableWriteLabel
|
|
38
34
|
} from '../error';
|
|
39
35
|
import { Logger } from '../logger';
|
|
40
36
|
import type { ServerApi } from '../mongo_client';
|
|
@@ -43,7 +39,6 @@ import type { ClientSession } from '../sessions';
|
|
|
43
39
|
import { isTransactionCommand } from '../transactions';
|
|
44
40
|
import {
|
|
45
41
|
Callback,
|
|
46
|
-
CallbackWithType,
|
|
47
42
|
collationNotSupported,
|
|
48
43
|
EventEmitterWithState,
|
|
49
44
|
makeStateMachine,
|
|
@@ -98,6 +93,8 @@ export interface ServerPrivate {
|
|
|
98
93
|
pool: ConnectionPool;
|
|
99
94
|
/** MongoDB server API version */
|
|
100
95
|
serverApi?: ServerApi;
|
|
96
|
+
/** A count of the operations currently running against the server. */
|
|
97
|
+
operationCount: number;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
100
|
/** @public */
|
|
@@ -119,7 +116,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
119
116
|
s: ServerPrivate;
|
|
120
117
|
serverApi?: ServerApi;
|
|
121
118
|
hello?: Document;
|
|
122
|
-
[kMonitor]: Monitor;
|
|
119
|
+
[kMonitor]: Monitor | null;
|
|
123
120
|
|
|
124
121
|
/** @event */
|
|
125
122
|
static readonly SERVER_HEARTBEAT_STARTED = SERVER_HEARTBEAT_STARTED;
|
|
@@ -152,7 +149,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
152
149
|
logger: new Logger('Server'),
|
|
153
150
|
state: STATE_CLOSED,
|
|
154
151
|
topology,
|
|
155
|
-
pool: new ConnectionPool(poolOptions)
|
|
152
|
+
pool: new ConnectionPool(poolOptions),
|
|
153
|
+
operationCount: 0
|
|
156
154
|
};
|
|
157
155
|
|
|
158
156
|
for (const event of [...CMAP_EVENTS, ...APM_EVENTS]) {
|
|
@@ -163,22 +161,27 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
163
161
|
this.clusterTime = clusterTime;
|
|
164
162
|
});
|
|
165
163
|
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
if (this.loadBalanced) {
|
|
165
|
+
this[kMonitor] = null;
|
|
166
|
+
// monitoring is disabled in load balancing mode
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
168
169
|
|
|
169
170
|
// create the monitor
|
|
170
|
-
|
|
171
|
+
// TODO(NODE-4144): Remove new variable for type narrowing
|
|
172
|
+
const monitor = new Monitor(this, this.s.options);
|
|
173
|
+
this[kMonitor] = monitor;
|
|
171
174
|
|
|
172
175
|
for (const event of HEARTBEAT_EVENTS) {
|
|
173
|
-
|
|
176
|
+
monitor.on(event, (e: any) => this.emit(event, e));
|
|
174
177
|
}
|
|
175
178
|
|
|
176
|
-
|
|
179
|
+
monitor.on('resetConnectionPool', () => {
|
|
177
180
|
this.s.pool.clear();
|
|
178
181
|
});
|
|
179
182
|
|
|
180
|
-
|
|
181
|
-
|
|
183
|
+
monitor.on('resetServer', (error: MongoError) => markServerUnknown(this, error));
|
|
184
|
+
monitor.on(Server.SERVER_HEARTBEAT_SUCCEEDED, (event: ServerHeartbeatSucceededEvent) => {
|
|
182
185
|
this.emit(
|
|
183
186
|
Server.DESCRIPTION_RECEIVED,
|
|
184
187
|
new ServerDescription(this.description.hostAddress, event.reply, {
|
|
@@ -213,6 +216,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
213
216
|
if (this.s.options && this.s.options.autoEncrypter) {
|
|
214
217
|
return this.s.options.autoEncrypter;
|
|
215
218
|
}
|
|
219
|
+
return;
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
get loadBalanced(): boolean {
|
|
@@ -233,7 +237,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
233
237
|
// a load balancer. It never transitions out of this state and
|
|
234
238
|
// has no monitor.
|
|
235
239
|
if (!this.loadBalanced) {
|
|
236
|
-
this[kMonitor]
|
|
240
|
+
this[kMonitor]?.connect();
|
|
237
241
|
} else {
|
|
238
242
|
stateTransition(this, STATE_CONNECTED);
|
|
239
243
|
this.emit(Server.CONNECT, this);
|
|
@@ -256,7 +260,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
256
260
|
stateTransition(this, STATE_CLOSING);
|
|
257
261
|
|
|
258
262
|
if (!this.loadBalanced) {
|
|
259
|
-
this[kMonitor]
|
|
263
|
+
this[kMonitor]?.close();
|
|
260
264
|
}
|
|
261
265
|
|
|
262
266
|
this.s.pool.close(options, err => {
|
|
@@ -274,7 +278,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
274
278
|
*/
|
|
275
279
|
requestCheck(): void {
|
|
276
280
|
if (!this.loadBalanced) {
|
|
277
|
-
this[kMonitor]
|
|
281
|
+
this[kMonitor]?.requestCheck();
|
|
278
282
|
}
|
|
279
283
|
}
|
|
280
284
|
|
|
@@ -282,24 +286,12 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
282
286
|
* Execute a command
|
|
283
287
|
* @internal
|
|
284
288
|
*/
|
|
285
|
-
command(ns: MongoDBNamespace, cmd: Document, callback: Callback): void;
|
|
286
|
-
/** @internal */
|
|
287
289
|
command(
|
|
288
290
|
ns: MongoDBNamespace,
|
|
289
291
|
cmd: Document,
|
|
290
292
|
options: CommandOptions,
|
|
291
293
|
callback: Callback<Document>
|
|
292
|
-
): void;
|
|
293
|
-
command(
|
|
294
|
-
ns: MongoDBNamespace,
|
|
295
|
-
cmd: Document,
|
|
296
|
-
options?: CommandOptions | Callback<Document>,
|
|
297
|
-
callback?: Callback<Document>
|
|
298
294
|
): void {
|
|
299
|
-
if (typeof options === 'function') {
|
|
300
|
-
(callback = options), (options = {}), (options = options ?? {});
|
|
301
|
-
}
|
|
302
|
-
|
|
303
295
|
if (callback == null) {
|
|
304
296
|
throw new MongoInvalidArgumentError('Callback must be provided');
|
|
305
297
|
}
|
|
@@ -336,6 +328,15 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
336
328
|
// NOTE: This is a hack! We can't retrieve the connections used for executing an operation
|
|
337
329
|
// (and prevent them from being checked back in) at the point of operation execution.
|
|
338
330
|
// This should be considered as part of the work for NODE-2882
|
|
331
|
+
// NOTE:
|
|
332
|
+
// When incrementing operation count, it's important that we increment it before we
|
|
333
|
+
// attempt to check out a connection from the pool. This ensures that operations that
|
|
334
|
+
// are waiting for a connection are included in the operation count. Load balanced
|
|
335
|
+
// mode will only ever have a single server, so the operation count doesn't matter.
|
|
336
|
+
// Incrementing the operation count above the logic to handle load balanced mode would
|
|
337
|
+
// require special logic to decrement it again, or would double increment (the load
|
|
338
|
+
// balanced code makes a recursive call). Instead, we increment the count after this
|
|
339
|
+
// check.
|
|
339
340
|
if (this.loadBalanced && session && conn == null && isPinnableCommand(cmd, session)) {
|
|
340
341
|
this.s.pool.checkOut((err, checkedOut) => {
|
|
341
342
|
if (err || checkedOut == null) {
|
|
@@ -344,16 +345,18 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
344
345
|
}
|
|
345
346
|
|
|
346
347
|
session.pin(checkedOut);
|
|
347
|
-
this.command(ns, cmd, finalOptions, callback
|
|
348
|
+
this.command(ns, cmd, finalOptions, callback);
|
|
348
349
|
});
|
|
349
|
-
|
|
350
350
|
return;
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
+
this.s.operationCount += 1;
|
|
354
|
+
|
|
353
355
|
this.s.pool.withConnection(
|
|
354
356
|
conn,
|
|
355
357
|
(err, conn, cb) => {
|
|
356
358
|
if (err || !conn) {
|
|
359
|
+
this.s.operationCount -= 1;
|
|
357
360
|
markServerUnknown(this, err);
|
|
358
361
|
return cb(err);
|
|
359
362
|
}
|
|
@@ -362,36 +365,10 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
362
365
|
ns,
|
|
363
366
|
cmd,
|
|
364
367
|
finalOptions,
|
|
365
|
-
makeOperationHandler(this, conn, cmd, finalOptions,
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Execute a query against the server
|
|
374
|
-
* @internal
|
|
375
|
-
*/
|
|
376
|
-
query(ns: MongoDBNamespace, cmd: Document, options: QueryOptions, callback: Callback): void {
|
|
377
|
-
if (this.s.state === STATE_CLOSING || this.s.state === STATE_CLOSED) {
|
|
378
|
-
callback(new MongoServerClosedError());
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
this.s.pool.withConnection(
|
|
383
|
-
undefined,
|
|
384
|
-
(err, conn, cb) => {
|
|
385
|
-
if (err || !conn) {
|
|
386
|
-
markServerUnknown(this, err);
|
|
387
|
-
return cb(err);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
conn.query(
|
|
391
|
-
ns,
|
|
392
|
-
cmd,
|
|
393
|
-
options,
|
|
394
|
-
makeOperationHandler(this, conn, cmd, options, cb) as Callback
|
|
368
|
+
makeOperationHandler(this, conn, cmd, finalOptions, (error, response) => {
|
|
369
|
+
this.s.operationCount -= 1;
|
|
370
|
+
cb(error, response);
|
|
371
|
+
})
|
|
395
372
|
);
|
|
396
373
|
},
|
|
397
374
|
callback
|
|
@@ -413,10 +390,13 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
413
390
|
return;
|
|
414
391
|
}
|
|
415
392
|
|
|
393
|
+
this.s.operationCount += 1;
|
|
394
|
+
|
|
416
395
|
this.s.pool.withConnection(
|
|
417
396
|
options.session?.pinnedConnection,
|
|
418
397
|
(err, conn, cb) => {
|
|
419
398
|
if (err || !conn) {
|
|
399
|
+
this.s.operationCount -= 1;
|
|
420
400
|
markServerUnknown(this, err);
|
|
421
401
|
return cb(err);
|
|
422
402
|
}
|
|
@@ -425,7 +405,10 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
425
405
|
ns,
|
|
426
406
|
cursorId,
|
|
427
407
|
options,
|
|
428
|
-
makeOperationHandler(this, conn, {}, options,
|
|
408
|
+
makeOperationHandler(this, conn, {}, options, (error, response) => {
|
|
409
|
+
this.s.operationCount -= 1;
|
|
410
|
+
cb(error, response);
|
|
411
|
+
})
|
|
429
412
|
);
|
|
430
413
|
},
|
|
431
414
|
callback
|
|
@@ -450,10 +433,12 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
450
433
|
return;
|
|
451
434
|
}
|
|
452
435
|
|
|
436
|
+
this.s.operationCount += 1;
|
|
453
437
|
this.s.pool.withConnection(
|
|
454
438
|
options.session?.pinnedConnection,
|
|
455
439
|
(err, conn, cb) => {
|
|
456
440
|
if (err || !conn) {
|
|
441
|
+
this.s.operationCount -= 1;
|
|
457
442
|
markServerUnknown(this, err);
|
|
458
443
|
return cb(err);
|
|
459
444
|
}
|
|
@@ -462,7 +447,10 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
462
447
|
ns,
|
|
463
448
|
cursorIds,
|
|
464
449
|
options,
|
|
465
|
-
makeOperationHandler(this, conn, {}, undefined,
|
|
450
|
+
makeOperationHandler(this, conn, {}, undefined, (error, response) => {
|
|
451
|
+
this.s.operationCount -= 1;
|
|
452
|
+
cb(error, response);
|
|
453
|
+
})
|
|
466
454
|
);
|
|
467
455
|
},
|
|
468
456
|
callback
|
|
@@ -486,7 +474,7 @@ function markServerUnknown(server: Server, error?: MongoError) {
|
|
|
486
474
|
}
|
|
487
475
|
|
|
488
476
|
if (error instanceof MongoNetworkError && !(error instanceof MongoNetworkTimeoutError)) {
|
|
489
|
-
server[kMonitor]
|
|
477
|
+
server[kMonitor]?.reset();
|
|
490
478
|
}
|
|
491
479
|
|
|
492
480
|
server.emit(
|
|
@@ -546,67 +534,87 @@ function makeOperationHandler(
|
|
|
546
534
|
cmd: Document,
|
|
547
535
|
options: CommandOptions | GetMoreOptions | undefined,
|
|
548
536
|
callback: Callback
|
|
549
|
-
):
|
|
537
|
+
): Callback {
|
|
550
538
|
const session = options?.session;
|
|
551
|
-
return function handleOperationResult(
|
|
552
|
-
if (
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
session.serverSession.isDirty = true;
|
|
556
|
-
}
|
|
539
|
+
return function handleOperationResult(error, result) {
|
|
540
|
+
if (result != null) {
|
|
541
|
+
return callback(undefined, result);
|
|
542
|
+
}
|
|
557
543
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
544
|
+
if (!error) {
|
|
545
|
+
return callback(new MongoUnexpectedServerResponseError('Empty response with no error'));
|
|
546
|
+
}
|
|
562
547
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
) {
|
|
568
|
-
err.addErrorLabel('RetryableWriteError');
|
|
569
|
-
}
|
|
548
|
+
if (!(error instanceof MongoError)) {
|
|
549
|
+
// Node.js or some other error we have not special handling for
|
|
550
|
+
return callback(error);
|
|
551
|
+
}
|
|
570
552
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
553
|
+
if (connectionIsStale(server.s.pool, connection)) {
|
|
554
|
+
return callback(error);
|
|
555
|
+
}
|
|
574
556
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
557
|
+
if (error instanceof MongoNetworkError) {
|
|
558
|
+
if (session && !session.hasEnded && session.serverSession) {
|
|
559
|
+
session.serverSession.isDirty = true;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// inActiveTransaction check handles commit and abort.
|
|
563
|
+
if (
|
|
564
|
+
inActiveTransaction(session, cmd) &&
|
|
565
|
+
!error.hasErrorLabel(MongoErrorLabel.TransientTransactionError)
|
|
566
|
+
) {
|
|
567
|
+
error.addErrorLabel(MongoErrorLabel.TransientTransactionError);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (
|
|
571
|
+
(isRetryableWritesEnabled(server.s.topology) || isTransactionCommand(cmd)) &&
|
|
572
|
+
supportsRetryableWrites(server) &&
|
|
573
|
+
!inActiveTransaction(session, cmd)
|
|
574
|
+
) {
|
|
575
|
+
error.addErrorLabel(MongoErrorLabel.RetryableWriteError);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (!(error instanceof MongoNetworkTimeoutError) || isNetworkErrorBeforeHandshake(error)) {
|
|
579
|
+
// In load balanced mode we never mark the server as unknown and always
|
|
580
|
+
// clear for the specific service id.
|
|
581
|
+
|
|
582
|
+
server.s.pool.clear(connection.serviceId);
|
|
583
|
+
if (!server.loadBalanced) {
|
|
584
|
+
markServerUnknown(server, error);
|
|
589
585
|
}
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
if (
|
|
589
|
+
(isRetryableWritesEnabled(server.s.topology) || isTransactionCommand(cmd)) &&
|
|
590
|
+
needsRetryableWriteLabel(error, maxWireVersion(server)) &&
|
|
591
|
+
!inActiveTransaction(session, cmd)
|
|
592
|
+
) {
|
|
593
|
+
error.addErrorLabel(MongoErrorLabel.RetryableWriteError);
|
|
594
|
+
}
|
|
590
595
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
+
if (isSDAMUnrecoverableError(error)) {
|
|
597
|
+
if (shouldHandleStateChangeError(server, error)) {
|
|
598
|
+
if (maxWireVersion(server) <= 7 || isNodeShuttingDownError(error)) {
|
|
599
|
+
server.s.pool.clear(connection.serviceId);
|
|
600
|
+
}
|
|
596
601
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
602
|
+
if (!server.loadBalanced) {
|
|
603
|
+
markServerUnknown(server, error);
|
|
604
|
+
process.nextTick(() => server.requestCheck());
|
|
601
605
|
}
|
|
602
606
|
}
|
|
603
607
|
}
|
|
608
|
+
}
|
|
604
609
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
610
|
+
if (
|
|
611
|
+
session &&
|
|
612
|
+
session.isPinned &&
|
|
613
|
+
error.hasErrorLabel(MongoErrorLabel.TransientTransactionError)
|
|
614
|
+
) {
|
|
615
|
+
session.unpin({ force: true });
|
|
608
616
|
}
|
|
609
617
|
|
|
610
|
-
callback(
|
|
618
|
+
return callback(error);
|
|
611
619
|
};
|
|
612
620
|
}
|
package/src/sdam/topology.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { deserialize, serialize } from '../bson';
|
|
|
4
4
|
import type { MongoCredentials } from '../cmap/auth/mongo_credentials';
|
|
5
5
|
import type { ConnectionEvents, DestroyOptions } from '../cmap/connection';
|
|
6
6
|
import type { CloseOptions, ConnectionPoolEvents } from '../cmap/connection_pool';
|
|
7
|
-
import { DEFAULT_OPTIONS } from '../connection_string';
|
|
7
|
+
import { DEFAULT_OPTIONS, FEATURE_FLAGS } from '../connection_string';
|
|
8
8
|
import {
|
|
9
9
|
CLOSE,
|
|
10
10
|
CONNECT,
|
|
@@ -50,7 +50,6 @@ import {
|
|
|
50
50
|
} from '../utils';
|
|
51
51
|
import {
|
|
52
52
|
_advanceClusterTime,
|
|
53
|
-
clearAndRemoveTimerFrom,
|
|
54
53
|
ClusterTime,
|
|
55
54
|
drainTimerQueue,
|
|
56
55
|
ServerType,
|
|
@@ -154,6 +153,8 @@ export interface TopologyOptions extends BSONSerializeOptions, ServerOptions {
|
|
|
154
153
|
metadata: ClientMetadata;
|
|
155
154
|
/** MongoDB server API version */
|
|
156
155
|
serverApi?: ServerApi;
|
|
156
|
+
/** @internal */
|
|
157
|
+
[featureFlag: symbol]: any;
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
/** @public */
|
|
@@ -249,22 +250,8 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
249
250
|
// Options should only be undefined in tests, MongoClient will always have defined options
|
|
250
251
|
options = options ?? {
|
|
251
252
|
hosts: [HostAddress.fromString('localhost:27017')],
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
serverSelectionTimeoutMS: DEFAULT_OPTIONS.get('serverSelectionTimeoutMS'),
|
|
255
|
-
directConnection: DEFAULT_OPTIONS.get('directConnection'),
|
|
256
|
-
loadBalanced: DEFAULT_OPTIONS.get('loadBalanced'),
|
|
257
|
-
metadata: DEFAULT_OPTIONS.get('metadata'),
|
|
258
|
-
monitorCommands: DEFAULT_OPTIONS.get('monitorCommands'),
|
|
259
|
-
tls: DEFAULT_OPTIONS.get('tls'),
|
|
260
|
-
maxPoolSize: DEFAULT_OPTIONS.get('maxPoolSize'),
|
|
261
|
-
minPoolSize: DEFAULT_OPTIONS.get('minPoolSize'),
|
|
262
|
-
waitQueueTimeoutMS: DEFAULT_OPTIONS.get('waitQueueTimeoutMS'),
|
|
263
|
-
connectionType: DEFAULT_OPTIONS.get('connectionType'),
|
|
264
|
-
connectTimeoutMS: DEFAULT_OPTIONS.get('connectTimeoutMS'),
|
|
265
|
-
maxIdleTimeMS: DEFAULT_OPTIONS.get('maxIdleTimeMS'),
|
|
266
|
-
heartbeatFrequencyMS: DEFAULT_OPTIONS.get('heartbeatFrequencyMS'),
|
|
267
|
-
minHeartbeatFrequencyMS: DEFAULT_OPTIONS.get('minHeartbeatFrequencyMS')
|
|
253
|
+
...Object.fromEntries(DEFAULT_OPTIONS.entries()),
|
|
254
|
+
...Object.fromEntries(FEATURE_FLAGS.entries())
|
|
268
255
|
};
|
|
269
256
|
|
|
270
257
|
if (typeof seeds === 'string') {
|
|
@@ -334,7 +321,6 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
334
321
|
|
|
335
322
|
// timer management
|
|
336
323
|
connectionTimers: new Set<NodeJS.Timeout>(),
|
|
337
|
-
|
|
338
324
|
detectShardedTopology: ev => this.detectShardedTopology(ev),
|
|
339
325
|
detectSrvRecords: ev => this.detectSrvRecords(ev)
|
|
340
326
|
};
|
|
@@ -435,7 +421,12 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
435
421
|
|
|
436
422
|
// connect all known servers, then attempt server selection to connect
|
|
437
423
|
const serverDescriptions = Array.from(this.s.description.servers.values());
|
|
438
|
-
|
|
424
|
+
this.s.servers = new Map(
|
|
425
|
+
serverDescriptions.map(serverDescription => [
|
|
426
|
+
serverDescription.address,
|
|
427
|
+
createAndConnectServer(this, serverDescription)
|
|
428
|
+
])
|
|
429
|
+
);
|
|
439
430
|
|
|
440
431
|
// In load balancer mode we need to fake a server description getting
|
|
441
432
|
// emitted from the monitor, since the monitor doesn't exist.
|
|
@@ -458,8 +449,9 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
458
449
|
}
|
|
459
450
|
|
|
460
451
|
// TODO: NODE-2471
|
|
461
|
-
|
|
462
|
-
|
|
452
|
+
const skipPingOnConnect = this.s.options[Symbol.for('@@mdb.skipPingOnConnect')] === true;
|
|
453
|
+
if (!skipPingOnConnect && server && this.s.credentials) {
|
|
454
|
+
server.command(ns('admin.$cmd'), { ping: 1 }, {}, err => {
|
|
463
455
|
if (err) {
|
|
464
456
|
typeof callback === 'function' ? callback(err) : this.emit(Topology.ERROR, err);
|
|
465
457
|
return;
|
|
@@ -549,27 +541,11 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
549
541
|
* @param callback - The callback used to indicate success or failure
|
|
550
542
|
* @returns An instance of a `Server` meeting the criteria of the predicate provided
|
|
551
543
|
*/
|
|
552
|
-
selectServer(options: SelectServerOptions, callback: Callback<Server>): void;
|
|
553
|
-
selectServer(
|
|
554
|
-
selector: string | ReadPreference | ServerSelector,
|
|
555
|
-
callback: Callback<Server>
|
|
556
|
-
): void;
|
|
557
544
|
selectServer(
|
|
558
545
|
selector: string | ReadPreference | ServerSelector,
|
|
559
546
|
options: SelectServerOptions,
|
|
560
547
|
callback: Callback<Server>
|
|
561
|
-
): void;
|
|
562
|
-
selectServer(
|
|
563
|
-
selector: string | ReadPreference | ServerSelector | SelectServerOptions,
|
|
564
|
-
_options?: SelectServerOptions | Callback<Server>,
|
|
565
|
-
_callback?: Callback<Server>
|
|
566
548
|
): void {
|
|
567
|
-
let options = _options as SelectServerOptions;
|
|
568
|
-
const callback = (_callback ?? _options) as Callback<Server>;
|
|
569
|
-
if (typeof options === 'function') {
|
|
570
|
-
options = {};
|
|
571
|
-
}
|
|
572
|
-
|
|
573
549
|
let serverSelector;
|
|
574
550
|
if (typeof selector !== 'function') {
|
|
575
551
|
if (typeof selector === 'string') {
|
|
@@ -667,6 +643,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
667
643
|
|
|
668
644
|
this.selectServer(
|
|
669
645
|
readPreferenceServerSelector(ReadPreference.primaryPreferred),
|
|
646
|
+
{},
|
|
670
647
|
(err, server) => {
|
|
671
648
|
if (err || !server) {
|
|
672
649
|
if (typeof callback === 'function') callback(err);
|
|
@@ -866,22 +843,13 @@ function topologyTypeFromOptions(options?: TopologyOptions) {
|
|
|
866
843
|
return TopologyType.Unknown;
|
|
867
844
|
}
|
|
868
845
|
|
|
869
|
-
function randomSelection(array: ServerDescription[]): ServerDescription {
|
|
870
|
-
return array[Math.floor(Math.random() * array.length)];
|
|
871
|
-
}
|
|
872
|
-
|
|
873
846
|
/**
|
|
874
847
|
* Creates new server instances and attempts to connect them
|
|
875
848
|
*
|
|
876
849
|
* @param topology - The topology that this server belongs to
|
|
877
850
|
* @param serverDescription - The description for the server to initialize and connect to
|
|
878
|
-
* @param connectDelay - Time to wait before attempting initial connection
|
|
879
851
|
*/
|
|
880
|
-
function createAndConnectServer(
|
|
881
|
-
topology: Topology,
|
|
882
|
-
serverDescription: ServerDescription,
|
|
883
|
-
connectDelay?: number
|
|
884
|
-
) {
|
|
852
|
+
function createAndConnectServer(topology: Topology, serverDescription: ServerDescription) {
|
|
885
853
|
topology.emit(
|
|
886
854
|
Topology.SERVER_OPENING,
|
|
887
855
|
new ServerOpeningEvent(topology.s.id, serverDescription.address)
|
|
@@ -894,38 +862,10 @@ function createAndConnectServer(
|
|
|
894
862
|
|
|
895
863
|
server.on(Server.DESCRIPTION_RECEIVED, description => topology.serverUpdateHandler(description));
|
|
896
864
|
|
|
897
|
-
if (connectDelay) {
|
|
898
|
-
const connectTimer = setTimeout(() => {
|
|
899
|
-
clearAndRemoveTimerFrom(connectTimer, topology.s.connectionTimers);
|
|
900
|
-
server.connect();
|
|
901
|
-
}, connectDelay);
|
|
902
|
-
|
|
903
|
-
topology.s.connectionTimers.add(connectTimer);
|
|
904
|
-
return server;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
865
|
server.connect();
|
|
908
866
|
return server;
|
|
909
867
|
}
|
|
910
868
|
|
|
911
|
-
/**
|
|
912
|
-
* Create `Server` instances for all initially known servers, connect them, and assign
|
|
913
|
-
* them to the passed in `Topology`.
|
|
914
|
-
*
|
|
915
|
-
* @param topology - The topology responsible for the servers
|
|
916
|
-
* @param serverDescriptions - A list of server descriptions to connect
|
|
917
|
-
*/
|
|
918
|
-
function connectServers(topology: Topology, serverDescriptions: ServerDescription[]) {
|
|
919
|
-
topology.s.servers = serverDescriptions.reduce(
|
|
920
|
-
(servers: Map<string, Server>, serverDescription: ServerDescription) => {
|
|
921
|
-
const server = createAndConnectServer(topology, serverDescription);
|
|
922
|
-
servers.set(serverDescription.address, server);
|
|
923
|
-
return servers;
|
|
924
|
-
},
|
|
925
|
-
new Map<string, Server>()
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
|
|
929
869
|
/**
|
|
930
870
|
* @param topology - Topology to update.
|
|
931
871
|
* @param incomingServerDescription - New server description.
|
|
@@ -1019,13 +959,34 @@ function processWaitQueue(topology: Topology) {
|
|
|
1019
959
|
continue;
|
|
1020
960
|
}
|
|
1021
961
|
|
|
962
|
+
let selectedServer;
|
|
1022
963
|
if (selectedDescriptions.length === 0) {
|
|
1023
964
|
topology[kWaitQueue].push(waitQueueMember);
|
|
1024
965
|
continue;
|
|
966
|
+
} else if (selectedDescriptions.length === 1) {
|
|
967
|
+
selectedServer = topology.s.servers.get(selectedDescriptions[0].address);
|
|
968
|
+
} else {
|
|
969
|
+
// don't shuffle the array if there are only two elements
|
|
970
|
+
const descriptions =
|
|
971
|
+
selectedDescriptions.length === 2 ? selectedDescriptions : shuffle(selectedDescriptions, 2);
|
|
972
|
+
const server1 = topology.s.servers.get(descriptions[0].address);
|
|
973
|
+
const server2 = topology.s.servers.get(descriptions[1].address);
|
|
974
|
+
|
|
975
|
+
selectedServer =
|
|
976
|
+
server1 && server2 && server1.s.operationCount < server2.s.operationCount
|
|
977
|
+
? server1
|
|
978
|
+
: server2;
|
|
1025
979
|
}
|
|
1026
980
|
|
|
1027
|
-
|
|
1028
|
-
|
|
981
|
+
if (!selectedServer) {
|
|
982
|
+
waitQueueMember.callback(
|
|
983
|
+
new MongoServerSelectionError(
|
|
984
|
+
'server selection returned a server description but the server was not found in the topology',
|
|
985
|
+
topology.description
|
|
986
|
+
)
|
|
987
|
+
);
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
1029
990
|
const transaction = waitQueueMember.transaction;
|
|
1030
991
|
if (isSharded && transaction && transaction.isActive && selectedServer) {
|
|
1031
992
|
transaction.pinServer(selectedServer);
|
|
@@ -58,15 +58,12 @@ export class TopologyDescription {
|
|
|
58
58
|
) {
|
|
59
59
|
options = options ?? {};
|
|
60
60
|
|
|
61
|
-
// TODO: consider assigning all these values to a temporary value `s` which
|
|
62
|
-
// we use `Object.freeze` on, ensuring the internal state of this type
|
|
63
|
-
// is immutable.
|
|
64
61
|
this.type = topologyType ?? TopologyType.Unknown;
|
|
65
62
|
this.servers = serverDescriptions ?? new Map();
|
|
66
63
|
this.stale = false;
|
|
67
64
|
this.compatible = true;
|
|
68
65
|
this.heartbeatFrequencyMS = options.heartbeatFrequencyMS ?? 0;
|
|
69
|
-
this.localThresholdMS = options.localThresholdMS ??
|
|
66
|
+
this.localThresholdMS = options.localThresholdMS ?? 15;
|
|
70
67
|
|
|
71
68
|
if (setName) {
|
|
72
69
|
this.setName = setName;
|
|
@@ -204,6 +201,7 @@ export class TopologyDescription {
|
|
|
204
201
|
let { type: topologyType, setName, maxSetVersion, maxElectionId, commonWireVersion } = this;
|
|
205
202
|
|
|
206
203
|
if (serverDescription.setName && setName && serverDescription.setName !== setName) {
|
|
204
|
+
// TODO(NODE-4159): servers with an incorrect setName should be removed not marked Unknown
|
|
207
205
|
serverDescription = new ServerDescription(address, undefined);
|
|
208
206
|
}
|
|
209
207
|
|
|
@@ -321,6 +319,7 @@ export class TopologyDescription {
|
|
|
321
319
|
if (descriptionsWithError.length > 0) {
|
|
322
320
|
return descriptionsWithError[0].error;
|
|
323
321
|
}
|
|
322
|
+
return;
|
|
324
323
|
}
|
|
325
324
|
|
|
326
325
|
/**
|