mongodb 6.10.0 → 6.11.0-dev.20241128.sha.4842cd8a
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 +2 -0
- package/lib/admin.js +3 -2
- package/lib/admin.js.map +1 -1
- package/lib/beta.d.ts +562 -45
- package/lib/bulk/common.js +4 -4
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +111 -51
- package/lib/change_stream.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +8 -5
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +48 -18
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/state_machine.js +43 -29
- package/lib/client-side-encryption/state_machine.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js +5 -2
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +1 -1
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js +38 -0
- package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js.map +1 -0
- package/lib/cmap/auth/mongodb_oidc.js +2 -0
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
- package/lib/cmap/connect.js +13 -1
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +75 -17
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +14 -12
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/wire_protocol/on_data.js +5 -1
- package/lib/cmap/wire_protocol/on_data.js.map +1 -1
- package/lib/cmap/wire_protocol/responses.js +30 -0
- package/lib/cmap/wire_protocol/responses.js.map +1 -1
- package/lib/collection.js +62 -3
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +2 -0
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +221 -38
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +29 -7
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/change_stream_cursor.js +2 -2
- package/lib/cursor/change_stream_cursor.js.map +1 -1
- package/lib/cursor/client_bulk_write_cursor.js +1 -1
- package/lib/cursor/client_bulk_write_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +18 -8
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/cursor/list_collections_cursor.js +1 -1
- package/lib/cursor/list_collections_cursor.js.map +1 -1
- package/lib/cursor/list_indexes_cursor.js +1 -1
- package/lib/cursor/list_indexes_cursor.js.map +1 -1
- package/lib/cursor/run_command_cursor.js +6 -4
- package/lib/cursor/run_command_cursor.js.map +1 -1
- package/lib/db.js +63 -3
- package/lib/db.js.map +1 -1
- package/lib/error.js +34 -9
- package/lib/error.js.map +1 -1
- package/lib/explain.js +57 -1
- package/lib/explain.js.map +1 -1
- package/lib/gridfs/download.js +31 -3
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js +49 -14
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +80 -22
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +9 -5
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +70 -1
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/aggregate.js +2 -2
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/bulk_write.js +7 -2
- package/lib/operations/bulk_write.js.map +1 -1
- package/lib/operations/client_bulk_write/client_bulk_write.js +3 -3
- package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -1
- package/lib/operations/client_bulk_write/executor.js +14 -3
- package/lib/operations/client_bulk_write/executor.js.map +1 -1
- package/lib/operations/command.js +5 -2
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/count.js +2 -2
- package/lib/operations/count.js.map +1 -1
- package/lib/operations/create_collection.js +8 -7
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/delete.js +6 -6
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/distinct.js +2 -2
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/drop.js +8 -8
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/estimated_document_count.js +2 -2
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/execute_operation.js +16 -10
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +6 -3
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/find_and_modify.js +2 -2
- package/lib/operations/find_and_modify.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 +6 -6
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +6 -6
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/kill_cursors.js +5 -2
- package/lib/operations/kill_cursors.js.map +1 -1
- package/lib/operations/list_collections.js +2 -2
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js +2 -2
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/profiling_level.js +2 -2
- package/lib/operations/profiling_level.js.map +1 -1
- package/lib/operations/remove_user.js +2 -2
- package/lib/operations/remove_user.js.map +1 -1
- package/lib/operations/rename.js +2 -2
- package/lib/operations/rename.js.map +1 -1
- package/lib/operations/run_command.js +6 -4
- package/lib/operations/run_command.js.map +1 -1
- package/lib/operations/search_indexes/create.js +5 -2
- package/lib/operations/search_indexes/create.js.map +1 -1
- package/lib/operations/search_indexes/drop.js +2 -2
- package/lib/operations/search_indexes/drop.js.map +1 -1
- package/lib/operations/search_indexes/update.js +2 -2
- package/lib/operations/search_indexes/update.js.map +1 -1
- package/lib/operations/set_profiling_level.js +2 -2
- package/lib/operations/set_profiling_level.js.map +1 -1
- package/lib/operations/stats.js +2 -2
- package/lib/operations/stats.js.map +1 -1
- package/lib/operations/update.js +8 -8
- package/lib/operations/update.js.map +1 -1
- package/lib/operations/validate_collection.js +2 -2
- package/lib/operations/validate_collection.js.map +1 -1
- package/lib/read_concern.js +1 -1
- package/lib/sdam/common.js +0 -7
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/server.js +4 -1
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +4 -2
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/server_selection.js +5 -2
- package/lib/sdam/server_selection.js.map +1 -1
- package/lib/sdam/topology.js +38 -15
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +157 -98
- package/lib/sessions.js.map +1 -1
- package/lib/timeout.js +231 -16
- package/lib/timeout.js.map +1 -1
- package/lib/utils.js +36 -19
- package/lib/utils.js.map +1 -1
- package/lib/write_concern.js.map +1 -1
- package/mongodb.d.ts +562 -45
- package/package.json +18 -17
- package/src/admin.ts +6 -2
- package/src/bulk/common.ts +17 -5
- package/src/change_stream.ts +127 -52
- package/src/client-side-encryption/auto_encrypter.ts +12 -5
- package/src/client-side-encryption/client_encryption.ts +103 -20
- package/src/client-side-encryption/state_machine.ts +66 -32
- package/src/cmap/auth/mongo_credentials.ts +6 -3
- package/src/cmap/auth/mongodb_aws.ts +1 -1
- package/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts +38 -0
- package/src/cmap/auth/mongodb_oidc.ts +3 -1
- package/src/cmap/connect.ts +18 -1
- package/src/cmap/connection.ts +105 -17
- package/src/cmap/connection_pool.ts +15 -17
- package/src/cmap/handshake/client_metadata.ts +1 -1
- package/src/cmap/wire_protocol/on_data.ts +11 -1
- package/src/cmap/wire_protocol/responses.ts +35 -1
- package/src/collection.ts +81 -9
- package/src/connection_string.ts +2 -0
- package/src/cursor/abstract_cursor.ts +287 -39
- package/src/cursor/aggregation_cursor.ts +54 -8
- package/src/cursor/change_stream_cursor.ts +6 -2
- package/src/cursor/client_bulk_write_cursor.ts +6 -2
- package/src/cursor/find_cursor.ts +40 -9
- package/src/cursor/list_collections_cursor.ts +1 -1
- package/src/cursor/list_indexes_cursor.ts +1 -1
- package/src/cursor/run_command_cursor.ts +50 -5
- package/src/db.ts +75 -7
- package/src/error.ts +33 -8
- package/src/explain.ts +85 -0
- package/src/gridfs/download.ts +43 -4
- package/src/gridfs/index.ts +64 -16
- package/src/gridfs/upload.ts +153 -45
- package/src/index.ts +27 -5
- package/src/mongo_client.ts +76 -4
- package/src/operations/aggregate.ts +10 -2
- package/src/operations/bulk_write.ts +9 -2
- package/src/operations/client_bulk_write/client_bulk_write.ts +11 -3
- package/src/operations/client_bulk_write/executor.ts +15 -3
- package/src/operations/command.ts +18 -8
- package/src/operations/count.ts +10 -3
- package/src/operations/create_collection.ts +14 -7
- package/src/operations/delete.ts +15 -6
- package/src/operations/distinct.ts +7 -2
- package/src/operations/drop.ts +18 -8
- package/src/operations/estimated_document_count.ts +7 -2
- package/src/operations/execute_operation.ts +22 -13
- package/src/operations/find.ts +17 -5
- package/src/operations/find_and_modify.ts +7 -2
- package/src/operations/get_more.ts +4 -1
- package/src/operations/indexes.ts +20 -7
- package/src/operations/insert.ts +13 -6
- package/src/operations/kill_cursors.ts +10 -2
- package/src/operations/list_collections.ts +10 -1
- package/src/operations/list_databases.ts +9 -2
- package/src/operations/operation.ts +16 -2
- package/src/operations/profiling_level.ts +7 -2
- package/src/operations/remove_user.ts +7 -2
- package/src/operations/rename.ts +7 -2
- package/src/operations/run_command.ts +23 -4
- package/src/operations/search_indexes/create.ts +10 -2
- package/src/operations/search_indexes/drop.ts +7 -2
- package/src/operations/search_indexes/update.ts +7 -2
- package/src/operations/set_profiling_level.ts +4 -2
- package/src/operations/stats.ts +7 -2
- package/src/operations/update.ts +16 -8
- package/src/operations/validate_collection.ts +7 -2
- package/src/read_concern.ts +1 -1
- package/src/sdam/common.ts +0 -11
- package/src/sdam/server.ts +14 -4
- package/src/sdam/server_description.ts +6 -2
- package/src/sdam/server_selection.ts +5 -2
- package/src/sdam/topology.ts +43 -27
- package/src/sessions.ts +206 -120
- package/src/timeout.ts +327 -23
- package/src/transactions.ts +1 -1
- package/src/utils.ts +47 -30
- package/src/write_concern.ts +6 -3
package/src/cmap/connection.ts
CHANGED
|
@@ -21,9 +21,11 @@ import {
|
|
|
21
21
|
} from '../constants';
|
|
22
22
|
import {
|
|
23
23
|
MongoCompatibilityError,
|
|
24
|
+
MONGODB_ERROR_CODES,
|
|
24
25
|
MongoMissingDependencyError,
|
|
25
26
|
MongoNetworkError,
|
|
26
27
|
MongoNetworkTimeoutError,
|
|
28
|
+
MongoOperationTimeoutError,
|
|
27
29
|
MongoParseError,
|
|
28
30
|
MongoServerError,
|
|
29
31
|
MongoUnexpectedServerResponseError
|
|
@@ -35,6 +37,7 @@ import { type CancellationToken, TypedEventEmitter } from '../mongo_types';
|
|
|
35
37
|
import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
|
|
36
38
|
import { ServerType } from '../sdam/common';
|
|
37
39
|
import { applySession, type ClientSession, updateSessionFromResponse } from '../sessions';
|
|
40
|
+
import { type TimeoutContext, TimeoutError } from '../timeout';
|
|
38
41
|
import {
|
|
39
42
|
BufferPool,
|
|
40
43
|
calculateDurationInMs,
|
|
@@ -88,6 +91,7 @@ export interface CommandOptions extends BSONSerializeOptions {
|
|
|
88
91
|
documentsReturnedIn?: string;
|
|
89
92
|
noResponse?: boolean;
|
|
90
93
|
omitReadPreference?: boolean;
|
|
94
|
+
omitMaxTimeMS?: boolean;
|
|
91
95
|
|
|
92
96
|
// TODO(NODE-2802): Currently the CommandOptions take a property willRetryWrite which is a hint
|
|
93
97
|
// from executeOperation that the txnNum should be applied to this command.
|
|
@@ -99,6 +103,9 @@ export interface CommandOptions extends BSONSerializeOptions {
|
|
|
99
103
|
writeConcern?: WriteConcern;
|
|
100
104
|
|
|
101
105
|
directConnection?: boolean;
|
|
106
|
+
|
|
107
|
+
/** @internal */
|
|
108
|
+
timeoutContext?: TimeoutContext;
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
/** @public */
|
|
@@ -420,6 +427,8 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
420
427
|
...options
|
|
421
428
|
};
|
|
422
429
|
|
|
430
|
+
options.timeoutContext?.addMaxTimeMSToCommand(cmd, options);
|
|
431
|
+
|
|
423
432
|
const message = this.supportsOpMsg
|
|
424
433
|
? new OpMsgRequest(db, cmd, commandOptions)
|
|
425
434
|
: new OpQueryRequest(db, cmd, commandOptions);
|
|
@@ -434,16 +443,17 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
434
443
|
): AsyncGenerator<MongoDBResponse> {
|
|
435
444
|
this.throwIfAborted();
|
|
436
445
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
this.
|
|
441
|
-
|
|
446
|
+
const timeout =
|
|
447
|
+
options.socketTimeoutMS ??
|
|
448
|
+
options?.timeoutContext?.getSocketTimeoutMS() ??
|
|
449
|
+
this.socketTimeoutMS;
|
|
450
|
+
this.socket.setTimeout(timeout);
|
|
442
451
|
|
|
443
452
|
try {
|
|
444
453
|
await this.writeCommand(message, {
|
|
445
454
|
agreedCompressor: this.description.compressor ?? 'none',
|
|
446
|
-
zlibCompressionLevel: this.description.zlibCompressionLevel
|
|
455
|
+
zlibCompressionLevel: this.description.zlibCompressionLevel,
|
|
456
|
+
timeoutContext: options.timeoutContext
|
|
447
457
|
});
|
|
448
458
|
|
|
449
459
|
if (options.noResponse || message.moreToCome) {
|
|
@@ -453,7 +463,17 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
453
463
|
|
|
454
464
|
this.throwIfAborted();
|
|
455
465
|
|
|
456
|
-
|
|
466
|
+
if (
|
|
467
|
+
options.timeoutContext?.csotEnabled() &&
|
|
468
|
+
options.timeoutContext.minRoundTripTime != null &&
|
|
469
|
+
options.timeoutContext.remainingTimeMS < options.timeoutContext.minRoundTripTime
|
|
470
|
+
) {
|
|
471
|
+
throw new MongoOperationTimeoutError(
|
|
472
|
+
'Server roundtrip time is greater than the time remaining'
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
for await (const response of this.readMany({ timeoutContext: options.timeoutContext })) {
|
|
457
477
|
this.socket.setTimeout(0);
|
|
458
478
|
const bson = response.parse();
|
|
459
479
|
|
|
@@ -462,11 +482,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
462
482
|
yield document;
|
|
463
483
|
this.throwIfAborted();
|
|
464
484
|
|
|
465
|
-
|
|
466
|
-
this.socket.setTimeout(options.socketTimeoutMS);
|
|
467
|
-
} else if (this.socketTimeoutMS !== 0) {
|
|
468
|
-
this.socket.setTimeout(this.socketTimeoutMS);
|
|
469
|
-
}
|
|
485
|
+
this.socket.setTimeout(timeout);
|
|
470
486
|
}
|
|
471
487
|
} finally {
|
|
472
488
|
this.socket.setTimeout(0);
|
|
@@ -480,7 +496,6 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
480
496
|
responseType?: MongoDBResponseConstructor
|
|
481
497
|
) {
|
|
482
498
|
const message = this.prepareCommand(ns.db, command, options);
|
|
483
|
-
|
|
484
499
|
let started = 0;
|
|
485
500
|
if (this.shouldEmitAndLogCommand) {
|
|
486
501
|
started = now();
|
|
@@ -522,6 +537,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
522
537
|
}
|
|
523
538
|
|
|
524
539
|
if (document.ok === 0) {
|
|
540
|
+
if (options.timeoutContext?.csotEnabled() && document.isMaxTimeExpiredError) {
|
|
541
|
+
throw new MongoOperationTimeoutError('Server reported a timeout error', {
|
|
542
|
+
cause: new MongoServerError((object ??= document.toObject(bsonOptions)))
|
|
543
|
+
});
|
|
544
|
+
}
|
|
525
545
|
throw new MongoServerError((object ??= document.toObject(bsonOptions)));
|
|
526
546
|
}
|
|
527
547
|
|
|
@@ -595,6 +615,28 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
595
615
|
): Promise<Document> {
|
|
596
616
|
this.throwIfAborted();
|
|
597
617
|
for await (const document of this.sendCommand(ns, command, options, responseType)) {
|
|
618
|
+
if (options.timeoutContext?.csotEnabled()) {
|
|
619
|
+
if (MongoDBResponse.is(document)) {
|
|
620
|
+
if (document.isMaxTimeExpiredError) {
|
|
621
|
+
throw new MongoOperationTimeoutError('Server reported a timeout error', {
|
|
622
|
+
cause: new MongoServerError(document.toObject())
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
} else {
|
|
626
|
+
if (
|
|
627
|
+
(Array.isArray(document?.writeErrors) &&
|
|
628
|
+
document.writeErrors.some(
|
|
629
|
+
error => error?.code === MONGODB_ERROR_CODES.MaxTimeMSExpired
|
|
630
|
+
)) ||
|
|
631
|
+
document?.writeConcernError?.code === MONGODB_ERROR_CODES.MaxTimeMSExpired
|
|
632
|
+
) {
|
|
633
|
+
throw new MongoOperationTimeoutError('Server reported a timeout error', {
|
|
634
|
+
cause: new MongoServerError(document)
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
598
640
|
return document;
|
|
599
641
|
}
|
|
600
642
|
throw new MongoUnexpectedServerResponseError('Unable to get response from server');
|
|
@@ -630,7 +672,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
630
672
|
*/
|
|
631
673
|
private async writeCommand(
|
|
632
674
|
command: WriteProtocolMessageType,
|
|
633
|
-
options: {
|
|
675
|
+
options: {
|
|
676
|
+
agreedCompressor?: CompressorName;
|
|
677
|
+
zlibCompressionLevel?: number;
|
|
678
|
+
timeoutContext?: TimeoutContext;
|
|
679
|
+
}
|
|
634
680
|
): Promise<void> {
|
|
635
681
|
const finalCommand =
|
|
636
682
|
options.agreedCompressor === 'none' || !OpCompressedRequest.canCompress(command)
|
|
@@ -642,8 +688,36 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
642
688
|
|
|
643
689
|
const buffer = Buffer.concat(await finalCommand.toBin());
|
|
644
690
|
|
|
691
|
+
if (options.timeoutContext?.csotEnabled()) {
|
|
692
|
+
if (
|
|
693
|
+
options.timeoutContext.minRoundTripTime != null &&
|
|
694
|
+
options.timeoutContext.remainingTimeMS < options.timeoutContext.minRoundTripTime
|
|
695
|
+
) {
|
|
696
|
+
throw new MongoOperationTimeoutError(
|
|
697
|
+
'Server roundtrip time is greater than the time remaining'
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
645
702
|
if (this.socket.write(buffer)) return;
|
|
646
|
-
|
|
703
|
+
|
|
704
|
+
const drainEvent = once<void>(this.socket, 'drain');
|
|
705
|
+
const timeout = options?.timeoutContext?.timeoutForSocketWrite;
|
|
706
|
+
if (timeout) {
|
|
707
|
+
try {
|
|
708
|
+
return await Promise.race([drainEvent, timeout]);
|
|
709
|
+
} catch (error) {
|
|
710
|
+
let err = error;
|
|
711
|
+
if (TimeoutError.is(error)) {
|
|
712
|
+
err = new MongoOperationTimeoutError('Timed out at socket write');
|
|
713
|
+
this.cleanup(err);
|
|
714
|
+
}
|
|
715
|
+
throw error;
|
|
716
|
+
} finally {
|
|
717
|
+
timeout.clear();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return await drainEvent;
|
|
647
721
|
}
|
|
648
722
|
|
|
649
723
|
/**
|
|
@@ -655,10 +729,13 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
655
729
|
*
|
|
656
730
|
* Note that `for-await` loops call `return` automatically when the loop is exited.
|
|
657
731
|
*/
|
|
658
|
-
private async *readMany(
|
|
732
|
+
private async *readMany(options: {
|
|
733
|
+
timeoutContext?: TimeoutContext;
|
|
734
|
+
}): AsyncGenerator<OpMsgResponse | OpReply> {
|
|
659
735
|
try {
|
|
660
|
-
this.dataEvents = onData(this.messageStream);
|
|
736
|
+
this.dataEvents = onData(this.messageStream, options);
|
|
661
737
|
this.messageStream.resume();
|
|
738
|
+
|
|
662
739
|
for await (const message of this.dataEvents) {
|
|
663
740
|
const response = await decompressResponse(message);
|
|
664
741
|
yield response;
|
|
@@ -667,6 +744,17 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
667
744
|
return;
|
|
668
745
|
}
|
|
669
746
|
}
|
|
747
|
+
} catch (readError) {
|
|
748
|
+
const err = readError;
|
|
749
|
+
if (TimeoutError.is(readError)) {
|
|
750
|
+
const error = new MongoOperationTimeoutError(
|
|
751
|
+
`Timed out during socket read (${readError.duration}ms)`
|
|
752
|
+
);
|
|
753
|
+
this.dataEvents = null;
|
|
754
|
+
this.onError(error);
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
throw err;
|
|
670
758
|
} finally {
|
|
671
759
|
this.dataEvents = null;
|
|
672
760
|
this.messageStream.pause();
|
|
@@ -21,12 +21,13 @@ import {
|
|
|
21
21
|
MongoInvalidArgumentError,
|
|
22
22
|
MongoMissingCredentialsError,
|
|
23
23
|
MongoNetworkError,
|
|
24
|
+
MongoOperationTimeoutError,
|
|
24
25
|
MongoRuntimeError,
|
|
25
26
|
MongoServerError
|
|
26
27
|
} from '../error';
|
|
27
28
|
import { CancellationToken, TypedEventEmitter } from '../mongo_types';
|
|
28
29
|
import type { Server } from '../sdam/server';
|
|
29
|
-
import {
|
|
30
|
+
import { type TimeoutContext, TimeoutError } from '../timeout';
|
|
30
31
|
import { type Callback, List, makeCounter, now, promiseWithResolvers } from '../utils';
|
|
31
32
|
import { connect } from './connect';
|
|
32
33
|
import { Connection, type ConnectionEvents, type ConnectionOptions } from './connection';
|
|
@@ -102,7 +103,6 @@ export interface ConnectionPoolOptions extends Omit<ConnectionOptions, 'id' | 'g
|
|
|
102
103
|
export interface WaitQueueMember {
|
|
103
104
|
resolve: (conn: Connection) => void;
|
|
104
105
|
reject: (err: AnyError) => void;
|
|
105
|
-
timeout: Timeout;
|
|
106
106
|
[kCancelled]?: boolean;
|
|
107
107
|
checkoutTime: number;
|
|
108
108
|
}
|
|
@@ -355,23 +355,20 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
355
355
|
* will be held by the pool. This means that if a connection is checked out it MUST be checked back in or
|
|
356
356
|
* explicitly destroyed by the new owner.
|
|
357
357
|
*/
|
|
358
|
-
async checkOut(): Promise<Connection> {
|
|
358
|
+
async checkOut(options: { timeoutContext: TimeoutContext }): Promise<Connection> {
|
|
359
359
|
const checkoutTime = now();
|
|
360
360
|
this.emitAndLog(
|
|
361
361
|
ConnectionPool.CONNECTION_CHECK_OUT_STARTED,
|
|
362
362
|
new ConnectionCheckOutStartedEvent(this)
|
|
363
363
|
);
|
|
364
364
|
|
|
365
|
-
const waitQueueTimeoutMS = this.options.waitQueueTimeoutMS;
|
|
366
|
-
|
|
367
365
|
const { promise, resolve, reject } = promiseWithResolvers<Connection>();
|
|
368
366
|
|
|
369
|
-
const timeout =
|
|
367
|
+
const timeout = options.timeoutContext.connectionCheckoutTimeout;
|
|
370
368
|
|
|
371
369
|
const waitQueueMember: WaitQueueMember = {
|
|
372
370
|
resolve,
|
|
373
371
|
reject,
|
|
374
|
-
timeout,
|
|
375
372
|
checkoutTime
|
|
376
373
|
};
|
|
377
374
|
|
|
@@ -379,13 +376,13 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
379
376
|
process.nextTick(() => this.processWaitQueue());
|
|
380
377
|
|
|
381
378
|
try {
|
|
382
|
-
|
|
379
|
+
timeout?.throwIfExpired();
|
|
380
|
+
return await (timeout ? Promise.race([promise, timeout]) : promise);
|
|
383
381
|
} catch (error) {
|
|
384
382
|
if (TimeoutError.is(error)) {
|
|
383
|
+
timeout?.clear();
|
|
385
384
|
waitQueueMember[kCancelled] = true;
|
|
386
385
|
|
|
387
|
-
waitQueueMember.timeout.clear();
|
|
388
|
-
|
|
389
386
|
this.emitAndLog(
|
|
390
387
|
ConnectionPool.CONNECTION_CHECK_OUT_FAILED,
|
|
391
388
|
new ConnectionCheckOutFailedEvent(this, 'timeout', waitQueueMember.checkoutTime)
|
|
@@ -396,9 +393,16 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
396
393
|
: 'Timed out while checking out a connection from connection pool',
|
|
397
394
|
this.address
|
|
398
395
|
);
|
|
396
|
+
if (options.timeoutContext.csotEnabled()) {
|
|
397
|
+
throw new MongoOperationTimeoutError('Timed out during connection checkout', {
|
|
398
|
+
cause: timeoutError
|
|
399
|
+
});
|
|
400
|
+
}
|
|
399
401
|
throw timeoutError;
|
|
400
402
|
}
|
|
401
403
|
throw error;
|
|
404
|
+
} finally {
|
|
405
|
+
timeout?.clear();
|
|
402
406
|
}
|
|
403
407
|
}
|
|
404
408
|
|
|
@@ -681,6 +685,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
681
685
|
},
|
|
682
686
|
error => {
|
|
683
687
|
this[kPending]--;
|
|
688
|
+
this[kServer].handleError(error);
|
|
684
689
|
this.emitAndLog(
|
|
685
690
|
ConnectionPool.CONNECTION_CLOSED,
|
|
686
691
|
new ConnectionClosedEvent(
|
|
@@ -715,9 +720,6 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
715
720
|
// connection permits because that potentially delays the availability of
|
|
716
721
|
// the connection to a checkout request
|
|
717
722
|
this.createConnection((err, connection) => {
|
|
718
|
-
if (err) {
|
|
719
|
-
this[kServer].handleError(err);
|
|
720
|
-
}
|
|
721
723
|
if (!err && connection) {
|
|
722
724
|
this[kConnections].push(connection);
|
|
723
725
|
process.nextTick(() => this.processWaitQueue());
|
|
@@ -764,7 +766,6 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
764
766
|
ConnectionPool.CONNECTION_CHECK_OUT_FAILED,
|
|
765
767
|
new ConnectionCheckOutFailedEvent(this, reason, waitQueueMember.checkoutTime, error)
|
|
766
768
|
);
|
|
767
|
-
waitQueueMember.timeout.clear();
|
|
768
769
|
this[kWaitQueue].shift();
|
|
769
770
|
waitQueueMember.reject(error);
|
|
770
771
|
continue;
|
|
@@ -785,7 +786,6 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
785
786
|
ConnectionPool.CONNECTION_CHECKED_OUT,
|
|
786
787
|
new ConnectionCheckedOutEvent(this, connection, waitQueueMember.checkoutTime)
|
|
787
788
|
);
|
|
788
|
-
waitQueueMember.timeout.clear();
|
|
789
789
|
|
|
790
790
|
this[kWaitQueue].shift();
|
|
791
791
|
waitQueueMember.resolve(connection);
|
|
@@ -828,8 +828,6 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
828
828
|
);
|
|
829
829
|
waitQueueMember.resolve(connection);
|
|
830
830
|
}
|
|
831
|
-
|
|
832
|
-
waitQueueMember.timeout.clear();
|
|
833
831
|
}
|
|
834
832
|
process.nextTick(() => this.processWaitQueue());
|
|
835
833
|
});
|
|
@@ -11,7 +11,7 @@ const NODE_DRIVER_VERSION = require('../../../package.json').version;
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @public
|
|
14
|
-
* @see https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.
|
|
14
|
+
* @see https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.md#hello-command
|
|
15
15
|
*/
|
|
16
16
|
export interface ClientMetadata {
|
|
17
17
|
driver: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type EventEmitter } from 'events';
|
|
2
2
|
|
|
3
|
+
import { type TimeoutContext } from '../../timeout';
|
|
3
4
|
import { List, promiseWithResolvers } from '../../utils';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -18,7 +19,10 @@ type PendingPromises = Omit<
|
|
|
18
19
|
* Returns an AsyncIterator that iterates each 'data' event emitted from emitter.
|
|
19
20
|
* It will reject upon an error event.
|
|
20
21
|
*/
|
|
21
|
-
export function onData(
|
|
22
|
+
export function onData(
|
|
23
|
+
emitter: EventEmitter,
|
|
24
|
+
{ timeoutContext }: { timeoutContext?: TimeoutContext }
|
|
25
|
+
) {
|
|
22
26
|
// Setup pending events and pending promise lists
|
|
23
27
|
/**
|
|
24
28
|
* When the caller has not yet called .next(), we store the
|
|
@@ -87,6 +91,10 @@ export function onData(emitter: EventEmitter) {
|
|
|
87
91
|
emitter.on('data', eventHandler);
|
|
88
92
|
emitter.on('error', errorHandler);
|
|
89
93
|
|
|
94
|
+
const timeoutForSocketRead = timeoutContext?.timeoutForSocketRead;
|
|
95
|
+
timeoutForSocketRead?.throwIfExpired();
|
|
96
|
+
timeoutForSocketRead?.then(undefined, errorHandler);
|
|
97
|
+
|
|
90
98
|
return iterator;
|
|
91
99
|
|
|
92
100
|
function eventHandler(value: Buffer) {
|
|
@@ -97,6 +105,7 @@ export function onData(emitter: EventEmitter) {
|
|
|
97
105
|
|
|
98
106
|
function errorHandler(err: Error) {
|
|
99
107
|
const promise = unconsumedPromises.shift();
|
|
108
|
+
|
|
100
109
|
if (promise != null) promise.reject(err);
|
|
101
110
|
else error = err;
|
|
102
111
|
void closeHandler();
|
|
@@ -107,6 +116,7 @@ export function onData(emitter: EventEmitter) {
|
|
|
107
116
|
emitter.off('data', eventHandler);
|
|
108
117
|
emitter.off('error', errorHandler);
|
|
109
118
|
finished = true;
|
|
119
|
+
timeoutForSocketRead?.clear();
|
|
110
120
|
const doneResult = { value: undefined, done: finished } as const;
|
|
111
121
|
|
|
112
122
|
for (const promise of unconsumedPromises) {
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
pluckBSONSerializeOptions,
|
|
11
11
|
type Timestamp
|
|
12
12
|
} from '../../bson';
|
|
13
|
-
import { MongoUnexpectedServerResponseError } from '../../error';
|
|
13
|
+
import { MONGODB_ERROR_CODES, MongoUnexpectedServerResponseError } from '../../error';
|
|
14
14
|
import { type ClusterTime } from '../../sdam/common';
|
|
15
15
|
import { decorateDecryptionResult, ns } from '../../utils';
|
|
16
16
|
import {
|
|
@@ -110,6 +110,40 @@ export class MongoDBResponse extends OnDemandDocument {
|
|
|
110
110
|
// {ok:1}
|
|
111
111
|
static empty = new MongoDBResponse(new Uint8Array([13, 0, 0, 0, 16, 111, 107, 0, 1, 0, 0, 0, 0]));
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Returns true iff:
|
|
115
|
+
* - ok is 0 and the top-level code === 50
|
|
116
|
+
* - ok is 1 and the writeErrors array contains a code === 50
|
|
117
|
+
* - ok is 1 and the writeConcern object contains a code === 50
|
|
118
|
+
*/
|
|
119
|
+
get isMaxTimeExpiredError() {
|
|
120
|
+
// {ok: 0, code: 50 ... }
|
|
121
|
+
const isTopLevel = this.ok === 0 && this.code === MONGODB_ERROR_CODES.MaxTimeMSExpired;
|
|
122
|
+
if (isTopLevel) return true;
|
|
123
|
+
|
|
124
|
+
if (this.ok === 0) return false;
|
|
125
|
+
|
|
126
|
+
// {ok: 1, writeConcernError: {code: 50 ... }}
|
|
127
|
+
const isWriteConcern =
|
|
128
|
+
this.get('writeConcernError', BSONType.object)?.getNumber('code') ===
|
|
129
|
+
MONGODB_ERROR_CODES.MaxTimeMSExpired;
|
|
130
|
+
if (isWriteConcern) return true;
|
|
131
|
+
|
|
132
|
+
const writeErrors = this.get('writeErrors', BSONType.array);
|
|
133
|
+
if (writeErrors?.size()) {
|
|
134
|
+
for (let i = 0; i < writeErrors.size(); i++) {
|
|
135
|
+
const isWriteError =
|
|
136
|
+
writeErrors.get(i, BSONType.object)?.getNumber('code') ===
|
|
137
|
+
MONGODB_ERROR_CODES.MaxTimeMSExpired;
|
|
138
|
+
|
|
139
|
+
// {ok: 1, writeErrors: [{code: 50 ... }]}
|
|
140
|
+
if (isWriteError) return true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
113
147
|
/**
|
|
114
148
|
* Drivers can safely assume that the `recoveryToken` field is always a BSON document but drivers MUST NOT modify the
|
|
115
149
|
* contents of the document.
|
package/src/collection.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type ListSearchIndexesOptions
|
|
12
12
|
} from './cursor/list_search_indexes_cursor';
|
|
13
13
|
import type { Db } from './db';
|
|
14
|
-
import { MongoInvalidArgumentError } from './error';
|
|
14
|
+
import { MongoInvalidArgumentError, MongoOperationTimeoutError } from './error';
|
|
15
15
|
import type { MongoClient, PkFactory } from './mongo_client';
|
|
16
16
|
import type {
|
|
17
17
|
Filter,
|
|
@@ -115,7 +115,10 @@ export interface CollectionOptions extends BSONSerializeOptions, WriteConcernOpt
|
|
|
115
115
|
readConcern?: ReadConcernLike;
|
|
116
116
|
/** The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). */
|
|
117
117
|
readPreference?: ReadPreferenceLike;
|
|
118
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* @experimental
|
|
120
|
+
* Specifies the time an operation will run until it throws a timeout error
|
|
121
|
+
*/
|
|
119
122
|
timeoutMS?: number;
|
|
120
123
|
}
|
|
121
124
|
|
|
@@ -262,6 +265,10 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
262
265
|
this.s.collectionHint = normalizeHintField(v);
|
|
263
266
|
}
|
|
264
267
|
|
|
268
|
+
public get timeoutMS(): number | undefined {
|
|
269
|
+
return this.s.options.timeoutMS;
|
|
270
|
+
}
|
|
271
|
+
|
|
265
272
|
/**
|
|
266
273
|
* Inserts a single document into MongoDB. If documents passed in do not contain the **_id** field,
|
|
267
274
|
* one will be added to each of the documents missing it by the driver, mutating the document. This behavior
|
|
@@ -465,10 +472,14 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
465
472
|
// Intentionally, we do not inherit options from parent for this operation.
|
|
466
473
|
return await executeOperation(
|
|
467
474
|
this.client,
|
|
468
|
-
new RenameOperation(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
475
|
+
new RenameOperation(
|
|
476
|
+
this as TODO_NODE_3286,
|
|
477
|
+
newName,
|
|
478
|
+
resolveOptions(undefined, {
|
|
479
|
+
...options,
|
|
480
|
+
readPreference: ReadPreference.PRIMARY
|
|
481
|
+
})
|
|
482
|
+
) as TODO_NODE_3286
|
|
472
483
|
);
|
|
473
484
|
}
|
|
474
485
|
|
|
@@ -492,12 +503,18 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
492
503
|
*/
|
|
493
504
|
async findOne(): Promise<WithId<TSchema> | null>;
|
|
494
505
|
async findOne(filter: Filter<TSchema>): Promise<WithId<TSchema> | null>;
|
|
495
|
-
async findOne(
|
|
506
|
+
async findOne(
|
|
507
|
+
filter: Filter<TSchema>,
|
|
508
|
+
options: Omit<FindOptions, 'timeoutMode'>
|
|
509
|
+
): Promise<WithId<TSchema> | null>;
|
|
496
510
|
|
|
497
511
|
// allow an override of the schema.
|
|
498
512
|
async findOne<T = TSchema>(): Promise<T | null>;
|
|
499
513
|
async findOne<T = TSchema>(filter: Filter<TSchema>): Promise<T | null>;
|
|
500
|
-
async findOne<T = TSchema>(
|
|
514
|
+
async findOne<T = TSchema>(
|
|
515
|
+
filter: Filter<TSchema>,
|
|
516
|
+
options?: Omit<FindOptions, 'timeoutMode'>
|
|
517
|
+
): Promise<T | null>;
|
|
501
518
|
|
|
502
519
|
async findOne(
|
|
503
520
|
filter: Filter<TSchema> = {},
|
|
@@ -669,7 +686,9 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
669
686
|
new DropIndexOperation(this as TODO_NODE_3286, '*', resolveOptions(this, options))
|
|
670
687
|
);
|
|
671
688
|
return true;
|
|
672
|
-
} catch {
|
|
689
|
+
} catch (error) {
|
|
690
|
+
// TODO(NODE-6517): Driver should only filter for namespace not found error. Other errors should be thrown.
|
|
691
|
+
if (error instanceof MongoOperationTimeoutError) throw error;
|
|
673
692
|
return false;
|
|
674
693
|
}
|
|
675
694
|
}
|
|
@@ -1033,6 +1052,59 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
1033
1052
|
* });
|
|
1034
1053
|
* ```
|
|
1035
1054
|
*
|
|
1055
|
+
* @remarks
|
|
1056
|
+
* When `timeoutMS` is configured for a change stream, it will have different behaviour depending
|
|
1057
|
+
* on whether the change stream is in iterator mode or emitter mode. In both cases, a change
|
|
1058
|
+
* stream will time out if it does not receive a change event within `timeoutMS` of the last change
|
|
1059
|
+
* event.
|
|
1060
|
+
*
|
|
1061
|
+
* Note that if a change stream is consistently timing out when watching a collection, database or
|
|
1062
|
+
* client that is being changed, then this may be due to the server timing out before it can finish
|
|
1063
|
+
* processing the existing oplog. To address this, restart the change stream with a higher
|
|
1064
|
+
* `timeoutMS`.
|
|
1065
|
+
*
|
|
1066
|
+
* If the change stream times out the initial aggregate operation to establish the change stream on
|
|
1067
|
+
* the server, then the client will close the change stream. If the getMore calls to the server
|
|
1068
|
+
* time out, then the change stream will be left open, but will throw a MongoOperationTimeoutError
|
|
1069
|
+
* when in iterator mode and emit an error event that returns a MongoOperationTimeoutError in
|
|
1070
|
+
* emitter mode.
|
|
1071
|
+
*
|
|
1072
|
+
* To determine whether or not the change stream is still open following a timeout, check the
|
|
1073
|
+
* {@link ChangeStream.closed} getter.
|
|
1074
|
+
*
|
|
1075
|
+
* @example
|
|
1076
|
+
* In iterator mode, if a next() call throws a timeout error, it will attempt to resume the change stream.
|
|
1077
|
+
* The next call can just be retried after this succeeds.
|
|
1078
|
+
* ```ts
|
|
1079
|
+
* const changeStream = collection.watch([], { timeoutMS: 100 });
|
|
1080
|
+
* try {
|
|
1081
|
+
* await changeStream.next();
|
|
1082
|
+
* } catch (e) {
|
|
1083
|
+
* if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
|
|
1084
|
+
* await changeStream.next();
|
|
1085
|
+
* }
|
|
1086
|
+
* throw e;
|
|
1087
|
+
* }
|
|
1088
|
+
* ```
|
|
1089
|
+
*
|
|
1090
|
+
* @example
|
|
1091
|
+
* In emitter mode, if the change stream goes `timeoutMS` without emitting a change event, it will
|
|
1092
|
+
* emit an error event that returns a MongoOperationTimeoutError, but will not close the change
|
|
1093
|
+
* stream unless the resume attempt fails. There is no need to re-establish change listeners as
|
|
1094
|
+
* this will automatically continue emitting change events once the resume attempt completes.
|
|
1095
|
+
*
|
|
1096
|
+
* ```ts
|
|
1097
|
+
* const changeStream = collection.watch([], { timeoutMS: 100 });
|
|
1098
|
+
* changeStream.on('change', console.log);
|
|
1099
|
+
* changeStream.on('error', e => {
|
|
1100
|
+
* if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
|
|
1101
|
+
* // do nothing
|
|
1102
|
+
* } else {
|
|
1103
|
+
* changeStream.close();
|
|
1104
|
+
* }
|
|
1105
|
+
* });
|
|
1106
|
+
* ```
|
|
1107
|
+
*
|
|
1036
1108
|
* @param pipeline - An array of {@link https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.
|
|
1037
1109
|
* @param options - Optional settings for the command
|
|
1038
1110
|
* @typeParam TLocal - Type of the data being detected by the change stream
|
package/src/connection_string.ts
CHANGED
|
@@ -1092,6 +1092,7 @@ export const OPTIONS = {
|
|
|
1092
1092
|
type: 'string'
|
|
1093
1093
|
},
|
|
1094
1094
|
socketTimeoutMS: {
|
|
1095
|
+
// TODO(NODE-6491): deprecated: 'Please use timeoutMS instead',
|
|
1095
1096
|
default: 0,
|
|
1096
1097
|
type: 'uint'
|
|
1097
1098
|
},
|
|
@@ -1162,6 +1163,7 @@ export const OPTIONS = {
|
|
|
1162
1163
|
}
|
|
1163
1164
|
},
|
|
1164
1165
|
waitQueueTimeoutMS: {
|
|
1166
|
+
// TODO(NODE-6491): deprecated: 'Please use timeoutMS instead',
|
|
1165
1167
|
default: 0,
|
|
1166
1168
|
type: 'uint'
|
|
1167
1169
|
},
|