mongodb 6.5.0 → 6.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 +9 -9
- package/lib/admin.js +9 -9
- package/lib/admin.js.map +1 -1
- package/lib/bson.js +33 -22
- package/lib/bson.js.map +1 -1
- package/lib/bulk/common.js +13 -12
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +28 -18
- package/lib/change_stream.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +2 -2
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +6 -6
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/providers/aws.js +13 -10
- package/lib/client-side-encryption/providers/aws.js.map +1 -1
- package/lib/client-side-encryption/providers/azure.js +6 -3
- package/lib/client-side-encryption/providers/azure.js.map +1 -1
- package/lib/cmap/auth/aws_temporary_credentials.js +140 -0
- package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -0
- package/lib/cmap/auth/gssapi.js +7 -6
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +8 -101
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js +1 -1
- package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js +2 -1
- package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/service_workflow.js +1 -1
- package/lib/cmap/auth/mongodb_oidc/service_workflow.js.map +1 -1
- package/lib/cmap/auth/scram.js +2 -2
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/commands.js +24 -111
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +4 -4
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +61 -36
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +22 -13
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/handshake/client_metadata.js +2 -2
- package/lib/cmap/handshake/client_metadata.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +8 -8
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/cmap/wire_protocol/on_demand/document.js +218 -0
- package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -0
- package/lib/cmap/wire_protocol/responses.js +184 -0
- package/lib/cmap/wire_protocol/responses.js.map +1 -0
- package/lib/collection.js +42 -38
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +4 -6
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +76 -43
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +16 -33
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +36 -18
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/cursor/run_command_cursor.js +3 -2
- package/lib/cursor/run_command_cursor.js.map +1 -1
- package/lib/db.js +15 -19
- package/lib/db.js.map +1 -1
- package/lib/deps.js +31 -26
- package/lib/deps.js.map +1 -1
- package/lib/encrypter.js +14 -5
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +4 -3
- package/lib/error.js.map +1 -1
- package/lib/gridfs/download.js +19 -14
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +6 -1
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +11 -7
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_logger.js +3 -0
- package/lib/mongo_logger.js.map +1 -1
- package/lib/operations/aggregate.js +2 -1
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/command.js +1 -1
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/create_collection.js +1 -1
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/delete.js +4 -3
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/drop.js +1 -1
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/execute_operation.js +23 -8
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +4 -4
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/get_more.js +2 -1
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +29 -121
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +3 -3
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/kill_cursors.js +3 -1
- package/lib/operations/kill_cursors.js.map +1 -1
- package/lib/operations/list_collections.js +1 -1
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js +1 -1
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/run_command.js +4 -2
- package/lib/operations/run_command.js.map +1 -1
- package/lib/operations/search_indexes/create.js.map +1 -1
- package/lib/operations/stats.js +1 -1
- package/lib/operations/stats.js.map +1 -1
- package/lib/operations/update.js +1 -1
- package/lib/operations/update.js.map +1 -1
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/monitor.js +139 -42
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +5 -15
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +1 -0
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/server_selection.js +1 -1
- package/lib/sdam/server_selection.js.map +1 -1
- package/lib/sdam/srv_polling.js +2 -1
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +67 -54
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +10 -0
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +133 -93
- package/lib/sessions.js.map +1 -1
- package/lib/timeout.js +77 -0
- package/lib/timeout.js.map +1 -0
- package/lib/utils.js +61 -28
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +150 -38
- package/package.json +16 -13
- package/src/admin.ts +9 -9
- package/src/bson.ts +14 -0
- package/src/bulk/common.ts +3 -2
- package/src/change_stream.ts +39 -30
- package/src/client-side-encryption/auto_encrypter.ts +2 -2
- package/src/client-side-encryption/client_encryption.ts +6 -6
- package/src/client-side-encryption/providers/aws.ts +17 -10
- package/src/client-side-encryption/providers/azure.ts +5 -3
- package/src/cmap/auth/aws_temporary_credentials.ts +169 -0
- package/src/cmap/auth/gssapi.ts +9 -11
- package/src/cmap/auth/mongodb_aws.ts +19 -126
- package/src/cmap/auth/mongodb_oidc/aws_service_workflow.ts +1 -1
- package/src/cmap/auth/mongodb_oidc/callback_lock_cache.ts +2 -1
- package/src/cmap/auth/mongodb_oidc/service_workflow.ts +1 -1
- package/src/cmap/auth/scram.ts +2 -2
- package/src/cmap/commands.ts +28 -132
- package/src/cmap/connect.ts +4 -4
- package/src/cmap/connection.ts +107 -43
- package/src/cmap/connection_pool.ts +32 -29
- package/src/cmap/handshake/client_metadata.ts +2 -5
- package/src/cmap/wire_protocol/compression.ts +11 -13
- package/src/cmap/wire_protocol/on_demand/document.ts +338 -0
- package/src/cmap/wire_protocol/responses.ts +237 -0
- package/src/collection.ts +87 -58
- package/src/connection_string.ts +9 -7
- package/src/cursor/abstract_cursor.ts +102 -38
- package/src/cursor/aggregation_cursor.ts +32 -34
- package/src/cursor/find_cursor.ts +33 -21
- package/src/cursor/list_search_indexes_cursor.ts +1 -1
- package/src/cursor/run_command_cursor.ts +3 -2
- package/src/db.ts +42 -21
- package/src/deps.ts +52 -40
- package/src/encrypter.ts +14 -5
- package/src/error.ts +9 -3
- package/src/gridfs/download.ts +19 -31
- package/src/gridfs/index.ts +2 -0
- package/src/gridfs/upload.ts +11 -8
- package/src/index.ts +13 -5
- package/src/mongo_client.ts +21 -15
- package/src/mongo_logger.ts +3 -0
- package/src/mongo_types.ts +1 -1
- package/src/operations/aggregate.ts +2 -1
- package/src/operations/command.ts +1 -1
- package/src/operations/create_collection.ts +7 -2
- package/src/operations/delete.ts +4 -3
- package/src/operations/drop.ts +1 -1
- package/src/operations/execute_operation.ts +29 -10
- package/src/operations/find.ts +13 -14
- package/src/operations/get_more.ts +9 -1
- package/src/operations/indexes.ts +103 -176
- package/src/operations/insert.ts +2 -2
- package/src/operations/kill_cursors.ts +3 -2
- package/src/operations/list_collections.ts +5 -1
- package/src/operations/list_databases.ts +1 -1
- package/src/operations/operation.ts +3 -0
- package/src/operations/run_command.ts +6 -4
- package/src/operations/search_indexes/create.ts +4 -1
- package/src/operations/stats.ts +1 -1
- package/src/operations/update.ts +7 -7
- package/src/sdam/common.ts +8 -2
- package/src/sdam/monitor.ts +178 -61
- package/src/sdam/server.ts +27 -20
- package/src/sdam/server_description.ts +8 -3
- package/src/sdam/server_selection.ts +2 -3
- package/src/sdam/srv_polling.ts +3 -2
- package/src/sdam/topology.ts +114 -117
- package/src/sdam/topology_description.ts +14 -4
- package/src/sessions.ts +168 -148
- package/src/timeout.ts +96 -0
- package/src/utils.ts +85 -32
- package/lib/operations/common_functions.js +0 -38
- package/lib/operations/common_functions.js.map +0 -1
- package/src/operations/common_functions.ts +0 -79
package/src/sdam/monitor.ts
CHANGED
|
@@ -8,8 +8,14 @@ import { LEGACY_HELLO_COMMAND } from '../constants';
|
|
|
8
8
|
import { MongoError, MongoErrorLabel, MongoNetworkTimeoutError } from '../error';
|
|
9
9
|
import { MongoLoggableComponent } from '../mongo_logger';
|
|
10
10
|
import { CancellationToken, TypedEventEmitter } from '../mongo_types';
|
|
11
|
-
import
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
calculateDurationInMs,
|
|
13
|
+
type Callback,
|
|
14
|
+
type EventEmitterWithState,
|
|
15
|
+
makeStateMachine,
|
|
16
|
+
now,
|
|
17
|
+
ns
|
|
18
|
+
} from '../utils';
|
|
13
19
|
import { ServerType, STATE_CLOSED, STATE_CLOSING } from './common';
|
|
14
20
|
import {
|
|
15
21
|
ServerHeartbeatFailedEvent,
|
|
@@ -25,8 +31,6 @@ const kServer = Symbol('server');
|
|
|
25
31
|
const kMonitorId = Symbol('monitorId');
|
|
26
32
|
/** @internal */
|
|
27
33
|
const kCancellationToken = Symbol('cancellationToken');
|
|
28
|
-
/** @internal */
|
|
29
|
-
const kRoundTripTime = Symbol('roundTripTime');
|
|
30
34
|
|
|
31
35
|
const STATE_IDLE = 'idle';
|
|
32
36
|
const STATE_MONITORING = 'monitoring';
|
|
@@ -100,6 +104,8 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
100
104
|
rttPinger?: RTTPinger;
|
|
101
105
|
/** @internal */
|
|
102
106
|
override component = MongoLoggableComponent.TOPOLOGY;
|
|
107
|
+
/** @internal */
|
|
108
|
+
private rttSampler: RTTSampler;
|
|
103
109
|
|
|
104
110
|
constructor(server: Server, options: MonitorOptions) {
|
|
105
111
|
super();
|
|
@@ -121,6 +127,7 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
121
127
|
});
|
|
122
128
|
this.isRunningInFaasEnv = getFAASEnv() != null;
|
|
123
129
|
this.mongoLogger = this[kServer].topology.client?.mongoLogger;
|
|
130
|
+
this.rttSampler = new RTTSampler(10);
|
|
124
131
|
|
|
125
132
|
const cancellationToken = this[kCancellationToken];
|
|
126
133
|
// TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
|
|
@@ -203,6 +210,26 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
203
210
|
this.emit('close');
|
|
204
211
|
stateTransition(this, STATE_CLOSED);
|
|
205
212
|
}
|
|
213
|
+
|
|
214
|
+
get roundTripTime(): number {
|
|
215
|
+
return this.rttSampler.average();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
get minRoundTripTime(): number {
|
|
219
|
+
return this.rttSampler.min();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
get latestRtt(): number {
|
|
223
|
+
return this.rttSampler.last ?? 0; // FIXME: Check if this is acceptable
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
addRttSample(rtt: number) {
|
|
227
|
+
this.rttSampler.addSample(rtt);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
clearRttSamples() {
|
|
231
|
+
this.rttSampler.clear();
|
|
232
|
+
}
|
|
206
233
|
}
|
|
207
234
|
|
|
208
235
|
function resetMonitorState(monitor: Monitor) {
|
|
@@ -216,6 +243,8 @@ function resetMonitorState(monitor: Monitor) {
|
|
|
216
243
|
|
|
217
244
|
monitor.connection?.destroy();
|
|
218
245
|
monitor.connection = null;
|
|
246
|
+
|
|
247
|
+
monitor.clearRttSamples();
|
|
219
248
|
}
|
|
220
249
|
|
|
221
250
|
function useStreamingProtocol(monitor: Monitor, topologyVersion: TopologyVersion | null): boolean {
|
|
@@ -249,7 +278,6 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
249
278
|
function onHeartbeatFailed(err: Error) {
|
|
250
279
|
monitor.connection?.destroy();
|
|
251
280
|
monitor.connection = null;
|
|
252
|
-
|
|
253
281
|
monitor.emitAndLogHeartbeat(
|
|
254
282
|
Server.SERVER_HEARTBEAT_FAILED,
|
|
255
283
|
monitor[kServer].topology.s.id,
|
|
@@ -275,11 +303,15 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
275
303
|
hello.isWritablePrimary = hello[LEGACY_HELLO_COMMAND];
|
|
276
304
|
}
|
|
277
305
|
|
|
306
|
+
// NOTE: here we use the latestRtt as this measurement corresponds with the value
|
|
307
|
+
// obtained for this successful heartbeat
|
|
278
308
|
const duration =
|
|
279
309
|
isAwaitable && monitor.rttPinger
|
|
280
|
-
? monitor.rttPinger.
|
|
310
|
+
? monitor.rttPinger.latestRtt ?? calculateDurationInMs(start)
|
|
281
311
|
: calculateDurationInMs(start);
|
|
282
312
|
|
|
313
|
+
monitor.addRttSample(duration);
|
|
314
|
+
|
|
283
315
|
monitor.emitAndLogHeartbeat(
|
|
284
316
|
Server.SERVER_HEARTBEAT_SUCCEEDED,
|
|
285
317
|
monitor[kServer].topology.s.id,
|
|
@@ -328,13 +360,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
328
360
|
: { socketTimeoutMS: connectTimeoutMS };
|
|
329
361
|
|
|
330
362
|
if (isAwaitable && monitor.rttPinger == null) {
|
|
331
|
-
monitor.rttPinger = new RTTPinger(
|
|
332
|
-
monitor[kCancellationToken],
|
|
333
|
-
Object.assign(
|
|
334
|
-
{ heartbeatFrequencyMS: monitor.options.heartbeatFrequencyMS },
|
|
335
|
-
monitor.connectOptions
|
|
336
|
-
)
|
|
337
|
-
);
|
|
363
|
+
monitor.rttPinger = new RTTPinger(monitor);
|
|
338
364
|
}
|
|
339
365
|
|
|
340
366
|
// Record new start time before sending handshake
|
|
@@ -351,6 +377,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
351
377
|
awaited = false;
|
|
352
378
|
connection
|
|
353
379
|
.command(ns('admin.$cmd'), cmd, options)
|
|
380
|
+
// eslint-disable-next-line github/no-then
|
|
354
381
|
.then(onHeartbeatSucceeded, onHeartbeatFailed);
|
|
355
382
|
|
|
356
383
|
return;
|
|
@@ -369,12 +396,15 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
369
396
|
connection.destroy();
|
|
370
397
|
throw error;
|
|
371
398
|
}
|
|
399
|
+
// eslint-disable-next-line github/no-then
|
|
372
400
|
})().then(
|
|
373
401
|
connection => {
|
|
374
402
|
if (isInCloseState(monitor)) {
|
|
375
403
|
connection.destroy();
|
|
376
404
|
return;
|
|
377
405
|
}
|
|
406
|
+
const duration = calculateDurationInMs(start);
|
|
407
|
+
monitor.addRttSample(duration);
|
|
378
408
|
|
|
379
409
|
monitor.connection = connection;
|
|
380
410
|
monitor.emitAndLogHeartbeat(
|
|
@@ -383,7 +413,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
383
413
|
connection.hello?.connectionId,
|
|
384
414
|
new ServerHeartbeatSucceededEvent(
|
|
385
415
|
monitor.address,
|
|
386
|
-
|
|
416
|
+
duration,
|
|
387
417
|
connection.hello,
|
|
388
418
|
useStreamingProtocol(monitor, connection.hello?.topologyVersion)
|
|
389
419
|
)
|
|
@@ -456,23 +486,30 @@ export class RTTPinger {
|
|
|
456
486
|
/** @internal */
|
|
457
487
|
[kCancellationToken]: CancellationToken;
|
|
458
488
|
/** @internal */
|
|
459
|
-
[kRoundTripTime]: number;
|
|
460
|
-
/** @internal */
|
|
461
489
|
[kMonitorId]: NodeJS.Timeout;
|
|
490
|
+
/** @internal */
|
|
491
|
+
monitor: Monitor;
|
|
462
492
|
closed: boolean;
|
|
493
|
+
/** @internal */
|
|
494
|
+
latestRtt?: number;
|
|
463
495
|
|
|
464
|
-
constructor(
|
|
496
|
+
constructor(monitor: Monitor) {
|
|
465
497
|
this.connection = undefined;
|
|
466
|
-
this[kCancellationToken] =
|
|
467
|
-
this[kRoundTripTime] = 0;
|
|
498
|
+
this[kCancellationToken] = monitor[kCancellationToken];
|
|
468
499
|
this.closed = false;
|
|
500
|
+
this.monitor = monitor;
|
|
501
|
+
this.latestRtt = monitor.latestRtt;
|
|
469
502
|
|
|
470
|
-
const heartbeatFrequencyMS = options.heartbeatFrequencyMS;
|
|
471
|
-
this[kMonitorId] = setTimeout(() => measureRoundTripTime(
|
|
503
|
+
const heartbeatFrequencyMS = monitor.options.heartbeatFrequencyMS;
|
|
504
|
+
this[kMonitorId] = setTimeout(() => this.measureRoundTripTime(), heartbeatFrequencyMS);
|
|
472
505
|
}
|
|
473
506
|
|
|
474
507
|
get roundTripTime(): number {
|
|
475
|
-
return this
|
|
508
|
+
return this.monitor.roundTripTime;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
get minRoundTripTime(): number {
|
|
512
|
+
return this.monitor.minRoundTripTime;
|
|
476
513
|
}
|
|
477
514
|
|
|
478
515
|
close(): void {
|
|
@@ -482,59 +519,60 @@ export class RTTPinger {
|
|
|
482
519
|
this.connection?.destroy();
|
|
483
520
|
this.connection = undefined;
|
|
484
521
|
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function measureRoundTripTime(rttPinger: RTTPinger, options: RTTPingerOptions) {
|
|
488
|
-
const start = now();
|
|
489
|
-
options.cancellationToken = rttPinger[kCancellationToken];
|
|
490
|
-
const heartbeatFrequencyMS = options.heartbeatFrequencyMS;
|
|
491
|
-
|
|
492
|
-
if (rttPinger.closed) {
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
522
|
|
|
496
|
-
|
|
497
|
-
if (
|
|
523
|
+
private measureAndReschedule(start?: number, conn?: Connection) {
|
|
524
|
+
if (start == null) {
|
|
525
|
+
start = now();
|
|
526
|
+
}
|
|
527
|
+
if (this.closed) {
|
|
498
528
|
conn?.destroy();
|
|
499
529
|
return;
|
|
500
530
|
}
|
|
501
531
|
|
|
502
|
-
if (
|
|
503
|
-
|
|
532
|
+
if (this.connection == null) {
|
|
533
|
+
this.connection = conn;
|
|
504
534
|
}
|
|
505
535
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
() => measureRoundTripTime(
|
|
509
|
-
heartbeatFrequencyMS
|
|
536
|
+
this.latestRtt = calculateDurationInMs(start);
|
|
537
|
+
this[kMonitorId] = setTimeout(
|
|
538
|
+
() => this.measureRoundTripTime(),
|
|
539
|
+
this.monitor.options.heartbeatFrequencyMS
|
|
510
540
|
);
|
|
511
541
|
}
|
|
512
542
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
543
|
+
private measureRoundTripTime() {
|
|
544
|
+
const start = now();
|
|
545
|
+
|
|
546
|
+
if (this.closed) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const connection = this.connection;
|
|
551
|
+
if (connection == null) {
|
|
552
|
+
// eslint-disable-next-line github/no-then
|
|
553
|
+
connect(this.monitor.connectOptions).then(
|
|
554
|
+
connection => {
|
|
555
|
+
this.measureAndReschedule(start, connection);
|
|
556
|
+
},
|
|
557
|
+
() => {
|
|
558
|
+
this.connection = undefined;
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const commandName =
|
|
565
|
+
connection.serverApi?.version || connection.helloOk ? 'hello' : LEGACY_HELLO_COMMAND;
|
|
566
|
+
// eslint-disable-next-line github/no-then
|
|
567
|
+
connection.command(ns('admin.$cmd'), { [commandName]: 1 }, undefined).then(
|
|
568
|
+
() => this.measureAndReschedule(),
|
|
519
569
|
() => {
|
|
520
|
-
|
|
521
|
-
|
|
570
|
+
this.connection?.destroy();
|
|
571
|
+
this.connection = undefined;
|
|
572
|
+
return;
|
|
522
573
|
}
|
|
523
574
|
);
|
|
524
|
-
return;
|
|
525
575
|
}
|
|
526
|
-
|
|
527
|
-
const commandName =
|
|
528
|
-
connection.serverApi?.version || connection.helloOk ? 'hello' : LEGACY_HELLO_COMMAND;
|
|
529
|
-
connection.command(ns('admin.$cmd'), { [commandName]: 1 }, undefined).then(
|
|
530
|
-
() => measureAndReschedule(),
|
|
531
|
-
() => {
|
|
532
|
-
rttPinger.connection?.destroy();
|
|
533
|
-
rttPinger.connection = undefined;
|
|
534
|
-
rttPinger[kRoundTripTime] = 0;
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
);
|
|
538
576
|
}
|
|
539
577
|
|
|
540
578
|
/**
|
|
@@ -662,3 +700,82 @@ export class MonitorInterval {
|
|
|
662
700
|
});
|
|
663
701
|
};
|
|
664
702
|
}
|
|
703
|
+
|
|
704
|
+
/** @internal
|
|
705
|
+
* This class implements the RTT sampling logic specified for [CSOT](https://github.com/mongodb/specifications/blob/bbb335e60cd7ea1e0f7cd9a9443cb95fc9d3b64d/source/client-side-operations-timeout/client-side-operations-timeout.md#drivers-use-minimum-rtt-to-short-circuit-operations)
|
|
706
|
+
*
|
|
707
|
+
* This is implemented as a [circular buffer](https://en.wikipedia.org/wiki/Circular_buffer) keeping
|
|
708
|
+
* the most recent `windowSize` samples
|
|
709
|
+
* */
|
|
710
|
+
export class RTTSampler {
|
|
711
|
+
/** Index of the next slot to be overwritten */
|
|
712
|
+
private writeIndex: number;
|
|
713
|
+
private length: number;
|
|
714
|
+
private rttSamples: Float64Array;
|
|
715
|
+
|
|
716
|
+
constructor(windowSize = 10) {
|
|
717
|
+
this.rttSamples = new Float64Array(windowSize);
|
|
718
|
+
this.length = 0;
|
|
719
|
+
this.writeIndex = 0;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Adds an rtt sample to the end of the circular buffer
|
|
724
|
+
* When `windowSize` samples have been collected, `addSample` overwrites the least recently added
|
|
725
|
+
* sample
|
|
726
|
+
*/
|
|
727
|
+
addSample(sample: number) {
|
|
728
|
+
this.rttSamples[this.writeIndex++] = sample;
|
|
729
|
+
if (this.length < this.rttSamples.length) {
|
|
730
|
+
this.length++;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
this.writeIndex %= this.rttSamples.length;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* When \< 2 samples have been collected, returns 0
|
|
738
|
+
* Otherwise computes the minimum value samples contained in the buffer
|
|
739
|
+
*/
|
|
740
|
+
min(): number {
|
|
741
|
+
if (this.length < 2) return 0;
|
|
742
|
+
let min = this.rttSamples[0];
|
|
743
|
+
for (let i = 1; i < this.length; i++) {
|
|
744
|
+
if (this.rttSamples[i] < min) min = this.rttSamples[i];
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return min;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Returns mean of samples contained in the buffer
|
|
752
|
+
*/
|
|
753
|
+
average(): number {
|
|
754
|
+
if (this.length === 0) return 0;
|
|
755
|
+
let sum = 0;
|
|
756
|
+
for (let i = 0; i < this.length; i++) {
|
|
757
|
+
sum += this.rttSamples[i];
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return sum / this.length;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Returns most recently inserted element in the buffer
|
|
765
|
+
* Returns null if the buffer is empty
|
|
766
|
+
* */
|
|
767
|
+
get last(): number | null {
|
|
768
|
+
if (this.length === 0) return null;
|
|
769
|
+
return this.rttSamples[this.writeIndex === 0 ? this.length - 1 : this.writeIndex - 1];
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Clear the buffer
|
|
774
|
+
* NOTE: this does not overwrite the data held in the internal array, just the pointers into
|
|
775
|
+
* this array
|
|
776
|
+
*/
|
|
777
|
+
clear() {
|
|
778
|
+
this.length = 0;
|
|
779
|
+
this.writeIndex = 0;
|
|
780
|
+
}
|
|
781
|
+
}
|
package/src/sdam/server.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type ConnectionPoolOptions
|
|
8
8
|
} from '../cmap/connection_pool';
|
|
9
9
|
import { PoolClearedError } from '../cmap/errors';
|
|
10
|
+
import { type MongoDBResponseConstructor } from '../cmap/wire_protocol/responses';
|
|
10
11
|
import {
|
|
11
12
|
APM_EVENTS,
|
|
12
13
|
CLOSED,
|
|
@@ -175,7 +176,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
175
176
|
this.emit(
|
|
176
177
|
Server.DESCRIPTION_RECEIVED,
|
|
177
178
|
new ServerDescription(this.description.hostAddress, event.reply, {
|
|
178
|
-
roundTripTime:
|
|
179
|
+
roundTripTime: this.monitor?.roundTripTime,
|
|
180
|
+
minRoundTripTime: this.monitor?.minRoundTripTime
|
|
179
181
|
})
|
|
180
182
|
);
|
|
181
183
|
|
|
@@ -261,11 +263,25 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
261
263
|
}
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
266
|
+
public async command<T extends MongoDBResponseConstructor>(
|
|
267
|
+
ns: MongoDBNamespace,
|
|
268
|
+
command: Document,
|
|
269
|
+
options: CommandOptions | undefined,
|
|
270
|
+
responseType: T | undefined
|
|
271
|
+
): Promise<typeof responseType extends undefined ? Document : InstanceType<T>>;
|
|
272
|
+
|
|
273
|
+
public async command(
|
|
274
|
+
ns: MongoDBNamespace,
|
|
275
|
+
command: Document,
|
|
276
|
+
options?: CommandOptions
|
|
277
|
+
): Promise<Document>;
|
|
278
|
+
|
|
279
|
+
public async command(
|
|
280
|
+
ns: MongoDBNamespace,
|
|
281
|
+
cmd: Document,
|
|
282
|
+
options: CommandOptions,
|
|
283
|
+
responseType?: MongoDBResponseConstructor
|
|
284
|
+
): Promise<Document> {
|
|
269
285
|
if (ns.db == null || typeof ns === 'string') {
|
|
270
286
|
throw new MongoInvalidArgumentError('Namespace must not be a string');
|
|
271
287
|
}
|
|
@@ -307,7 +323,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
307
323
|
|
|
308
324
|
try {
|
|
309
325
|
try {
|
|
310
|
-
return await conn.command(ns, cmd, finalOptions);
|
|
326
|
+
return await conn.command(ns, cmd, finalOptions, responseType);
|
|
311
327
|
} catch (commandError) {
|
|
312
328
|
throw this.decorateCommandError(conn, cmd, finalOptions, commandError);
|
|
313
329
|
}
|
|
@@ -318,7 +334,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
318
334
|
) {
|
|
319
335
|
await this.pool.reauthenticate(conn);
|
|
320
336
|
try {
|
|
321
|
-
return await conn.command(ns, cmd, finalOptions);
|
|
337
|
+
return await conn.command(ns, cmd, finalOptions, responseType);
|
|
322
338
|
} catch (commandError) {
|
|
323
339
|
throw this.decorateCommandError(conn, cmd, finalOptions, commandError);
|
|
324
340
|
}
|
|
@@ -357,7 +373,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
357
373
|
// clear for the specific service id.
|
|
358
374
|
if (!this.loadBalanced) {
|
|
359
375
|
error.addErrorLabel(MongoErrorLabel.ResetPool);
|
|
360
|
-
markServerUnknown(this, error
|
|
376
|
+
markServerUnknown(this, error);
|
|
361
377
|
} else if (connection) {
|
|
362
378
|
this.pool.clear({ serviceId: connection.serviceId });
|
|
363
379
|
}
|
|
@@ -373,7 +389,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
373
389
|
if (shouldClearPool) {
|
|
374
390
|
error.addErrorLabel(MongoErrorLabel.ResetPool);
|
|
375
391
|
}
|
|
376
|
-
markServerUnknown(this, error
|
|
392
|
+
markServerUnknown(this, error);
|
|
377
393
|
process.nextTick(() => this.requestCheck());
|
|
378
394
|
}
|
|
379
395
|
}
|
|
@@ -467,16 +483,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
467
483
|
}
|
|
468
484
|
}
|
|
469
485
|
|
|
470
|
-
function
|
|
471
|
-
if (oldRtt === -1) {
|
|
472
|
-
return duration;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const alpha = 0.2;
|
|
476
|
-
return alpha * duration + (1 - alpha) * oldRtt;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function markServerUnknown(server: Server, error?: MongoServerError) {
|
|
486
|
+
function markServerUnknown(server: Server, error?: MongoError) {
|
|
480
487
|
// Load balancer servers can never be marked unknown.
|
|
481
488
|
if (server.loadBalanced) {
|
|
482
489
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Document, Long, type ObjectId } from '../bson';
|
|
2
|
-
import { type MongoError, MongoRuntimeError
|
|
2
|
+
import { type MongoError, MongoRuntimeError } from '../error';
|
|
3
3
|
import { arrayStrictEqual, compareObjectId, errorStrictEqual, HostAddress, now } from '../utils';
|
|
4
4
|
import type { ClusterTime } from './common';
|
|
5
5
|
import { ServerType } from './common';
|
|
@@ -31,10 +31,12 @@ export type TagSet = { [key: string]: string };
|
|
|
31
31
|
/** @internal */
|
|
32
32
|
export interface ServerDescriptionOptions {
|
|
33
33
|
/** An Error used for better reporting debugging */
|
|
34
|
-
error?:
|
|
34
|
+
error?: MongoError;
|
|
35
35
|
|
|
36
|
-
/** The round trip time to ping this server (in ms) */
|
|
36
|
+
/** The average round trip time to ping this server (in ms) */
|
|
37
37
|
roundTripTime?: number;
|
|
38
|
+
/** The minimum round trip time to ping this server over the past 10 samples(in ms) */
|
|
39
|
+
minRoundTripTime?: number;
|
|
38
40
|
|
|
39
41
|
/** If the client is in load balancing mode. */
|
|
40
42
|
loadBalanced?: boolean;
|
|
@@ -58,6 +60,8 @@ export class ServerDescription {
|
|
|
58
60
|
minWireVersion: number;
|
|
59
61
|
maxWireVersion: number;
|
|
60
62
|
roundTripTime: number;
|
|
63
|
+
/** The minimum measurement of the last 10 measurements of roundTripTime that have been collected */
|
|
64
|
+
minRoundTripTime: number;
|
|
61
65
|
lastUpdateTime: number;
|
|
62
66
|
lastWriteDate: number;
|
|
63
67
|
me: string | null;
|
|
@@ -98,6 +102,7 @@ export class ServerDescription {
|
|
|
98
102
|
this.minWireVersion = hello?.minWireVersion ?? 0;
|
|
99
103
|
this.maxWireVersion = hello?.maxWireVersion ?? 0;
|
|
100
104
|
this.roundTripTime = options?.roundTripTime ?? -1;
|
|
105
|
+
this.minRoundTripTime = options?.minRoundTripTime ?? 0;
|
|
101
106
|
this.lastUpdateTime = now();
|
|
102
107
|
this.lastWriteDate = hello?.lastWrite?.lastWriteDate ?? 0;
|
|
103
108
|
this.error = options.error ?? null;
|
|
@@ -223,9 +223,8 @@ function latencyWindowReducer(
|
|
|
223
223
|
servers: ServerDescription[]
|
|
224
224
|
): ServerDescription[] {
|
|
225
225
|
const low = servers.reduce(
|
|
226
|
-
(min: number, server: ServerDescription) =>
|
|
227
|
-
|
|
228
|
-
-1
|
|
226
|
+
(min: number, server: ServerDescription) => Math.min(server.roundTripTime, min),
|
|
227
|
+
Infinity
|
|
229
228
|
);
|
|
230
229
|
|
|
231
230
|
const high = low + topologyDescription.localThresholdMS;
|
package/src/sdam/srv_polling.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { clearTimeout, setTimeout } from 'timers';
|
|
|
3
3
|
|
|
4
4
|
import { MongoRuntimeError } from '../error';
|
|
5
5
|
import { TypedEventEmitter } from '../mongo_types';
|
|
6
|
-
import { HostAddress, matchesParentDomain } from '../utils';
|
|
6
|
+
import { HostAddress, matchesParentDomain, squashError } from '../utils';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @internal
|
|
@@ -95,7 +95,8 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
this._timeout = setTimeout(() => {
|
|
98
|
-
|
|
98
|
+
// eslint-disable-next-line github/no-then
|
|
99
|
+
this._poll().then(undefined, squashError);
|
|
99
100
|
}, this.intervalMS);
|
|
100
101
|
}
|
|
101
102
|
|