1
- import { promisify } from 'util' ;
2
-
3
1
import { Binary , type Document , Long , type Timestamp } from './bson' ;
4
2
import type { CommandOptions , Connection } from './cmap/connection' ;
5
3
import { ConnectionPoolMetrics } from './cmap/metrics' ;
@@ -38,7 +36,6 @@ import {
38
36
import {
39
37
ByteUtils ,
40
38
calculateDurationInMs ,
41
- type Callback ,
42
39
commandSupportsReadConcern ,
43
40
isPromiseLike ,
44
41
List ,
@@ -415,14 +412,14 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
415
412
* Commits the currently active transaction in this session.
416
413
*/
417
414
async commitTransaction ( ) : Promise < void > {
418
- return endTransactionAsync ( this , 'commitTransaction' ) ;
415
+ return endTransaction ( this , 'commitTransaction' ) ;
419
416
}
420
417
421
418
/**
422
419
* Aborts the currently active transaction in this session.
423
420
*/
424
421
async abortTransaction ( ) : Promise < void > {
425
- return endTransactionAsync ( this , 'abortTransaction' ) ;
422
+ return endTransaction ( this , 'abortTransaction' ) ;
426
423
}
427
424
428
425
/**
@@ -545,33 +542,33 @@ function isMaxTimeMSExpiredError(err: MongoError) {
545
542
) ;
546
543
}
547
544
548
- function attemptTransactionCommit < T > (
545
+ async function attemptTransactionCommit < T > (
549
546
session : ClientSession ,
550
547
startTime : number ,
551
548
fn : WithTransactionCallback < T > ,
552
549
result : any ,
553
550
options : TransactionOptions
554
551
) : Promise < T > {
555
- return session . commitTransaction ( ) . then (
556
- ( ) => result ,
557
- ( err : MongoError ) => {
558
- if (
559
- err instanceof MongoError &&
560
- hasNotTimedOut ( startTime , MAX_WITH_TRANSACTION_TIMEOUT ) &&
561
- ! isMaxTimeMSExpiredError ( err )
562
- ) {
563
- if ( err . hasErrorLabel ( MongoErrorLabel . UnknownTransactionCommitResult ) ) {
564
- return attemptTransactionCommit ( session , startTime , fn , result , options ) ;
565
- }
566
-
567
- if ( err . hasErrorLabel ( MongoErrorLabel . TransientTransactionError ) ) {
568
- return attemptTransaction ( session , startTime , fn , options ) ;
569
- }
552
+ try {
553
+ await session . commitTransaction ( ) ;
554
+ return result ;
555
+ } catch ( err ) {
556
+ if (
557
+ err instanceof MongoError &&
558
+ hasNotTimedOut ( startTime , MAX_WITH_TRANSACTION_TIMEOUT ) &&
559
+ ! isMaxTimeMSExpiredError ( err )
560
+ ) {
561
+ if ( err . hasErrorLabel ( MongoErrorLabel . UnknownTransactionCommitResult ) ) {
562
+ return attemptTransactionCommit ( session , startTime , fn , result , options ) ;
570
563
}
571
564
572
- throw err ;
565
+ if ( err . hasErrorLabel ( MongoErrorLabel . TransientTransactionError ) ) {
566
+ return attemptTransaction ( session , startTime , fn , options ) ;
567
+ }
573
568
}
574
- ) ;
569
+
570
+ throw err ;
571
+ }
575
572
}
576
573
577
574
const USER_EXPLICIT_TXN_END_STATES = new Set < TxnState > ( [
@@ -584,81 +581,64 @@ function userExplicitlyEndedTransaction(session: ClientSession) {
584
581
return USER_EXPLICIT_TXN_END_STATES . has ( session . transaction . state ) ;
585
582
}
586
583
587
- function attemptTransaction < T > (
584
+ async function attemptTransaction < T > (
588
585
session : ClientSession ,
589
586
startTime : number ,
590
587
fn : WithTransactionCallback < T > ,
591
588
options : TransactionOptions = { }
592
589
) : Promise < any > {
593
590
session . startTransaction ( options ) ;
591
+ async function maybeRetryOrThrow ( err : MongoError ) : Promise < any > {
592
+ if (
593
+ err instanceof MongoError &&
594
+ err . hasErrorLabel ( MongoErrorLabel . TransientTransactionError ) &&
595
+ hasNotTimedOut ( startTime , MAX_WITH_TRANSACTION_TIMEOUT )
596
+ ) {
597
+ const transactionResult = await attemptTransaction ( session , startTime , fn , options ) ;
598
+ return transactionResult ;
599
+ }
594
600
595
- let promise ;
596
- try {
597
- promise = fn ( session ) ;
598
- } catch ( err ) {
599
- promise = Promise . reject ( err ) ;
600
- }
601
+ if ( isMaxTimeMSExpiredError ( err ) ) {
602
+ err . addErrorLabel ( MongoErrorLabel . UnknownTransactionCommitResult ) ;
603
+ }
601
604
602
- if ( ! isPromiseLike ( promise ) ) {
603
- session . abortTransaction ( ) . catch ( ( ) => null ) ;
604
- return Promise . reject (
605
- new MongoInvalidArgumentError ( 'Function provided to `withTransaction` must return a Promise' )
606
- ) ;
605
+ throw err ;
607
606
}
608
607
609
- return promise . then (
610
- result => {
611
- if ( userExplicitlyEndedTransaction ( session ) ) {
612
- return result ;
613
- }
614
-
615
- return attemptTransactionCommit ( session , startTime , fn , result , options ) ;
616
- } ,
617
- err => {
618
- function maybeRetryOrThrow ( err : MongoError ) : Promise < any > {
619
- if (
620
- err instanceof MongoError &&
621
- err . hasErrorLabel ( MongoErrorLabel . TransientTransactionError ) &&
622
- hasNotTimedOut ( startTime , MAX_WITH_TRANSACTION_TIMEOUT )
623
- ) {
624
- return attemptTransaction ( session , startTime , fn , options ) ;
625
- }
626
-
627
- if ( isMaxTimeMSExpiredError ( err ) ) {
628
- err . addErrorLabel ( MongoErrorLabel . UnknownTransactionCommitResult ) ;
629
- }
608
+ try {
609
+ const promise = fn ( session ) ;
630
610
631
- throw err ;
632
- }
611
+ if ( ! isPromiseLike ( promise ) ) {
612
+ await session . abortTransaction ( ) ;
613
+ return new MongoInvalidArgumentError (
614
+ 'Function provided to `withTransaction` must return a Promise'
615
+ ) ;
616
+ }
617
+ const result = await promise ;
633
618
634
- if ( session . inTransaction ( ) ) {
635
- return session . abortTransaction ( ) . then ( ( ) => maybeRetryOrThrow ( err ) ) ;
636
- }
619
+ if ( userExplicitlyEndedTransaction ( session ) ) {
620
+ return result ;
621
+ }
637
622
638
- return maybeRetryOrThrow ( err ) ;
623
+ return await attemptTransactionCommit ( session , startTime , fn , result , options ) ;
624
+ } catch ( err ) {
625
+ if ( session . inTransaction ( ) ) {
626
+ await session . abortTransaction ( ) ;
639
627
}
640
- ) ;
641
- }
642
628
643
- const endTransactionAsync = promisify (
644
- endTransaction as (
645
- session : ClientSession ,
646
- commandName : 'abortTransaction' | 'commitTransaction' ,
647
- callback : ( error : Error ) => void
648
- ) => void
649
- ) ;
629
+ return await maybeRetryOrThrow ( err ) ;
630
+ }
631
+ }
650
632
651
- function endTransaction (
633
+ async function endTransaction (
652
634
session : ClientSession ,
653
- commandName : 'abortTransaction' | 'commitTransaction' ,
654
- callback : Callback < void >
635
+ commandName : 'abortTransaction' | 'commitTransaction'
655
636
) {
656
637
// handle any initial problematic cases
657
638
const txnState = session . transaction . state ;
658
639
659
640
if ( txnState === TxnState . NO_TRANSACTION ) {
660
- callback ( new MongoTransactionError ( 'No transaction started' ) ) ;
661
- return ;
641
+ throw new MongoTransactionError ( 'No transaction started' ) ;
662
642
}
663
643
664
644
if ( commandName === 'commitTransaction' ) {
@@ -668,37 +648,31 @@ function endTransaction(
668
648
) {
669
649
// the transaction was never started, we can safely exit here
670
650
session . transaction . transition ( TxnState . TRANSACTION_COMMITTED_EMPTY ) ;
671
- callback ( ) ;
672
651
return ;
673
652
}
674
653
675
654
if ( txnState === TxnState . TRANSACTION_ABORTED ) {
676
- callback (
677
- new MongoTransactionError ( 'Cannot call commitTransaction after calling abortTransaction' )
655
+ throw new MongoTransactionError (
656
+ 'Cannot call commitTransaction after calling abortTransaction'
678
657
) ;
679
- return ;
680
658
}
681
659
} else {
682
660
if ( txnState === TxnState . STARTING_TRANSACTION ) {
683
661
// the transaction was never started, we can safely exit here
684
662
session . transaction . transition ( TxnState . TRANSACTION_ABORTED ) ;
685
- callback ( ) ;
686
- return ;
687
663
}
688
664
689
665
if ( txnState === TxnState . TRANSACTION_ABORTED ) {
690
- callback ( new MongoTransactionError ( 'Cannot call abortTransaction twice' ) ) ;
691
- return ;
666
+ throw new MongoTransactionError ( 'Cannot call abortTransaction twice' ) ;
692
667
}
693
668
694
669
if (
695
670
txnState === TxnState . TRANSACTION_COMMITTED ||
696
671
txnState === TxnState . TRANSACTION_COMMITTED_EMPTY
697
672
) {
698
- callback (
699
- new MongoTransactionError ( 'Cannot call abortTransaction after calling commitTransaction' )
673
+ throw new MongoTransactionError (
674
+ 'Cannot call abortTransaction after calling commitTransaction'
700
675
) ;
701
- return ;
702
676
}
703
677
}
704
678
@@ -731,9 +705,6 @@ function endTransaction(
731
705
if ( session . loadBalanced ) {
732
706
maybeClearPinnedConnection ( session , { force : false } ) ;
733
707
}
734
-
735
- // The spec indicates that we should ignore all errors on `abortTransaction`
736
- return callback ( ) ;
737
708
}
738
709
739
710
session . transaction . transition ( TxnState . TRANSACTION_COMMITTED ) ;
@@ -753,15 +724,13 @@ function endTransaction(
753
724
session . unpin ( { error } ) ;
754
725
}
755
726
}
756
-
757
- callback ( error ) ;
758
727
}
759
728
760
729
if ( session . transaction . recoveryToken ) {
761
730
command . recoveryToken = session . transaction . recoveryToken ;
762
731
}
763
732
764
- const handleFirstCommandAttempt = ( error ?: Error ) => {
733
+ const handleFirstCommandAttempt = async ( error ?: Error ) => {
765
734
if ( command . abortTransaction ) {
766
735
// always unpin on abort regardless of command outcome
767
736
session . unpin ( ) ;
@@ -778,29 +747,37 @@ function endTransaction(
778
747
} ) ;
779
748
}
780
749
781
- executeOperation (
782
- session . client ,
783
- new RunAdminCommandOperation ( command , {
784
- session,
785
- readPreference : ReadPreference . primary ,
786
- bypassPinningCheck : true
787
- } )
788
- ) . then ( ( ) => commandHandler ( ) , commandHandler ) ;
789
- return ;
750
+ try {
751
+ await executeOperation (
752
+ session . client ,
753
+ new RunAdminCommandOperation ( command , {
754
+ session,
755
+ readPreference : ReadPreference . primary ,
756
+ bypassPinningCheck : true
757
+ } )
758
+ ) ;
759
+ commandHandler ( ) ;
760
+ } catch ( err ) {
761
+ commandHandler ( err ) ;
762
+ throw err ;
763
+ }
790
764
}
791
-
792
- commandHandler ( error ) ;
793
765
} ;
794
766
795
- // send the command
796
- executeOperation (
797
- session . client ,
798
- new RunAdminCommandOperation ( command , {
799
- session,
800
- readPreference : ReadPreference . primary ,
801
- bypassPinningCheck : true
802
- } )
803
- ) . then ( ( ) => handleFirstCommandAttempt ( ) , handleFirstCommandAttempt ) ;
767
+ try {
768
+ // send the command
769
+ await executeOperation (
770
+ session . client ,
771
+ new RunAdminCommandOperation ( command , {
772
+ session,
773
+ readPreference : ReadPreference . primary ,
774
+ bypassPinningCheck : true
775
+ } )
776
+ ) ;
777
+ await handleFirstCommandAttempt ( ) ;
778
+ } catch ( err ) {
779
+ await handleFirstCommandAttempt ( err ) ;
780
+ }
804
781
}
805
782
806
783
/** @public */
0 commit comments