@@ -7,7 +7,10 @@ pub mod options;
7
7
pub mod session;
8
8
9
9
use std:: {
10
- sync:: { atomic:: AtomicBool , Mutex as SyncMutex } ,
10
+ sync:: {
11
+ atomic:: { AtomicBool , Ordering } ,
12
+ Mutex as SyncMutex ,
13
+ } ,
11
14
time:: { Duration , Instant } ,
12
15
} ;
13
16
@@ -26,13 +29,18 @@ use crate::trace::{
26
29
COMMAND_TRACING_EVENT_TARGET ,
27
30
} ;
28
31
use crate :: {
32
+ bson:: doc,
29
33
concern:: { ReadConcern , WriteConcern } ,
30
34
db:: Database ,
31
35
error:: { Error , ErrorKind , Result } ,
32
36
event:: command:: CommandEvent ,
33
37
id_set:: IdSet ,
34
38
options:: { ClientOptions , DatabaseOptions , ReadPreference , SelectionCriteria , ServerAddress } ,
35
- sdam:: { server_selection, SelectedServer , Topology } ,
39
+ sdam:: {
40
+ server_selection:: { self , attempt_to_select_server} ,
41
+ SelectedServer ,
42
+ Topology ,
43
+ } ,
36
44
tracking_arc:: TrackingArc ,
37
45
BoxFuture ,
38
46
ClientSession ,
@@ -123,6 +131,7 @@ struct ClientInner {
123
131
options : ClientOptions ,
124
132
session_pool : ServerSessionPool ,
125
133
shutdown : Shutdown ,
134
+ dropped : AtomicBool ,
126
135
#[ cfg( feature = "in-use-encryption" ) ]
127
136
csfle : tokio:: sync:: RwLock < Option < csfle:: ClientState > > ,
128
137
#[ cfg( test) ]
@@ -159,6 +168,7 @@ impl Client {
159
168
pending_drops : SyncMutex :: new ( IdSet :: new ( ) ) ,
160
169
executed : AtomicBool :: new ( false ) ,
161
170
} ,
171
+ dropped : AtomicBool :: new ( false ) ,
162
172
#[ cfg( feature = "in-use-encryption" ) ]
163
173
csfle : Default :: default ( ) ,
164
174
#[ cfg( test) ]
@@ -591,6 +601,40 @@ impl Client {
591
601
pub ( crate ) fn options ( & self ) -> & ClientOptions {
592
602
& self . inner . options
593
603
}
604
+
605
+ /// Ends all sessions contained in this client's session pool on the server.
606
+ pub ( crate ) async fn end_all_sessions ( & self ) {
607
+ // The maximum number of session IDs that should be sent in a single endSessions command.
608
+ const MAX_END_SESSIONS_BATCH_SIZE : usize = 10_000 ;
609
+
610
+ let mut watcher = self . inner . topology . watch ( ) ;
611
+ let selection_criteria =
612
+ SelectionCriteria :: from ( ReadPreference :: PrimaryPreferred { options : None } ) ;
613
+
614
+ let session_ids = self . inner . session_pool . get_session_ids ( ) . await ;
615
+ for chunk in session_ids. chunks ( MAX_END_SESSIONS_BATCH_SIZE ) {
616
+ let state = watcher. observe_latest ( ) ;
617
+ let Ok ( Some ( _) ) = attempt_to_select_server (
618
+ & selection_criteria,
619
+ & state. description ,
620
+ & state. servers ( ) ,
621
+ None ,
622
+ ) else {
623
+ // If a suitable server is not available, do not proceed with the operation to avoid
624
+ // spinning for server_selection_timeout.
625
+ return ;
626
+ } ;
627
+
628
+ let end_sessions = doc ! {
629
+ "endSessions" : chunk,
630
+ } ;
631
+ let _ = self
632
+ . database ( "admin" )
633
+ . run_command ( end_sessions)
634
+ . selection_criteria ( selection_criteria. clone ( ) )
635
+ . await ;
636
+ }
637
+ }
594
638
}
595
639
596
640
#[ derive( Clone , Debug ) ]
@@ -625,3 +669,24 @@ impl AsyncDropToken {
625
669
Self { tx : self . tx . take ( ) }
626
670
}
627
671
}
672
+
673
+ impl Drop for Client {
674
+ fn drop ( & mut self ) {
675
+ if !self . inner . shutdown . executed . load ( Ordering :: SeqCst )
676
+ && !self . inner . dropped . load ( Ordering :: SeqCst )
677
+ && TrackingArc :: strong_count ( & self . inner ) == 1
678
+ {
679
+ // We need an owned copy of the client to move into the spawned future. However, if this
680
+ // call to drop completes before the spawned future completes, the number of strong
681
+ // references to the inner client will again be 1 when the cloned client drops, and thus
682
+ // end_all_sessions will be called continuously until the runtime shuts down. Storing a
683
+ // flag indicating whether end_all_sessions has already been called breaks
684
+ // this cycle.
685
+ self . inner . dropped . store ( true , Ordering :: SeqCst ) ;
686
+ let client = self . clone ( ) ;
687
+ crate :: runtime:: spawn ( async move {
688
+ client. end_all_sessions ( ) . await ;
689
+ } ) ;
690
+ }
691
+ }
692
+ }
0 commit comments