@@ -26,7 +26,13 @@ use crate::{
2626 Retryability ,
2727 } ,
2828 options:: SelectionCriteria ,
29- sdam:: { HandshakePhase , SelectedServer , SessionSupportStatus , TransactionSupportStatus } ,
29+ sdam:: {
30+ HandshakePhase ,
31+ SelectedServer ,
32+ ServerType ,
33+ SessionSupportStatus ,
34+ TransactionSupportStatus ,
35+ } ,
3036 selection_criteria:: ReadPreference ,
3137} ;
3238
@@ -91,6 +97,7 @@ impl Client {
9197 . into ( ) ) ;
9298 }
9399 }
100+
94101 self . execute_operation_with_retry ( op, Some ( session) ) . await
95102 }
96103 None => {
@@ -135,18 +142,23 @@ impl Client {
135142 }
136143 }
137144
138- let server = match self . select_server ( op. selection_criteria ( ) ) . await {
145+ let selection_criteria = session
146+ . as_ref ( )
147+ . and_then ( |s| s. transaction . pinned_mongos . as_ref ( ) )
148+ . or_else ( || op. selection_criteria ( ) ) ;
149+
150+ let server = match self . select_server ( selection_criteria) . await {
139151 Ok ( server) => server,
140152 Err ( mut err) => {
141- err. add_labels ( None , & session, None ) ?;
153+ err. add_labels_and_update_pin ( None , & mut session, None ) ?;
142154 return Err ( err) ;
143155 }
144156 } ;
145157
146158 let mut conn = match server. pool . check_out ( ) . await {
147159 Ok ( conn) => conn,
148160 Err ( mut err) => {
149- err. add_labels ( None , & session, None ) ?;
161+ err. add_labels_and_update_pin ( None , & mut session, None ) ?;
150162
151163 if err. is_pool_cleared ( ) {
152164 return self . execute_retry ( & mut op, & mut session, None , err) . await ;
@@ -229,6 +241,8 @@ impl Client {
229241 txn_number : Option < i64 > ,
230242 first_error : Error ,
231243 ) -> Result < T :: O > {
244+ op. update_for_retry ( ) ;
245+
232246 let server = match self . select_server ( op. selection_criteria ( ) ) . await {
233247 Ok ( server) => server,
234248 Err ( _) => {
@@ -246,8 +260,6 @@ impl Client {
246260 return Err ( first_error) ;
247261 }
248262
249- op. update_for_retry ( ) ;
250-
251263 match self
252264 . execute_operation_on_connection ( op, & mut conn, session, txn_number, & retryability)
253265 . await
@@ -286,7 +298,8 @@ impl Client {
286298 wc. validate ( ) ?;
287299 }
288300
289- let mut cmd = op. build ( connection. stream_description ( ) ?) ?;
301+ let stream_description = connection. stream_description ( ) ?;
302+ let mut cmd = op. build ( stream_description) ?;
290303 self . inner
291304 . topology
292305 . update_command_with_read_pref ( connection. address ( ) , & mut cmd, op. selection_criteria ( ) )
@@ -324,6 +337,9 @@ impl Client {
324337 cmd. set_start_transaction ( ) ;
325338 cmd. set_autocommit ( ) ;
326339 cmd. set_txn_read_concern ( * session) ?;
340+ if stream_description. initial_server_type == ServerType :: Mongos {
341+ session. pin_mongos ( connection. address ( ) . clone ( ) ) ;
342+ }
327343 session. transaction . state = TransactionState :: InProgress ;
328344 }
329345 TransactionState :: InProgress
@@ -471,13 +487,13 @@ impl Client {
471487 handler. handle_command_failed_event ( command_failed_event) ;
472488 } ) ;
473489
474- if let Some ( session) = session {
490+ if let Some ( ref mut session) = session {
475491 if err. is_network_error ( ) {
476492 session. mark_dirty ( ) ;
477493 }
478494 }
479495
480- err. add_labels ( Some ( connection) , session, Some ( retryability) ) ?;
496+ err. add_labels_and_update_pin ( Some ( connection) , session, Some ( retryability) ) ?;
481497 op. handle_error ( err)
482498 }
483499 Ok ( response) => {
@@ -504,7 +520,11 @@ impl Client {
504520 match op. handle_response ( response. deserialized , connection. stream_description ( ) ?) {
505521 Ok ( response) => Ok ( response) ,
506522 Err ( mut err) => {
507- err. add_labels ( Some ( connection) , session, Some ( retryability) ) ?;
523+ err. add_labels_and_update_pin (
524+ Some ( connection) ,
525+ session,
526+ Some ( retryability) ,
527+ ) ?;
508528 Err ( err)
509529 }
510530 }
@@ -618,7 +638,7 @@ impl Client {
618638}
619639
620640impl Error {
621- /// Adds the necessary labels to this Error.
641+ /// Adds the necessary labels to this Error, and unpins the session if needed .
622642 ///
623643 /// A TransientTransactionError label should be added if a transaction is in progress and the
624644 /// error is a network or server selection error.
@@ -628,10 +648,13 @@ impl Error {
628648 /// server version, a label should only be added if the `retry_writes` client option is not set
629649 /// to `false`, the operation during which the error occured is write-retryable, and a
630650 /// TransientTransactionError label has not already been added.
631- fn add_labels (
651+ ///
652+ /// If the TransientTransactionError or UnknownTransactionCommitResult labels are added, the
653+ /// ClientSession should be unpinned.
654+ fn add_labels_and_update_pin (
632655 & mut self ,
633656 conn : Option < & Connection > ,
634- session : & Option < & mut ClientSession > ,
657+ session : & mut Option < & mut ClientSession > ,
635658 retryability : Option < & Retryability > ,
636659 ) -> Result < ( ) > {
637660 let transaction_state = session. as_ref ( ) . map_or ( & TransactionState :: None , |session| {
@@ -675,6 +698,15 @@ impl Error {
675698 }
676699 }
677700 }
701+
702+ if let Some ( ref mut session) = session {
703+ if self . contains_label ( TRANSIENT_TRANSACTION_ERROR )
704+ || self . contains_label ( UNKNOWN_TRANSACTION_COMMIT_RESULT )
705+ {
706+ session. unpin_mongos ( ) ;
707+ }
708+ }
709+
678710 Ok ( ( ) )
679711 }
680712}
0 commit comments