mongodb 6.8.1 → 6.9.0-dev.20240913.sha.8b0f3541
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 +14 -1
- package/lib/beta.d.ts +7905 -0
- package/lib/beta.js +21 -0
- package/lib/beta.js.map +1 -0
- package/lib/bson.js +5 -5
- package/lib/bson.js.map +1 -1
- package/lib/bulk/common.js +16 -21
- package/lib/bulk/common.js.map +1 -1
- package/lib/bulk/ordered.js.map +1 -1
- package/lib/bulk/unordered.js.map +1 -1
- package/lib/change_stream.js +10 -8
- package/lib/change_stream.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +14 -3
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +25 -7
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/crypto_callbacks.js +6 -6
- package/lib/client-side-encryption/crypto_callbacks.js.map +1 -1
- package/lib/client-side-encryption/mongocryptd_manager.js +9 -5
- package/lib/client-side-encryption/mongocryptd_manager.js.map +1 -1
- package/lib/client-side-encryption/providers/aws.js +1 -2
- package/lib/client-side-encryption/providers/aws.js.map +1 -1
- package/lib/client-side-encryption/providers/azure.js +5 -5
- package/lib/client-side-encryption/providers/azure.js.map +1 -1
- package/lib/client-side-encryption/providers/gcp.js +1 -2
- package/lib/client-side-encryption/providers/gcp.js.map +1 -1
- package/lib/client-side-encryption/providers/index.js +2 -3
- package/lib/client-side-encryption/providers/index.js.map +1 -1
- package/lib/client-side-encryption/state_machine.js +9 -4
- package/lib/client-side-encryption/state_machine.js.map +1 -1
- package/lib/cmap/auth/auth_provider.js.map +1 -1
- package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -1
- package/lib/cmap/auth/gssapi.js +4 -4
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongocr.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/automated_callback_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/callback_workflow.js +0 -2
- package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/command_builders.js +2 -3
- package/lib/cmap/auth/mongodb_oidc/command_builders.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/machine_workflow.js +0 -2
- package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/token_cache.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
- package/lib/cmap/auth/plain.js.map +1 -1
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/auth/x509.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +62 -5
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +10 -7
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +3 -5
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +11 -9
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/connection_pool_events.js +7 -3
- package/lib/cmap/connection_pool_events.js.map +1 -1
- package/lib/cmap/handshake/client_metadata.js +5 -5
- package/lib/cmap/handshake/client_metadata.js.map +1 -1
- package/lib/cmap/metrics.js +1 -1
- package/lib/cmap/metrics.js.map +1 -1
- package/lib/cmap/stream_description.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +5 -5
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/cmap/wire_protocol/constants.js +2 -2
- package/lib/cmap/wire_protocol/on_data.js +1 -2
- package/lib/cmap/wire_protocol/on_data.js.map +1 -1
- package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
- package/lib/cmap/wire_protocol/responses.js +4 -4
- package/lib/cmap/wire_protocol/responses.js.map +1 -1
- package/lib/cmap/wire_protocol/shared.js +2 -3
- package/lib/cmap/wire_protocol/shared.js.map +1 -1
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +12 -6
- package/lib/connection_string.js.map +1 -1
- package/lib/constants.js +1 -0
- package/lib/constants.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +22 -7
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +1 -1
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/change_stream_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +3 -3
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/db.js +1 -1
- package/lib/db.js.map +1 -1
- package/lib/deps.js +16 -8
- package/lib/deps.js.map +1 -1
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +19 -14
- package/lib/error.js.map +1 -1
- package/lib/explain.js.map +1 -1
- package/lib/gridfs/download.js +1 -4
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js +1 -1
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +0 -4
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +4 -2
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +15 -1
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_client_auth_providers.js.map +1 -1
- package/lib/mongo_logger.js +8 -8
- package/lib/mongo_logger.js.map +1 -1
- package/lib/mongo_types.js +1 -0
- package/lib/mongo_types.js.map +1 -1
- package/lib/operations/aggregate.js +1 -0
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/bulk_write.js.map +1 -1
- package/lib/operations/client_bulk_write/command_builder.js +198 -0
- package/lib/operations/client_bulk_write/command_builder.js.map +1 -0
- package/lib/operations/client_bulk_write/common.js +3 -0
- package/lib/operations/client_bulk_write/common.js.map +1 -0
- package/lib/operations/collections.js.map +1 -1
- package/lib/operations/command.js +1 -1
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/count.js.map +1 -1
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/delete.js +2 -2
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/execute_operation.js +111 -109
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/find_and_modify.js +2 -8
- package/lib/operations/find_and_modify.js.map +1 -1
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/is_capped.js.map +1 -1
- package/lib/operations/kill_cursors.js.map +1 -1
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/operation.js +5 -5
- 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/search_indexes/drop.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 +2 -2
- package/lib/operations/update.js.map +1 -1
- package/lib/operations/validate_collection.js.map +1 -1
- package/lib/read_concern.js.map +1 -1
- package/lib/read_preference.js +1 -1
- package/lib/read_preference.js.map +1 -1
- package/lib/resource_management.js +58 -0
- package/lib/resource_management.js.map +1 -0
- package/lib/sdam/common.js +3 -3
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/monitor.js +1 -5
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +2 -2
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +3 -3
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/server_selection.js +5 -5
- package/lib/sdam/server_selection.js.map +1 -1
- package/lib/sdam/srv_polling.js +2 -3
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +1 -1
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +221 -218
- package/lib/sessions.js.map +1 -1
- package/lib/sort.js +2 -3
- package/lib/sort.js.map +1 -1
- package/lib/timeout.js +0 -1
- package/lib/timeout.js.map +1 -1
- package/lib/transactions.js +2 -2
- package/lib/transactions.js.map +1 -1
- package/lib/utils.js +49 -51
- package/lib/utils.js.map +1 -1
- package/lib/write_concern.js +2 -2
- package/lib/write_concern.js.map +1 -1
- package/mongodb.d.ts +150 -142
- package/package.json +27 -28
- package/src/beta.ts +22 -0
- package/src/bson.ts +1 -2
- package/src/bulk/common.ts +18 -18
- package/src/change_stream.ts +33 -15
- package/src/client-side-encryption/auto_encrypter.ts +18 -82
- package/src/client-side-encryption/client_encryption.ts +51 -54
- package/src/client-side-encryption/mongocryptd_manager.ts +10 -6
- package/src/client-side-encryption/state_machine.ts +28 -6
- package/src/cmap/auth/gssapi.ts +1 -1
- package/src/cmap/auth/mongodb_aws.ts +2 -2
- package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +2 -2
- package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +2 -2
- package/src/cmap/commands.ts +70 -5
- package/src/cmap/connect.ts +4 -1
- package/src/cmap/connection.ts +2 -2
- package/src/cmap/connection_pool.ts +17 -9
- package/src/cmap/connection_pool_events.ts +34 -2
- package/src/cmap/handshake/client_metadata.ts +1 -1
- package/src/cmap/wire_protocol/constants.ts +2 -2
- package/src/cmap/wire_protocol/shared.ts +1 -2
- package/src/collection.ts +16 -15
- package/src/connection_string.ts +10 -3
- package/src/constants.ts +1 -0
- package/src/cursor/abstract_cursor.ts +39 -14
- package/src/cursor/aggregation_cursor.ts +6 -4
- package/src/deps.ts +8 -1
- package/src/error.ts +33 -14
- package/src/gridfs/download.ts +28 -4
- package/src/gridfs/upload.ts +1 -6
- package/src/index.ts +5 -1
- package/src/mongo_client.ts +29 -5
- package/src/mongo_logger.ts +5 -3
- package/src/mongo_types.ts +69 -68
- package/src/operations/aggregate.ts +2 -1
- package/src/operations/bulk_write.ts +2 -2
- package/src/operations/client_bulk_write/command_builder.ts +283 -0
- package/src/operations/client_bulk_write/common.ts +146 -0
- package/src/operations/command.ts +1 -1
- package/src/operations/execute_operation.ts +137 -131
- package/src/operations/find_and_modify.ts +2 -7
- package/src/operations/insert.ts +3 -4
- package/src/operations/operation.ts +7 -10
- package/src/operations/search_indexes/drop.ts +4 -1
- package/src/resource_management.ts +74 -0
- package/src/sdam/monitor.ts +3 -5
- package/src/sdam/server.ts +1 -1
- package/src/sdam/server_description.ts +5 -6
- package/src/sdam/srv_polling.ts +1 -2
- package/src/sessions.ts +291 -277
- package/src/sort.ts +1 -1
- package/src/timeout.ts +0 -1
- package/src/transactions.ts +1 -2
- package/src/utils.ts +9 -4
- package/src/write_concern.ts +2 -2
- package/tsconfig.json +2 -1
package/src/sessions.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { executeOperation } from './operations/execute_operation';
|
|
|
27
27
|
import { RunAdminCommandOperation } from './operations/run_command';
|
|
28
28
|
import { ReadConcernLevel } from './read_concern';
|
|
29
29
|
import { ReadPreference } from './read_preference';
|
|
30
|
+
import { type AsyncDisposable, configureResourceManagement } from './resource_management';
|
|
30
31
|
import { _advanceClusterTime, type ClusterTime, TopologyType } from './sdam/common';
|
|
31
32
|
import {
|
|
32
33
|
isTransactionCommand,
|
|
@@ -45,7 +46,7 @@ import {
|
|
|
45
46
|
squashError,
|
|
46
47
|
uuidV4
|
|
47
48
|
} from './utils';
|
|
48
|
-
import { WriteConcern } from './write_concern';
|
|
49
|
+
import { WriteConcern, type WriteConcernOptions, type WriteConcernSettings } from './write_concern';
|
|
49
50
|
|
|
50
51
|
const minWireVersionForShardedTransactions = 8;
|
|
51
52
|
|
|
@@ -105,7 +106,10 @@ export interface EndSessionOptions {
|
|
|
105
106
|
* NOTE: not meant to be instantiated directly.
|
|
106
107
|
* @public
|
|
107
108
|
*/
|
|
108
|
-
export class ClientSession
|
|
109
|
+
export class ClientSession
|
|
110
|
+
extends TypedEventEmitter<ClientSessionEvents>
|
|
111
|
+
implements AsyncDisposable
|
|
112
|
+
{
|
|
109
113
|
/** @internal */
|
|
110
114
|
client: MongoClient;
|
|
111
115
|
/** @internal */
|
|
@@ -255,7 +259,10 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
/**
|
|
258
|
-
*
|
|
262
|
+
* Frees any client-side resources held by the current session. If a session is in a transaction,
|
|
263
|
+
* the transaction is aborted.
|
|
264
|
+
*
|
|
265
|
+
* Does not end the session on the server.
|
|
259
266
|
*
|
|
260
267
|
* @param options - Optional settings. Currently reserved for future use
|
|
261
268
|
*/
|
|
@@ -286,6 +293,16 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
286
293
|
maybeClearPinnedConnection(this, { force: true, ...options });
|
|
287
294
|
}
|
|
288
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* @beta
|
|
298
|
+
* @experimental
|
|
299
|
+
* An alias for {@link ClientSession.endSession|ClientSession.endSession()}.
|
|
300
|
+
*/
|
|
301
|
+
declare [Symbol.asyncDispose]: () => Promise<void>;
|
|
302
|
+
/** @internal */
|
|
303
|
+
async asyncDispose() {
|
|
304
|
+
await this.endSession({ force: true });
|
|
305
|
+
}
|
|
289
306
|
|
|
290
307
|
/**
|
|
291
308
|
* Advances the operationTime for a ClientSession.
|
|
@@ -426,14 +443,167 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
426
443
|
* Commits the currently active transaction in this session.
|
|
427
444
|
*/
|
|
428
445
|
async commitTransaction(): Promise<void> {
|
|
429
|
-
|
|
446
|
+
if (this.transaction.state === TxnState.NO_TRANSACTION) {
|
|
447
|
+
throw new MongoTransactionError('No transaction started');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (
|
|
451
|
+
this.transaction.state === TxnState.STARTING_TRANSACTION ||
|
|
452
|
+
this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY
|
|
453
|
+
) {
|
|
454
|
+
// the transaction was never started, we can safely exit here
|
|
455
|
+
this.transaction.transition(TxnState.TRANSACTION_COMMITTED_EMPTY);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {
|
|
460
|
+
throw new MongoTransactionError(
|
|
461
|
+
'Cannot call commitTransaction after calling abortTransaction'
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const command: {
|
|
466
|
+
commitTransaction: 1;
|
|
467
|
+
writeConcern?: WriteConcernSettings;
|
|
468
|
+
recoveryToken?: Document;
|
|
469
|
+
maxTimeMS?: number;
|
|
470
|
+
} = { commitTransaction: 1 };
|
|
471
|
+
|
|
472
|
+
const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
|
|
473
|
+
if (wc != null) {
|
|
474
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (this.transaction.state === TxnState.TRANSACTION_COMMITTED) {
|
|
478
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, ...wc, w: 'majority' });
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (typeof this.transaction.options.maxTimeMS === 'number') {
|
|
482
|
+
command.maxTimeMS = this.transaction.options.maxTimeMS;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (this.transaction.recoveryToken) {
|
|
486
|
+
command.recoveryToken = this.transaction.recoveryToken;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const operation = new RunAdminCommandOperation(command, {
|
|
490
|
+
session: this,
|
|
491
|
+
readPreference: ReadPreference.primary,
|
|
492
|
+
bypassPinningCheck: true
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
try {
|
|
496
|
+
await executeOperation(this.client, operation);
|
|
497
|
+
return;
|
|
498
|
+
} catch (firstCommitError) {
|
|
499
|
+
if (firstCommitError instanceof MongoError && isRetryableWriteError(firstCommitError)) {
|
|
500
|
+
// SPEC-1185: apply majority write concern when retrying commitTransaction
|
|
501
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, ...wc, w: 'majority' });
|
|
502
|
+
// per txns spec, must unpin session in this case
|
|
503
|
+
this.unpin({ force: true });
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
await executeOperation(this.client, operation);
|
|
507
|
+
return;
|
|
508
|
+
} catch (retryCommitError) {
|
|
509
|
+
// If the retry failed, we process that error instead of the original
|
|
510
|
+
if (shouldAddUnknownTransactionCommitResultLabel(retryCommitError)) {
|
|
511
|
+
retryCommitError.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (shouldUnpinAfterCommitError(retryCommitError)) {
|
|
515
|
+
this.unpin({ error: retryCommitError });
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
throw retryCommitError;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (shouldAddUnknownTransactionCommitResultLabel(firstCommitError)) {
|
|
523
|
+
firstCommitError.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (shouldUnpinAfterCommitError(firstCommitError)) {
|
|
527
|
+
this.unpin({ error: firstCommitError });
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
throw firstCommitError;
|
|
531
|
+
} finally {
|
|
532
|
+
this.transaction.transition(TxnState.TRANSACTION_COMMITTED);
|
|
533
|
+
}
|
|
430
534
|
}
|
|
431
535
|
|
|
432
536
|
/**
|
|
433
537
|
* Aborts the currently active transaction in this session.
|
|
434
538
|
*/
|
|
435
539
|
async abortTransaction(): Promise<void> {
|
|
436
|
-
|
|
540
|
+
if (this.transaction.state === TxnState.NO_TRANSACTION) {
|
|
541
|
+
throw new MongoTransactionError('No transaction started');
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (this.transaction.state === TxnState.STARTING_TRANSACTION) {
|
|
545
|
+
// the transaction was never started, we can safely exit here
|
|
546
|
+
this.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {
|
|
551
|
+
throw new MongoTransactionError('Cannot call abortTransaction twice');
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (
|
|
555
|
+
this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
|
|
556
|
+
this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY
|
|
557
|
+
) {
|
|
558
|
+
throw new MongoTransactionError(
|
|
559
|
+
'Cannot call abortTransaction after calling commitTransaction'
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const command: {
|
|
564
|
+
abortTransaction: 1;
|
|
565
|
+
writeConcern?: WriteConcernOptions;
|
|
566
|
+
recoveryToken?: Document;
|
|
567
|
+
} = { abortTransaction: 1 };
|
|
568
|
+
|
|
569
|
+
const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
|
|
570
|
+
if (wc != null) {
|
|
571
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (this.transaction.recoveryToken) {
|
|
575
|
+
command.recoveryToken = this.transaction.recoveryToken;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const operation = new RunAdminCommandOperation(command, {
|
|
579
|
+
session: this,
|
|
580
|
+
readPreference: ReadPreference.primary,
|
|
581
|
+
bypassPinningCheck: true
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
await executeOperation(this.client, operation);
|
|
586
|
+
this.unpin();
|
|
587
|
+
return;
|
|
588
|
+
} catch (firstAbortError) {
|
|
589
|
+
this.unpin();
|
|
590
|
+
|
|
591
|
+
if (firstAbortError instanceof MongoError && isRetryableWriteError(firstAbortError)) {
|
|
592
|
+
try {
|
|
593
|
+
await executeOperation(this.client, operation);
|
|
594
|
+
return;
|
|
595
|
+
} catch {
|
|
596
|
+
// we do not retry the retry
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// The spec indicates that if the operation times out or fails with a non-retryable error, we should ignore all errors on `abortTransaction`
|
|
601
|
+
} finally {
|
|
602
|
+
this.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
603
|
+
if (this.loadBalanced) {
|
|
604
|
+
maybeClearPinnedConnection(this, { force: false });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
437
607
|
}
|
|
438
608
|
|
|
439
609
|
/**
|
|
@@ -479,23 +649,132 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
479
649
|
fn: WithTransactionCallback<T>,
|
|
480
650
|
options?: TransactionOptions
|
|
481
651
|
): Promise<T> {
|
|
652
|
+
const MAX_TIMEOUT = 120000;
|
|
482
653
|
const startTime = now();
|
|
483
|
-
|
|
654
|
+
|
|
655
|
+
let committed = false;
|
|
656
|
+
let result: any;
|
|
657
|
+
|
|
658
|
+
while (!committed) {
|
|
659
|
+
this.startTransaction(options); // may throw on error
|
|
660
|
+
|
|
661
|
+
try {
|
|
662
|
+
const promise = fn(this);
|
|
663
|
+
if (!isPromiseLike(promise)) {
|
|
664
|
+
throw new MongoInvalidArgumentError(
|
|
665
|
+
'Function provided to `withTransaction` must return a Promise'
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
result = await promise;
|
|
670
|
+
|
|
671
|
+
if (
|
|
672
|
+
this.transaction.state === TxnState.NO_TRANSACTION ||
|
|
673
|
+
this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
|
|
674
|
+
this.transaction.state === TxnState.TRANSACTION_ABORTED
|
|
675
|
+
) {
|
|
676
|
+
// Assume callback intentionally ended the transaction
|
|
677
|
+
return result;
|
|
678
|
+
}
|
|
679
|
+
} catch (fnError) {
|
|
680
|
+
if (!(fnError instanceof MongoError) || fnError instanceof MongoInvalidArgumentError) {
|
|
681
|
+
await this.abortTransaction();
|
|
682
|
+
throw fnError;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (
|
|
686
|
+
this.transaction.state === TxnState.STARTING_TRANSACTION ||
|
|
687
|
+
this.transaction.state === TxnState.TRANSACTION_IN_PROGRESS
|
|
688
|
+
) {
|
|
689
|
+
await this.abortTransaction();
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (
|
|
693
|
+
fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
694
|
+
now() - startTime < MAX_TIMEOUT
|
|
695
|
+
) {
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
throw fnError;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
while (!committed) {
|
|
703
|
+
try {
|
|
704
|
+
/*
|
|
705
|
+
* We will rely on ClientSession.commitTransaction() to
|
|
706
|
+
* apply a majority write concern if commitTransaction is
|
|
707
|
+
* being retried (see: DRIVERS-601)
|
|
708
|
+
*/
|
|
709
|
+
await this.commitTransaction();
|
|
710
|
+
committed = true;
|
|
711
|
+
} catch (commitError) {
|
|
712
|
+
/*
|
|
713
|
+
* Note: a maxTimeMS error will have the MaxTimeMSExpired
|
|
714
|
+
* code (50) and can be reported as a top-level error or
|
|
715
|
+
* inside writeConcernError, ex.
|
|
716
|
+
* { ok:0, code: 50, codeName: 'MaxTimeMSExpired' }
|
|
717
|
+
* { ok:1, writeConcernError: { code: 50, codeName: 'MaxTimeMSExpired' } }
|
|
718
|
+
*/
|
|
719
|
+
if (
|
|
720
|
+
!isMaxTimeMSExpiredError(commitError) &&
|
|
721
|
+
commitError.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult) &&
|
|
722
|
+
now() - startTime < MAX_TIMEOUT
|
|
723
|
+
) {
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (
|
|
728
|
+
commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
729
|
+
now() - startTime < MAX_TIMEOUT
|
|
730
|
+
) {
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
throw commitError;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return result;
|
|
484
740
|
}
|
|
485
741
|
}
|
|
486
742
|
|
|
487
|
-
|
|
743
|
+
configureResourceManagement(ClientSession.prototype);
|
|
744
|
+
|
|
488
745
|
const NON_DETERMINISTIC_WRITE_CONCERN_ERRORS = new Set([
|
|
489
746
|
'CannotSatisfyWriteConcern',
|
|
490
747
|
'UnknownReplWriteConcern',
|
|
491
748
|
'UnsatisfiableWriteConcern'
|
|
492
749
|
]);
|
|
493
750
|
|
|
494
|
-
function
|
|
495
|
-
|
|
751
|
+
function shouldUnpinAfterCommitError(commitError: Error) {
|
|
752
|
+
if (commitError instanceof MongoError) {
|
|
753
|
+
if (
|
|
754
|
+
isRetryableWriteError(commitError) ||
|
|
755
|
+
commitError instanceof MongoWriteConcernError ||
|
|
756
|
+
isMaxTimeMSExpiredError(commitError)
|
|
757
|
+
) {
|
|
758
|
+
if (isUnknownTransactionCommitResult(commitError)) {
|
|
759
|
+
// per txns spec, must unpin session in this case
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
} else if (commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function shouldAddUnknownTransactionCommitResultLabel(commitError: MongoError) {
|
|
770
|
+
let ok = isRetryableWriteError(commitError);
|
|
771
|
+
ok ||= commitError instanceof MongoWriteConcernError;
|
|
772
|
+
ok ||= isMaxTimeMSExpiredError(commitError);
|
|
773
|
+
ok &&= isUnknownTransactionCommitResult(commitError);
|
|
774
|
+
return ok;
|
|
496
775
|
}
|
|
497
776
|
|
|
498
|
-
function isUnknownTransactionCommitResult(err: MongoError) {
|
|
777
|
+
function isUnknownTransactionCommitResult(err: MongoError): err is MongoError {
|
|
499
778
|
const isNonDeterministicWriteConcernError =
|
|
500
779
|
err instanceof MongoServerError &&
|
|
501
780
|
err.codeName &&
|
|
@@ -550,282 +829,17 @@ export function maybeClearPinnedConnection(
|
|
|
550
829
|
}
|
|
551
830
|
}
|
|
552
831
|
|
|
553
|
-
function isMaxTimeMSExpiredError(err: MongoError) {
|
|
832
|
+
function isMaxTimeMSExpiredError(err: MongoError): boolean {
|
|
554
833
|
if (err == null || !(err instanceof MongoServerError)) {
|
|
555
834
|
return false;
|
|
556
835
|
}
|
|
557
836
|
|
|
558
837
|
return (
|
|
559
838
|
err.code === MONGODB_ERROR_CODES.MaxTimeMSExpired ||
|
|
560
|
-
|
|
839
|
+
err.writeConcernError?.code === MONGODB_ERROR_CODES.MaxTimeMSExpired
|
|
561
840
|
);
|
|
562
841
|
}
|
|
563
842
|
|
|
564
|
-
async function attemptTransactionCommit<T>(
|
|
565
|
-
session: ClientSession,
|
|
566
|
-
startTime: number,
|
|
567
|
-
fn: WithTransactionCallback<T>,
|
|
568
|
-
result: T,
|
|
569
|
-
options: TransactionOptions
|
|
570
|
-
): Promise<T> {
|
|
571
|
-
try {
|
|
572
|
-
await session.commitTransaction();
|
|
573
|
-
return result;
|
|
574
|
-
} catch (commitErr) {
|
|
575
|
-
if (
|
|
576
|
-
commitErr instanceof MongoError &&
|
|
577
|
-
hasNotTimedOut(startTime, MAX_WITH_TRANSACTION_TIMEOUT) &&
|
|
578
|
-
!isMaxTimeMSExpiredError(commitErr)
|
|
579
|
-
) {
|
|
580
|
-
if (commitErr.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult)) {
|
|
581
|
-
return await attemptTransactionCommit(session, startTime, fn, result, options);
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
if (commitErr.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
|
|
585
|
-
return await attemptTransaction(session, startTime, fn, options);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
throw commitErr;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
const USER_EXPLICIT_TXN_END_STATES = new Set<TxnState>([
|
|
594
|
-
TxnState.NO_TRANSACTION,
|
|
595
|
-
TxnState.TRANSACTION_COMMITTED,
|
|
596
|
-
TxnState.TRANSACTION_ABORTED
|
|
597
|
-
]);
|
|
598
|
-
|
|
599
|
-
function userExplicitlyEndedTransaction(session: ClientSession) {
|
|
600
|
-
return USER_EXPLICIT_TXN_END_STATES.has(session.transaction.state);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
async function attemptTransaction<T>(
|
|
604
|
-
session: ClientSession,
|
|
605
|
-
startTime: number,
|
|
606
|
-
fn: WithTransactionCallback<T>,
|
|
607
|
-
options: TransactionOptions = {}
|
|
608
|
-
): Promise<T> {
|
|
609
|
-
session.startTransaction(options);
|
|
610
|
-
|
|
611
|
-
let promise;
|
|
612
|
-
try {
|
|
613
|
-
promise = fn(session);
|
|
614
|
-
} catch (err) {
|
|
615
|
-
promise = Promise.reject(err);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
if (!isPromiseLike(promise)) {
|
|
619
|
-
try {
|
|
620
|
-
await session.abortTransaction();
|
|
621
|
-
} catch (error) {
|
|
622
|
-
squashError(error);
|
|
623
|
-
}
|
|
624
|
-
throw new MongoInvalidArgumentError(
|
|
625
|
-
'Function provided to `withTransaction` must return a Promise'
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
try {
|
|
630
|
-
const result = await promise;
|
|
631
|
-
if (userExplicitlyEndedTransaction(session)) {
|
|
632
|
-
return result;
|
|
633
|
-
}
|
|
634
|
-
return await attemptTransactionCommit(session, startTime, fn, result, options);
|
|
635
|
-
} catch (err) {
|
|
636
|
-
if (session.inTransaction()) {
|
|
637
|
-
await session.abortTransaction();
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
if (
|
|
641
|
-
err instanceof MongoError &&
|
|
642
|
-
err.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
643
|
-
hasNotTimedOut(startTime, MAX_WITH_TRANSACTION_TIMEOUT)
|
|
644
|
-
) {
|
|
645
|
-
return await attemptTransaction(session, startTime, fn, options);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (isMaxTimeMSExpiredError(err)) {
|
|
649
|
-
err.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
throw err;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
async function endTransaction(
|
|
657
|
-
session: ClientSession,
|
|
658
|
-
commandName: 'abortTransaction' | 'commitTransaction'
|
|
659
|
-
): Promise<void> {
|
|
660
|
-
// handle any initial problematic cases
|
|
661
|
-
const txnState = session.transaction.state;
|
|
662
|
-
|
|
663
|
-
if (txnState === TxnState.NO_TRANSACTION) {
|
|
664
|
-
throw new MongoTransactionError('No transaction started');
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
if (commandName === 'commitTransaction') {
|
|
668
|
-
if (
|
|
669
|
-
txnState === TxnState.STARTING_TRANSACTION ||
|
|
670
|
-
txnState === TxnState.TRANSACTION_COMMITTED_EMPTY
|
|
671
|
-
) {
|
|
672
|
-
// the transaction was never started, we can safely exit here
|
|
673
|
-
session.transaction.transition(TxnState.TRANSACTION_COMMITTED_EMPTY);
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
if (txnState === TxnState.TRANSACTION_ABORTED) {
|
|
678
|
-
throw new MongoTransactionError(
|
|
679
|
-
'Cannot call commitTransaction after calling abortTransaction'
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
} else {
|
|
683
|
-
if (txnState === TxnState.STARTING_TRANSACTION) {
|
|
684
|
-
// the transaction was never started, we can safely exit here
|
|
685
|
-
session.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
if (txnState === TxnState.TRANSACTION_ABORTED) {
|
|
690
|
-
throw new MongoTransactionError('Cannot call abortTransaction twice');
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
if (
|
|
694
|
-
txnState === TxnState.TRANSACTION_COMMITTED ||
|
|
695
|
-
txnState === TxnState.TRANSACTION_COMMITTED_EMPTY
|
|
696
|
-
) {
|
|
697
|
-
throw new MongoTransactionError(
|
|
698
|
-
'Cannot call abortTransaction after calling commitTransaction'
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// construct and send the command
|
|
704
|
-
const command: Document = { [commandName]: 1 };
|
|
705
|
-
|
|
706
|
-
// apply a writeConcern if specified
|
|
707
|
-
let writeConcern;
|
|
708
|
-
if (session.transaction.options.writeConcern) {
|
|
709
|
-
writeConcern = Object.assign({}, session.transaction.options.writeConcern);
|
|
710
|
-
} else if (session.clientOptions && session.clientOptions.writeConcern) {
|
|
711
|
-
writeConcern = { w: session.clientOptions.writeConcern.w };
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
if (txnState === TxnState.TRANSACTION_COMMITTED) {
|
|
715
|
-
writeConcern = Object.assign({ wtimeoutMS: 10000 }, writeConcern, { w: 'majority' });
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if (writeConcern) {
|
|
719
|
-
WriteConcern.apply(command, writeConcern);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
if (commandName === 'commitTransaction' && session.transaction.options.maxTimeMS) {
|
|
723
|
-
Object.assign(command, { maxTimeMS: session.transaction.options.maxTimeMS });
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if (session.transaction.recoveryToken) {
|
|
727
|
-
command.recoveryToken = session.transaction.recoveryToken;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
try {
|
|
731
|
-
// send the command
|
|
732
|
-
await executeOperation(
|
|
733
|
-
session.client,
|
|
734
|
-
new RunAdminCommandOperation(command, {
|
|
735
|
-
session,
|
|
736
|
-
readPreference: ReadPreference.primary,
|
|
737
|
-
bypassPinningCheck: true
|
|
738
|
-
})
|
|
739
|
-
);
|
|
740
|
-
if (command.abortTransaction) {
|
|
741
|
-
// always unpin on abort regardless of command outcome
|
|
742
|
-
session.unpin();
|
|
743
|
-
}
|
|
744
|
-
if (commandName !== 'commitTransaction') {
|
|
745
|
-
session.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
746
|
-
if (session.loadBalanced) {
|
|
747
|
-
maybeClearPinnedConnection(session, { force: false });
|
|
748
|
-
}
|
|
749
|
-
} else {
|
|
750
|
-
session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
|
|
751
|
-
}
|
|
752
|
-
} catch (firstAttemptErr) {
|
|
753
|
-
if (command.abortTransaction) {
|
|
754
|
-
// always unpin on abort regardless of command outcome
|
|
755
|
-
session.unpin();
|
|
756
|
-
}
|
|
757
|
-
if (firstAttemptErr instanceof MongoError && isRetryableWriteError(firstAttemptErr)) {
|
|
758
|
-
// SPEC-1185: apply majority write concern when retrying commitTransaction
|
|
759
|
-
if (command.commitTransaction) {
|
|
760
|
-
// per txns spec, must unpin session in this case
|
|
761
|
-
session.unpin({ force: true });
|
|
762
|
-
|
|
763
|
-
command.writeConcern = Object.assign({ wtimeout: 10000 }, command.writeConcern, {
|
|
764
|
-
w: 'majority'
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
try {
|
|
769
|
-
await executeOperation(
|
|
770
|
-
session.client,
|
|
771
|
-
new RunAdminCommandOperation(command, {
|
|
772
|
-
session,
|
|
773
|
-
readPreference: ReadPreference.primary,
|
|
774
|
-
bypassPinningCheck: true
|
|
775
|
-
})
|
|
776
|
-
);
|
|
777
|
-
if (commandName !== 'commitTransaction') {
|
|
778
|
-
session.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
779
|
-
if (session.loadBalanced) {
|
|
780
|
-
maybeClearPinnedConnection(session, { force: false });
|
|
781
|
-
}
|
|
782
|
-
} else {
|
|
783
|
-
session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
|
|
784
|
-
}
|
|
785
|
-
} catch (secondAttemptErr) {
|
|
786
|
-
handleEndTransactionError(session, commandName, secondAttemptErr);
|
|
787
|
-
}
|
|
788
|
-
} else {
|
|
789
|
-
handleEndTransactionError(session, commandName, firstAttemptErr);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
function handleEndTransactionError(
|
|
795
|
-
session: ClientSession,
|
|
796
|
-
commandName: 'abortTransaction' | 'commitTransaction',
|
|
797
|
-
error: Error
|
|
798
|
-
) {
|
|
799
|
-
if (commandName !== 'commitTransaction') {
|
|
800
|
-
session.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
801
|
-
if (session.loadBalanced) {
|
|
802
|
-
maybeClearPinnedConnection(session, { force: false });
|
|
803
|
-
}
|
|
804
|
-
// The spec indicates that if the operation times out or fails with a non-retryable error, we should ignore all errors on `abortTransaction`
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
|
|
809
|
-
if (error instanceof MongoError) {
|
|
810
|
-
if (
|
|
811
|
-
isRetryableWriteError(error) ||
|
|
812
|
-
error instanceof MongoWriteConcernError ||
|
|
813
|
-
isMaxTimeMSExpiredError(error)
|
|
814
|
-
) {
|
|
815
|
-
if (isUnknownTransactionCommitResult(error)) {
|
|
816
|
-
error.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
|
|
817
|
-
|
|
818
|
-
// per txns spec, must unpin session in this case
|
|
819
|
-
session.unpin({ error });
|
|
820
|
-
}
|
|
821
|
-
} else if (error.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
|
|
822
|
-
session.unpin({ error });
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
throw error;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
843
|
/** @public */
|
|
830
844
|
export type ServerSessionId = { id: Binary };
|
|
831
845
|
|
package/src/sort.ts
CHANGED
package/src/timeout.ts
CHANGED
package/src/transactions.ts
CHANGED
|
@@ -2,8 +2,7 @@ import type { Document } from './bson';
|
|
|
2
2
|
import { MongoRuntimeError, MongoTransactionError } from './error';
|
|
3
3
|
import type { CommandOperationOptions } from './operations/command';
|
|
4
4
|
import { ReadConcern, type ReadConcernLike } from './read_concern';
|
|
5
|
-
import type
|
|
6
|
-
import { ReadPreference } from './read_preference';
|
|
5
|
+
import { ReadPreference, type ReadPreferenceLike } from './read_preference';
|
|
7
6
|
import type { Server } from './sdam/server';
|
|
8
7
|
import { WriteConcern } from './write_concern';
|
|
9
8
|
|