1
- use super :: Client ;
1
+ use super :: { Client , ClientSession } ;
2
2
3
- use std:: collections:: HashSet ;
3
+ use std:: { collections:: HashSet , sync :: Arc } ;
4
4
5
5
use bson:: Document ;
6
6
use lazy_static:: lazy_static;
7
7
use time:: PreciseTime ;
8
8
9
9
use crate :: {
10
10
cmap:: Connection ,
11
- error:: Result ,
11
+ error:: { ErrorKind , Result } ,
12
12
event:: command:: { CommandFailedEvent , CommandStartedEvent , CommandSucceededEvent } ,
13
13
operation:: Operation ,
14
+ options:: SelectionCriteria ,
15
+ sdam:: { Server , SessionSupportStatus } ,
14
16
} ;
15
17
16
18
lazy_static ! {
@@ -30,68 +32,84 @@ lazy_static! {
30
32
}
31
33
32
34
impl Client {
33
- /// Executes an operation and returns the connection used to do so along with the result of the
34
- /// operation. This will be used primarily for the opening of exhaust cursors.
35
- #[ allow( dead_code) ]
36
- pub ( crate ) async fn execute_exhaust_operation < T : Operation > (
35
+ /// Execute the given operation.
36
+ ///
37
+ /// Server selection will performed using the criteria specified on the operation, if any, and
38
+ /// an implicit session will be created if the operation and write concern are compatible with
39
+ /// sessions.
40
+ pub ( crate ) async fn execute_operation < T : Operation > ( & self , op : T ) -> Result < T :: O > {
41
+ let mut implicit_session = self . start_implicit_session ( & op) . await ?;
42
+ self . select_server_and_execute_operation ( op, implicit_session. as_mut ( ) )
43
+ . await
44
+ }
45
+
46
+ /// Execute the given operation, returning the implicit session created for it if one was.
47
+ ///
48
+ /// Server selection be will performed using the criteria specified on the operation, if any.
49
+ pub ( crate ) async fn execute_cursor_operation < T : Operation > (
37
50
& self ,
38
- op : & T ,
39
- ) -> Result < ( T :: O , Connection ) > {
40
- let server = self . select_server ( op. selection_criteria ( ) ) . await ?;
41
- let mut conn = server. checkout_connection ( ) . await ?;
42
- self . execute_operation_on_connection ( op, & mut conn)
51
+ op : T ,
52
+ ) -> Result < ( T :: O , Option < ClientSession > ) > {
53
+ let mut implicit_session = self . start_implicit_session ( & op) . await ?;
54
+ self . select_server_and_execute_operation ( op, implicit_session. as_mut ( ) )
43
55
. await
44
- . map ( |r | ( r , conn ) )
56
+ . map ( |result | ( result , implicit_session ) )
45
57
}
46
58
47
- pub ( crate ) async fn execute_operation_owned < T : Operation > ( self , op : T ) -> Result < T :: O > {
48
- self . execute_operation ( & op, None ) . await
59
+ /// Execute the given operation with the given session.
60
+ /// Server selection will performed using the criteria specified on the operation, if any.
61
+ pub ( crate ) async fn execute_operation_with_session < T : Operation > (
62
+ & self ,
63
+ op : T ,
64
+ session : & mut ClientSession ,
65
+ ) -> Result < T :: O > {
66
+ self . select_server_and_execute_operation ( op, Some ( session) )
67
+ . await
49
68
}
50
69
51
- /// Execute the given operation, optionally specifying a connection used to do so.
52
- /// If no connection is provided, server selection will performed using the criteria specified
53
- /// on the operation, if any.
54
- pub ( crate ) async fn execute_operation < T : Operation > (
70
+ /// Selects a server and executes the given operation on it, optionally using a provided
71
+ /// session.
72
+ ///
73
+ /// TODO: RUST-128: replace this with `execute_operation_with_retry` when implemented.
74
+ async fn select_server_and_execute_operation < T : Operation > (
55
75
& self ,
56
- op : & T ,
57
- connection : Option < & mut Connection > ,
76
+ op : T ,
77
+ session : Option < & mut ClientSession > ,
58
78
) -> Result < T :: O > {
59
- // if no connection provided, select one.
60
- match connection {
61
- Some ( conn) => self . execute_operation_on_connection ( op, conn) . await ,
62
- None => {
63
- let server = self . select_server ( op. selection_criteria ( ) ) . await ?;
64
-
65
- let mut conn = match server. checkout_connection ( ) . await {
66
- Ok ( conn) => conn,
67
- Err ( err) => {
68
- self . inner
69
- . topology
70
- . handle_pre_handshake_error ( err. clone ( ) , server. address . clone ( ) )
71
- . await ;
72
- return Err ( err) ;
73
- }
74
- } ;
75
-
76
- match self . execute_operation_on_connection ( op, & mut conn) . await {
77
- Ok ( result) => Ok ( result) ,
78
- Err ( err) => {
79
- self . inner
80
- . topology
81
- . handle_post_handshake_error ( err. clone ( ) , conn, server)
82
- . await ;
83
- Err ( err)
84
- }
85
- }
79
+ let server = self . select_server ( op. selection_criteria ( ) ) . await ?;
80
+
81
+ let mut conn = match server. checkout_connection ( ) . await {
82
+ Ok ( conn) => conn,
83
+ Err ( err) => {
84
+ self . inner
85
+ . topology
86
+ . handle_pre_handshake_error ( err. clone ( ) , server. address . clone ( ) )
87
+ . await ;
88
+ return Err ( err) ;
89
+ }
90
+ } ;
91
+
92
+ match self
93
+ . execute_operation_on_connection ( op, & mut conn, session)
94
+ . await
95
+ {
96
+ Ok ( result) => Ok ( result) ,
97
+ Err ( err) => {
98
+ self . inner
99
+ . topology
100
+ . handle_post_handshake_error ( err. clone ( ) , conn, server)
101
+ . await ;
102
+ Err ( err)
86
103
}
87
104
}
88
105
}
89
106
90
- /// Executes an operation on a given connection.
107
+ /// Executes an operation on a given connection, optionally using a provided session .
91
108
async fn execute_operation_on_connection < T : Operation > (
92
109
& self ,
93
- op : & T ,
110
+ op : T ,
94
111
connection : & mut Connection ,
112
+ mut session : Option < & mut ClientSession > ,
95
113
) -> Result < T :: O > {
96
114
if let Some ( wc) = op. write_concern ( ) {
97
115
wc. validate ( ) ?;
@@ -103,6 +121,34 @@ impl Client {
103
121
. update_command_with_read_pref ( connection. address ( ) , & mut cmd, op. selection_criteria ( ) )
104
122
. await ;
105
123
124
+ match session {
125
+ Some ( ref mut session) if op. supports_sessions ( ) && op. is_acknowledged ( ) => {
126
+ cmd. set_session ( session) ;
127
+ session. update_last_use ( ) ;
128
+ }
129
+ Some ( ref session) if !op. supports_sessions ( ) && !session. is_implicit ( ) => {
130
+ return Err ( ErrorKind :: ArgumentError {
131
+ message : format ! ( "{} does not support sessions" , cmd. name) ,
132
+ }
133
+ . into ( ) ) ;
134
+ }
135
+ Some ( ref session) if !op. is_acknowledged ( ) && !session. is_implicit ( ) => {
136
+ return Err ( ErrorKind :: ArgumentError {
137
+ message : "Cannot use ClientSessions with unacknowledged write concern"
138
+ . to_string ( ) ,
139
+ }
140
+ . into ( ) ) ;
141
+ }
142
+ _ => { }
143
+ }
144
+
145
+ let session_cluster_time = session. as_ref ( ) . and_then ( |session| session. cluster_time ( ) ) ;
146
+ let client_cluster_time = self . inner . topology . cluster_time ( ) . await ;
147
+ let max_cluster_time = std:: cmp:: max ( session_cluster_time, client_cluster_time. as_ref ( ) ) ;
148
+ if let Some ( cluster_time) = max_cluster_time {
149
+ cmd. set_cluster_time ( cluster_time) ;
150
+ }
151
+
106
152
let connection_info = connection. info ( ) ;
107
153
let request_id = crate :: cmap:: conn:: next_request_id ( ) ;
108
154
@@ -127,15 +173,23 @@ impl Client {
127
173
128
174
let start_time = PreciseTime :: now ( ) ;
129
175
130
- let response_result = connection
131
- . send_command ( cmd. clone ( ) , request_id)
132
- . await
133
- . and_then ( |response| {
176
+ let response_result = match connection. send_command ( cmd. clone ( ) , request_id) . await {
177
+ Ok ( response) => {
178
+ if let Some ( cluster_time) = response. cluster_time ( ) {
179
+ self . inner . topology . advance_cluster_time ( cluster_time) . await ;
180
+ if let Some ( ref mut session) = session {
181
+ session. advance_cluster_time ( cluster_time)
182
+ }
183
+ }
184
+
134
185
if !op. handles_command_errors ( ) {
135
- response. validate ( ) ?;
186
+ response. validate ( ) . map ( |_| response)
187
+ } else {
188
+ Ok ( response)
136
189
}
137
- Ok ( response)
138
- } ) ;
190
+ }
191
+ err => err,
192
+ } ;
139
193
140
194
let end_time = PreciseTime :: now ( ) ;
141
195
let duration = start_time. to ( end_time) . to_std ( ) ?;
@@ -153,6 +207,13 @@ impl Client {
153
207
154
208
handler. handle_command_failed_event ( command_failed_event) ;
155
209
} ) ;
210
+
211
+ if let Some ( session) = session {
212
+ if error. is_network_error ( ) {
213
+ session. mark_dirty ( ) ;
214
+ }
215
+ }
216
+
156
217
Err ( error)
157
218
}
158
219
Ok ( response) => {
@@ -174,8 +235,42 @@ impl Client {
174
235
} ;
175
236
handler. handle_command_succeeded_event ( command_succeeded_event) ;
176
237
} ) ;
238
+
177
239
op. handle_response ( response)
178
240
}
179
241
}
180
242
}
243
+
244
+ /// Start an implicit session if the operation and write concern are compatible with sessions.
245
+ async fn start_implicit_session < T : Operation > ( & self , op : & T ) -> Result < Option < ClientSession > > {
246
+ match self . get_session_support_status ( ) . await ? {
247
+ SessionSupportStatus :: Supported {
248
+ logical_session_timeout,
249
+ } if op. supports_sessions ( ) && op. is_acknowledged ( ) => Ok ( Some (
250
+ self . start_implicit_session_with_timeout ( logical_session_timeout)
251
+ . await ,
252
+ ) ) ,
253
+ _ => Ok ( None ) ,
254
+ }
255
+ }
256
+
257
+ /// Gets whether the topology supports sessions, and if so, returns the topology's logical
258
+ /// session timeout. If it has yet to be determined if the topology supports sessions, this
259
+ /// method will perform a server selection that will force that determination to be made.
260
+ async fn get_session_support_status ( & self ) -> Result < SessionSupportStatus > {
261
+ let initial_status = self . inner . topology . session_support_status ( ) . await ;
262
+
263
+ // Need to guarantee that we're connected to at least one server that can determine if
264
+ // sessions are supported or not.
265
+ match initial_status {
266
+ SessionSupportStatus :: Undetermined => {
267
+ let criteria = SelectionCriteria :: Predicate ( Arc :: new ( move |server_info| {
268
+ server_info. server_type ( ) . is_data_bearing ( )
269
+ } ) ) ;
270
+ let _: Arc < Server > = self . select_server ( Some ( & criteria) ) . await ?;
271
+ Ok ( self . inner . topology . session_support_status ( ) . await )
272
+ }
273
+ _ => Ok ( initial_status) ,
274
+ }
275
+ }
181
276
}
0 commit comments