1
1
use bson:: doc;
2
2
use lazy_static:: lazy_static;
3
+ use serde:: de:: DeserializeOwned ;
3
4
4
5
use std:: { collections:: HashSet , sync:: Arc , time:: Instant } ;
5
6
6
7
use super :: { session:: TransactionState , Client , ClientSession } ;
7
8
use crate :: {
8
9
bson:: Document ,
9
- cmap:: { Connection , RawCommand , RawCommandResponse } ,
10
+ cmap:: { conn:: PinnedConnectionHandle , Connection , RawCommand , RawCommandResponse } ,
11
+ cursor:: { session:: SessionCursor , Cursor , CursorSpecification } ,
10
12
error:: {
11
13
Error ,
12
14
ErrorKind ,
@@ -70,6 +72,16 @@ impl Client {
70
72
op : T ,
71
73
session : impl Into < Option < & mut ClientSession > > ,
72
74
) -> Result < T :: O > {
75
+ self . execute_operation_with_details ( op, session)
76
+ . await
77
+ . map ( |details| details. output . operation_output )
78
+ }
79
+
80
+ async fn execute_operation_with_details < T : Operation > (
81
+ & self ,
82
+ op : T ,
83
+ session : impl Into < Option < & mut ClientSession > > ,
84
+ ) -> Result < ExecutionDetails < T > > {
73
85
Box :: pin ( async {
74
86
// TODO RUST-9: allow unacknowledged write concerns
75
87
if !op. is_acknowledged ( ) {
@@ -78,7 +90,8 @@ impl Client {
78
90
}
79
91
. into ( ) ) ;
80
92
}
81
- match session. into ( ) {
93
+ let mut implicit_session = None ;
94
+ let session = match session. into ( ) {
82
95
Some ( session) => {
83
96
if !Arc :: ptr_eq ( & self . inner , & session. client ( ) . inner ) {
84
97
return Err ( ErrorKind :: InvalidArgument {
@@ -99,41 +112,83 @@ impl Client {
99
112
. into ( ) ) ;
100
113
}
101
114
}
102
- self . execute_operation_with_retry ( op , Some ( session) ) . await
115
+ Some ( session)
103
116
}
104
117
None => {
105
- let mut implicit_session = self . start_implicit_session ( & op) . await ?;
106
- self . execute_operation_with_retry ( op, implicit_session. as_mut ( ) )
107
- . await
118
+ implicit_session = self . start_implicit_session ( & op) . await ?;
119
+ implicit_session. as_mut ( )
108
120
}
109
- }
121
+ } ;
122
+ let output = self . execute_operation_with_retry ( op, session) . await ?;
123
+ Ok ( ExecutionDetails {
124
+ output,
125
+ implicit_session,
126
+ } )
110
127
} )
111
128
. await
112
129
}
113
130
114
- /// Execute the given operation, returning the implicit session created for it if one was .
131
+ /// Execute the given operation, returning the cursor created by the operation .
115
132
///
116
133
/// Server selection be will performed using the criteria specified on the operation, if any.
117
- pub ( crate ) async fn execute_cursor_operation < T : Operation > (
118
- & self ,
119
- op : T ,
120
- ) -> Result < ( T :: O , Option < ClientSession > ) > {
134
+ pub ( crate ) async fn execute_cursor_operation < Op , T > ( & self , op : Op ) -> Result < Cursor < T > >
135
+ where
136
+ Op : Operation < O = CursorSpecification < T > > ,
137
+ T : DeserializeOwned + Unpin + Send + Sync ,
138
+ {
121
139
Box :: pin ( async {
122
- let mut implicit_session = self . start_implicit_session ( & op) . await ?;
123
- self . execute_operation_with_retry ( op, implicit_session. as_mut ( ) )
124
- . await
125
- . map ( |result| ( result, implicit_session) )
140
+ let mut details = self . execute_operation_with_details ( op, None ) . await ?;
141
+ let pinned = self . pin_connection_for_cursor ( & mut details. output ) ?;
142
+ Ok ( Cursor :: new (
143
+ self . clone ( ) ,
144
+ details. output . operation_output ,
145
+ details. implicit_session ,
146
+ pinned,
147
+ ) )
126
148
} )
127
149
. await
128
150
}
129
151
152
+ pub ( crate ) async fn execute_session_cursor_operation < Op , T > (
153
+ & self ,
154
+ op : Op ,
155
+ session : & mut ClientSession ,
156
+ ) -> Result < SessionCursor < T > >
157
+ where
158
+ Op : Operation < O = CursorSpecification < T > > ,
159
+ T : DeserializeOwned + Unpin + Send + Sync ,
160
+ {
161
+ let mut details = self . execute_operation_with_details ( op, session) . await ?;
162
+ let pinned = self . pin_connection_for_cursor ( & mut details. output ) ?;
163
+ Ok ( SessionCursor :: new (
164
+ self . clone ( ) ,
165
+ details. output . operation_output ,
166
+ pinned,
167
+ ) )
168
+ }
169
+
170
+ fn pin_connection_for_cursor < Op , T > (
171
+ & self ,
172
+ details : & mut ExecutionOutput < Op > ,
173
+ ) -> Result < Option < PinnedConnectionHandle > >
174
+ where
175
+ Op : Operation < O = CursorSpecification < T > > ,
176
+ {
177
+ let is_load_balanced = self . inner . options . load_balanced . unwrap_or ( false ) ;
178
+ if is_load_balanced && details. operation_output . info . id != 0 {
179
+ Ok ( Some ( details. connection . pin ( ) ?) )
180
+ } else {
181
+ Ok ( None )
182
+ }
183
+ }
184
+
130
185
/// Selects a server and executes the given operation on it, optionally using a provided
131
186
/// session. Retries the operation upon failure if retryability is supported.
132
187
async fn execute_operation_with_retry < T : Operation > (
133
188
& self ,
134
189
mut op : T ,
135
190
mut session : Option < & mut ClientSession > ,
136
- ) -> Result < T :: O > {
191
+ ) -> Result < ExecutionOutput < T > > {
137
192
// If the current transaction has been committed/aborted and it is not being
138
193
// re-committed/re-aborted, reset the transaction's state to TransactionState::None.
139
194
if let Some ( ref mut session) = session {
@@ -161,17 +216,20 @@ impl Client {
161
216
}
162
217
} ;
163
218
164
- let mut conn = match server. pool . check_out ( ) . await {
165
- Ok ( conn) => conn,
166
- Err ( mut err) => {
167
- err. add_labels_and_update_pin ( None , & mut session, None ) ?;
219
+ let mut conn = match op. pinned_connection ( ) {
220
+ Some ( l) => l. take_connection ( ) . await ?,
221
+ None => match server. pool . check_out ( ) . await {
222
+ Ok ( c) => c,
223
+ Err ( mut err) => {
224
+ err. add_labels_and_update_pin ( None , & mut session, None ) ?;
168
225
169
- if err. is_pool_cleared ( ) {
170
- return self . execute_retry ( & mut op, & mut session, None , err) . await ;
171
- } else {
172
- return Err ( err) ;
226
+ if err. is_pool_cleared ( ) {
227
+ return self . execute_retry ( & mut op, & mut session, None , err) . await ;
228
+ } else {
229
+ return Err ( err) ;
230
+ }
173
231
}
174
- }
232
+ } ,
175
233
} ;
176
234
177
235
let retryability = self . get_retryability ( & conn, & op, & session) . await ?;
@@ -200,7 +258,10 @@ impl Client {
200
258
)
201
259
. await
202
260
{
203
- Ok ( result) => Ok ( result) ,
261
+ Ok ( operation_output) => Ok ( ExecutionOutput {
262
+ operation_output,
263
+ connection : conn,
264
+ } ) ,
204
265
Err ( mut err) => {
205
266
// Retryable writes are only supported by storage engines with document-level
206
267
// locking, so users need to disable retryable writes if using mmapv1.
@@ -246,7 +307,7 @@ impl Client {
246
307
session : & mut Option < & mut ClientSession > ,
247
308
txn_number : Option < i64 > ,
248
309
first_error : Error ,
249
- ) -> Result < T :: O > {
310
+ ) -> Result < ExecutionOutput < T > > {
250
311
op. update_for_retry ( ) ;
251
312
252
313
let server = match self . select_server ( op. selection_criteria ( ) ) . await {
@@ -256,9 +317,12 @@ impl Client {
256
317
}
257
318
} ;
258
319
259
- let mut conn = match server. pool . check_out ( ) . await {
260
- Ok ( c) => c,
261
- Err ( _) => return Err ( first_error) ,
320
+ let mut conn = match op. pinned_connection ( ) {
321
+ Some ( c) => c. take_connection ( ) . await ?,
322
+ None => match server. pool . check_out ( ) . await {
323
+ Ok ( c) => c,
324
+ Err ( _) => return Err ( first_error) ,
325
+ } ,
262
326
} ;
263
327
264
328
let retryability = self . get_retryability ( & conn, op, session) . await ?;
@@ -270,7 +334,10 @@ impl Client {
270
334
. execute_operation_on_connection ( op, & mut conn, session, txn_number, & retryability)
271
335
. await
272
336
{
273
- Ok ( result) => Ok ( result) ,
337
+ Ok ( operation_output) => Ok ( ExecutionOutput {
338
+ operation_output,
339
+ connection : conn,
340
+ } ) ,
274
341
Err ( err) => {
275
342
self . inner
276
343
. topology
@@ -761,3 +828,13 @@ struct CommandResult<T> {
761
828
raw : RawCommandResponse ,
762
829
deserialized : T ,
763
830
}
831
+
832
+ struct ExecutionDetails < T : Operation > {
833
+ output : ExecutionOutput < T > ,
834
+ implicit_session : Option < ClientSession > ,
835
+ }
836
+
837
+ struct ExecutionOutput < T : Operation > {
838
+ operation_output : T :: O ,
839
+ connection : Connection ,
840
+ }
0 commit comments