@@ -59,7 +59,7 @@ use crate::{
59
59
Retryability ,
60
60
} ,
61
61
options:: { ChangeStreamOptions , SelectionCriteria } ,
62
- sdam:: { HandshakePhase , SelectedServer , ServerType , TopologyType , TransactionSupportStatus } ,
62
+ sdam:: { HandshakePhase , ServerType , TopologyType , TransactionSupportStatus } ,
63
63
selection_criteria:: ReadPreference ,
64
64
tracking_arc:: TrackingArc ,
65
65
ClusterTime ,
@@ -318,15 +318,16 @@ impl Client {
318
318
. and_then ( |s| s. transaction . pinned_mongos ( ) )
319
319
. or_else ( || op. selection_criteria ( ) ) ;
320
320
321
- let server = match self
321
+ let ( server, effective_criteria ) = match self
322
322
. select_server (
323
323
selection_criteria,
324
324
op. name ( ) ,
325
325
retry. as_ref ( ) . map ( |r| & r. first_server ) ,
326
+ op. override_criteria ( ) ,
326
327
)
327
328
. await
328
329
{
329
- Ok ( server ) => server ,
330
+ Ok ( out ) => out ,
330
331
Err ( mut err) => {
331
332
retry. first_error ( ) ?;
332
333
@@ -398,6 +399,7 @@ impl Client {
398
399
& mut session,
399
400
txn_number,
400
401
retryability,
402
+ effective_criteria,
401
403
)
402
404
. await
403
405
{
@@ -471,127 +473,21 @@ impl Client {
471
473
session : & mut Option < & mut ClientSession > ,
472
474
txn_number : Option < i64 > ,
473
475
retryability : Retryability ,
476
+ effective_criteria : SelectionCriteria ,
474
477
) -> Result < T :: O > {
475
478
loop {
476
- let stream_description = connection. stream_description ( ) ?;
477
- let is_sharded = stream_description. initial_server_type == ServerType :: Mongos ;
478
- let mut cmd = op. build ( stream_description) ?;
479
- self . inner . topology . update_command_with_read_pref (
480
- connection. address ( ) ,
481
- & mut cmd,
482
- op. selection_criteria ( ) ,
483
- ) ;
484
-
485
- match session {
486
- Some ( ref mut session) if op. supports_sessions ( ) && op. is_acknowledged ( ) => {
487
- cmd. set_session ( session) ;
488
- if let Some ( txn_number) = txn_number {
489
- cmd. set_txn_number ( txn_number) ;
490
- }
491
- if session
492
- . options ( )
493
- . and_then ( |opts| opts. snapshot )
494
- . unwrap_or ( false )
495
- {
496
- if connection
497
- . stream_description ( ) ?
498
- . max_wire_version
499
- . unwrap_or ( 0 )
500
- < 13
501
- {
502
- let labels: Option < Vec < _ > > = None ;
503
- return Err ( Error :: new (
504
- ErrorKind :: IncompatibleServer {
505
- message : "Snapshot reads require MongoDB 5.0 or later" . into ( ) ,
506
- } ,
507
- labels,
508
- ) ) ;
509
- }
510
- cmd. set_snapshot_read_concern ( session) ;
511
- }
512
- // If this is a causally consistent session, set `readConcern.afterClusterTime`.
513
- // Causal consistency defaults to true, unless snapshot is true.
514
- else if session. causal_consistency ( )
515
- && matches ! (
516
- session. transaction. state,
517
- TransactionState :: None | TransactionState :: Starting
518
- )
519
- && op. supports_read_concern ( stream_description)
520
- {
521
- cmd. set_after_cluster_time ( session) ;
522
- }
523
-
524
- match session. transaction . state {
525
- TransactionState :: Starting => {
526
- cmd. set_start_transaction ( ) ;
527
- cmd. set_autocommit ( ) ;
528
- if session. causal_consistency ( ) {
529
- cmd. set_after_cluster_time ( session) ;
530
- }
531
-
532
- if let Some ( ref options) = session. transaction . options {
533
- if let Some ( ref read_concern) = options. read_concern {
534
- cmd. set_read_concern_level ( read_concern. level . clone ( ) ) ;
535
- }
536
- }
537
- if self . is_load_balanced ( ) {
538
- session. pin_connection ( connection. pin ( ) ?) ;
539
- } else if is_sharded {
540
- session. pin_mongos ( connection. address ( ) . clone ( ) ) ;
541
- }
542
- session. transaction . state = TransactionState :: InProgress ;
543
- }
544
- TransactionState :: InProgress => cmd. set_autocommit ( ) ,
545
- TransactionState :: Committed { .. } | TransactionState :: Aborted => {
546
- cmd. set_autocommit ( ) ;
547
-
548
- // Append the recovery token to the command if we are committing or
549
- // aborting on a sharded transaction.
550
- if is_sharded {
551
- if let Some ( ref recovery_token) = session. transaction . recovery_token
552
- {
553
- cmd. set_recovery_token ( recovery_token) ;
554
- }
555
- }
556
- }
557
- _ => { }
558
- }
559
- session. update_last_use ( ) ;
560
- }
561
- Some ( ref session) if !op. supports_sessions ( ) && !session. is_implicit ( ) => {
562
- return Err ( ErrorKind :: InvalidArgument {
563
- message : format ! ( "{} does not support sessions" , cmd. name) ,
564
- }
565
- . into ( ) ) ;
566
- }
567
- Some ( ref session) if !op. is_acknowledged ( ) && !session. is_implicit ( ) => {
568
- return Err ( ErrorKind :: InvalidArgument {
569
- message : "Cannot use ClientSessions with unacknowledged write concern"
570
- . to_string ( ) ,
571
- }
572
- . into ( ) ) ;
573
- }
574
- _ => { }
575
- }
576
-
577
- let session_cluster_time = session. as_ref ( ) . and_then ( |session| session. cluster_time ( ) ) ;
578
- let client_cluster_time = self . inner . topology . cluster_time ( ) ;
579
- let max_cluster_time =
580
- std:: cmp:: max ( session_cluster_time, client_cluster_time. as_ref ( ) ) ;
581
- if let Some ( cluster_time) = max_cluster_time {
582
- cmd. set_cluster_time ( cluster_time) ;
583
- }
479
+ let cmd = self . build_command (
480
+ op,
481
+ connection,
482
+ session,
483
+ txn_number,
484
+ effective_criteria. clone ( ) ,
485
+ ) ?;
584
486
585
487
let connection_info = connection. info ( ) ;
586
488
let service_id = connection. service_id ( ) ;
587
489
let request_id = next_request_id ( ) ;
588
-
589
- if let Some ( ref server_api) = self . inner . options . server_api {
590
- cmd. set_server_api ( server_api) ;
591
- }
592
-
593
490
let should_redact = cmd. should_redact ( ) ;
594
-
595
491
let cmd_name = cmd. name . clone ( ) ;
596
492
let target_db = cmd. target_db . clone ( ) ;
597
493
@@ -630,8 +526,9 @@ impl Client {
630
526
let start_time = Instant :: now ( ) ;
631
527
let command_result = match connection. send_message ( message) . await {
632
528
Ok ( response) => {
633
- self . handle_response ( op, session, is_sharded, response)
634
- . await
529
+ let is_sharded =
530
+ connection. stream_description ( ) ?. initial_server_type == ServerType :: Mongos ;
531
+ self . parse_response ( op, session, is_sharded, response) . await
635
532
}
636
533
Err ( err) => Err ( err) ,
637
534
} ;
@@ -706,6 +603,7 @@ impl Client {
706
603
let context = ExecutionContext {
707
604
connection,
708
605
session : session. as_deref_mut ( ) ,
606
+ effective_criteria : effective_criteria. clone ( ) ,
709
607
} ;
710
608
711
609
match op. handle_response ( response, context) . await {
@@ -737,6 +635,128 @@ impl Client {
737
635
}
738
636
}
739
637
638
+ fn build_command < T : Operation > (
639
+ & self ,
640
+ op : & mut T ,
641
+ connection : & mut PooledConnection ,
642
+ session : & mut Option < & mut ClientSession > ,
643
+ txn_number : Option < i64 > ,
644
+ effective_criteria : SelectionCriteria ,
645
+ ) -> Result < crate :: cmap:: Command > {
646
+ let stream_description = connection. stream_description ( ) ?;
647
+ let is_sharded = stream_description. initial_server_type == ServerType :: Mongos ;
648
+ let mut cmd = op. build ( stream_description) ?;
649
+ self . inner . topology . update_command_with_read_pref (
650
+ connection. address ( ) ,
651
+ & mut cmd,
652
+ & effective_criteria,
653
+ ) ;
654
+
655
+ match session {
656
+ Some ( ref mut session) if op. supports_sessions ( ) && op. is_acknowledged ( ) => {
657
+ cmd. set_session ( session) ;
658
+ if let Some ( txn_number) = txn_number {
659
+ cmd. set_txn_number ( txn_number) ;
660
+ }
661
+ if session
662
+ . options ( )
663
+ . and_then ( |opts| opts. snapshot )
664
+ . unwrap_or ( false )
665
+ {
666
+ if connection
667
+ . stream_description ( ) ?
668
+ . max_wire_version
669
+ . unwrap_or ( 0 )
670
+ < 13
671
+ {
672
+ let labels: Option < Vec < _ > > = None ;
673
+ return Err ( Error :: new (
674
+ ErrorKind :: IncompatibleServer {
675
+ message : "Snapshot reads require MongoDB 5.0 or later" . into ( ) ,
676
+ } ,
677
+ labels,
678
+ ) ) ;
679
+ }
680
+ cmd. set_snapshot_read_concern ( session) ;
681
+ }
682
+ // If this is a causally consistent session, set `readConcern.afterClusterTime`.
683
+ // Causal consistency defaults to true, unless snapshot is true.
684
+ else if session. causal_consistency ( )
685
+ && matches ! (
686
+ session. transaction. state,
687
+ TransactionState :: None | TransactionState :: Starting
688
+ )
689
+ && op. supports_read_concern ( stream_description)
690
+ {
691
+ cmd. set_after_cluster_time ( session) ;
692
+ }
693
+
694
+ match session. transaction . state {
695
+ TransactionState :: Starting => {
696
+ cmd. set_start_transaction ( ) ;
697
+ cmd. set_autocommit ( ) ;
698
+ if session. causal_consistency ( ) {
699
+ cmd. set_after_cluster_time ( session) ;
700
+ }
701
+
702
+ if let Some ( ref options) = session. transaction . options {
703
+ if let Some ( ref read_concern) = options. read_concern {
704
+ cmd. set_read_concern_level ( read_concern. level . clone ( ) ) ;
705
+ }
706
+ }
707
+ if self . is_load_balanced ( ) {
708
+ session. pin_connection ( connection. pin ( ) ?) ;
709
+ } else if is_sharded {
710
+ session. pin_mongos ( connection. address ( ) . clone ( ) ) ;
711
+ }
712
+ session. transaction . state = TransactionState :: InProgress ;
713
+ }
714
+ TransactionState :: InProgress => cmd. set_autocommit ( ) ,
715
+ TransactionState :: Committed { .. } | TransactionState :: Aborted => {
716
+ cmd. set_autocommit ( ) ;
717
+
718
+ // Append the recovery token to the command if we are committing or aborting
719
+ // on a sharded transaction.
720
+ if is_sharded {
721
+ if let Some ( ref recovery_token) = session. transaction . recovery_token {
722
+ cmd. set_recovery_token ( recovery_token) ;
723
+ }
724
+ }
725
+ }
726
+ _ => { }
727
+ }
728
+ session. update_last_use ( ) ;
729
+ }
730
+ Some ( ref session) if !op. supports_sessions ( ) && !session. is_implicit ( ) => {
731
+ return Err ( ErrorKind :: InvalidArgument {
732
+ message : format ! ( "{} does not support sessions" , cmd. name) ,
733
+ }
734
+ . into ( ) ) ;
735
+ }
736
+ Some ( ref session) if !op. is_acknowledged ( ) && !session. is_implicit ( ) => {
737
+ return Err ( ErrorKind :: InvalidArgument {
738
+ message : "Cannot use ClientSessions with unacknowledged write concern"
739
+ . to_string ( ) ,
740
+ }
741
+ . into ( ) ) ;
742
+ }
743
+ _ => { }
744
+ }
745
+
746
+ let session_cluster_time = session. as_ref ( ) . and_then ( |session| session. cluster_time ( ) ) ;
747
+ let client_cluster_time = self . inner . topology . cluster_time ( ) ;
748
+ let max_cluster_time = std:: cmp:: max ( session_cluster_time, client_cluster_time. as_ref ( ) ) ;
749
+ if let Some ( cluster_time) = max_cluster_time {
750
+ cmd. set_cluster_time ( cluster_time) ;
751
+ }
752
+
753
+ if let Some ( ref server_api) = self . inner . options . server_api {
754
+ cmd. set_server_api ( server_api) ;
755
+ }
756
+
757
+ Ok ( cmd)
758
+ }
759
+
740
760
#[ cfg( feature = "in-use-encryption" ) ]
741
761
fn auto_encrypt < ' a > (
742
762
& ' a self ,
@@ -789,7 +809,7 @@ impl Client {
789
809
. await
790
810
}
791
811
792
- async fn handle_response < T : Operation > (
812
+ async fn parse_response < T : Operation > (
793
813
& self ,
794
814
op : & T ,
795
815
session : & mut Option < & mut ClientSession > ,
@@ -864,8 +884,8 @@ impl Client {
864
884
( matches ! ( topology_type, TopologyType :: Single ) && server_type. is_available ( ) )
865
885
|| server_type. is_data_bearing ( )
866
886
} ) ) ;
867
- let _: SelectedServer = self
868
- . select_server ( Some ( & criteria) , operation_name, None )
887
+ let _ = self
888
+ . select_server ( Some ( & criteria) , operation_name, None , |_ , _| None )
869
889
. await ?;
870
890
Ok ( ( ) )
871
891
}
0 commit comments