@@ -3,17 +3,16 @@ pub mod cli;
33use std:: {
44 fmt,
55 sync:: {
6+ Arc ,
67 atomic:: { AtomicUsize , Ordering } ,
7- Arc , RwLock , RwLockWriteGuard ,
88 } ,
9- time:: { Duration , Instant } ,
9+ time:: Duration ,
1010} ;
1111
1212use anyhow:: Context ;
1313use async_trait:: async_trait;
1414use http:: header:: HeaderValue ;
15- use rand:: { rngs:: OsRng , seq:: IteratorRandom } ;
16- use reqwest:: { dns:: Resolve , Request , Response } ;
15+ use reqwest:: { Request , Response , dns:: Resolve } ;
1716use scopeguard:: defer;
1817
1918use super :: Error ;
@@ -160,6 +159,7 @@ impl ReqwestClientLeastLoaded {
160159#[ async_trait]
161160impl Client for ReqwestClientLeastLoaded {
162161 async fn execute ( & self , req : Request ) -> Result < Response , reqwest:: Error > {
162+ // Select the client with least outstanding requests
163163 let cli = self
164164 . inner
165165 . iter ( )
@@ -176,14 +176,6 @@ impl Client for ReqwestClientLeastLoaded {
176176 }
177177}
178178
179- pub trait GeneratesClients : Send + Sync + fmt:: Debug + ' static {
180- fn generate ( & self ) -> Result < Arc < dyn Client > , Error > ;
181- }
182-
183- pub trait GeneratesClientsWithStats : Send + Sync + fmt:: Debug + ' static {
184- fn generate ( & self ) -> Result < Arc < dyn ClientWithStats > , Error > ;
185- }
186-
187179#[ derive( Debug , Clone ) ]
188180pub struct ClientStats {
189181 pub pool_size : usize ,
@@ -194,138 +186,6 @@ pub trait Stats {
194186 fn stats ( & self ) -> ClientStats ;
195187}
196188
197- #[ derive( Debug ) ]
198- pub struct ReqwestClientDynamic < G : GeneratesClients > {
199- generator : G ,
200- min_clients : usize ,
201- max_clients : usize ,
202- max_outstanding : usize ,
203- idle_timeout : Duration ,
204- pool : RwLock < Vec < Arc < ReqwestClientDynamicInner > > > ,
205- }
206-
207- impl < G : GeneratesClients > ClientWithStats for ReqwestClientDynamic < G > {
208- fn to_client ( self : Arc < Self > ) -> Arc < dyn Client > {
209- self
210- }
211- }
212-
213- #[ derive( Debug ) ]
214- struct ReqwestClientDynamicInner {
215- cli : Arc < dyn Client > ,
216- outstanding : AtomicUsize ,
217- last_request : RwLock < Instant > ,
218- }
219-
220- impl ReqwestClientDynamicInner {
221- fn new ( cli : Arc < dyn Client > ) -> Self {
222- Self {
223- cli,
224- outstanding : AtomicUsize :: new ( 0 ) ,
225- last_request : RwLock :: new ( Instant :: now ( ) ) ,
226- }
227- }
228- }
229-
230- impl < G : GeneratesClients > ReqwestClientDynamic < G > {
231- pub fn new (
232- generator : G ,
233- min_clients : usize ,
234- max_clients : usize ,
235- max_outstanding : usize ,
236- idle_timeout : Duration ,
237- ) -> Result < Self , Error > {
238- let mut pool = Vec :: with_capacity ( max_clients) ;
239-
240- for _ in 0 ..min_clients {
241- let inner = Arc :: new ( ReqwestClientDynamicInner :: new ( generator. generate ( ) ?) ) ;
242- pool. push ( inner) ;
243- }
244-
245- Ok ( Self {
246- generator,
247- min_clients,
248- max_clients,
249- max_outstanding,
250- idle_timeout,
251- pool : RwLock :: new ( pool) ,
252- } )
253- }
254-
255- /// Drop unused clients while leaving min_clients always available.
256- /// Algo mimics Vec::retain().
257- /// TODO find a better way?
258- fn cleanup ( & self , pool : & mut RwLockWriteGuard < ' _ , Vec < Arc < ReqwestClientDynamicInner > > > ) {
259- let mut j = self . min_clients ;
260- for i in self . min_clients ..pool. len ( ) {
261- if !( pool[ i] . outstanding . load ( Ordering :: SeqCst ) == 0
262- && pool[ i] . last_request . read ( ) . unwrap ( ) . elapsed ( ) > self . idle_timeout )
263- {
264- pool. swap ( i, j) ;
265- j += 1
266- }
267- }
268- pool. truncate ( j) ;
269- }
270-
271- fn get_client ( & self ) -> Arc < ReqwestClientDynamicInner > {
272- let mut pool = self . pool . write ( ) . unwrap ( ) ;
273- self . cleanup ( & mut pool) ;
274-
275- pool. iter ( )
276- // First try to find an existing client with spare capacity
277- . find_map ( |x| {
278- ( x. outstanding . load ( Ordering :: SeqCst ) < self . max_outstanding ) . then ( || x. clone ( ) )
279- } )
280- // Otherwise see if we have spare space in the pool
281- . unwrap_or_else ( || {
282- // If not - just pick a random client
283- if pool. len ( ) >= self . max_clients {
284- pool. iter ( ) . choose ( & mut OsRng ) . unwrap ( ) . clone ( )
285- } else {
286- // Otherwise generate a new client and use it
287- // The error is checked only in new() for now.
288- let cli = self . generator . generate ( ) . unwrap ( ) ;
289- let inner = Arc :: new ( ReqwestClientDynamicInner :: new ( cli) ) ;
290- pool. push ( inner. clone ( ) ) ;
291- inner
292- }
293- } )
294- }
295- }
296-
297- impl < G : GeneratesClients > Stats for ReqwestClientDynamic < G > {
298- fn stats ( & self ) -> ClientStats {
299- let pool = self . pool . read ( ) . unwrap ( ) ;
300-
301- let outstanding: usize = pool
302- . iter ( )
303- . map ( |x| x. outstanding . load ( Ordering :: SeqCst ) )
304- . sum ( ) ;
305-
306- ClientStats {
307- pool_size : pool. len ( ) ,
308- outstanding,
309- }
310- }
311- }
312-
313- #[ async_trait]
314- impl < G : GeneratesClients > Client for ReqwestClientDynamic < G > {
315- async fn execute ( & self , req : Request ) -> Result < Response , reqwest:: Error > {
316- let inner = self . get_client ( ) ;
317-
318- // The future can be cancelled so we have to use defer to make sure the counter is decreased
319- defer ! {
320- inner. outstanding. fetch_sub( 1 , Ordering :: SeqCst ) ;
321- }
322-
323- * inner. last_request . write ( ) . unwrap ( ) = Instant :: now ( ) ;
324- inner. outstanding . fetch_add ( 1 , Ordering :: SeqCst ) ;
325- inner. cli . execute ( req) . await
326- }
327- }
328-
329189pub fn basic_auth < U , P > ( username : U , password : Option < P > ) -> HeaderValue
330190where
331191 U : fmt:: Display ,
@@ -348,56 +208,3 @@ where
348208 header. set_sensitive ( true ) ;
349209 header
350210}
351-
352- #[ cfg( test) ]
353- mod test {
354- use futures:: future:: join_all;
355-
356- use super :: * ;
357-
358- #[ derive( Debug ) ]
359- struct TestClient ;
360-
361- #[ async_trait]
362- impl Client for TestClient {
363- async fn execute ( & self , _req : Request ) -> Result < Response , reqwest:: Error > {
364- let resp = http:: Response :: new ( vec ! [ ] ) ;
365- tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
366- Ok ( resp. into ( ) )
367- }
368- }
369-
370- #[ derive( Debug ) ]
371- struct TestClientGenerator ;
372- impl GeneratesClients for TestClientGenerator {
373- fn generate ( & self ) -> Result < Arc < dyn Client > , Error > {
374- Ok ( Arc :: new ( TestClient ) )
375- }
376- }
377-
378- #[ tokio:: test]
379- async fn test_dynamic_client ( ) {
380- let cli = Arc :: new (
381- ReqwestClientDynamic :: new ( TestClientGenerator , 1 , 10 , 10 , Duration :: ZERO ) . unwrap ( ) ,
382- ) ;
383-
384- let mut futs = vec ! [ ] ;
385- for _ in 0 ..200 {
386- let req = Request :: new ( reqwest:: Method :: GET , url:: Url :: parse ( "http://foo" ) . unwrap ( ) ) ;
387-
388- let cli = cli. clone ( ) ;
389- futs. push ( async move { cli. execute ( req) . await } ) ;
390- }
391-
392- join_all ( futs) . await ;
393- let mut pool = cli. pool . write ( ) . unwrap ( ) ;
394- assert_eq ! ( pool. len( ) , 10 ) ;
395-
396- for x in pool. iter ( ) {
397- assert_eq ! ( x. outstanding. load( Ordering :: SeqCst ) , 0 ) ;
398- }
399-
400- cli. cleanup ( & mut pool) ;
401- assert_eq ! ( pool. len( ) , 1 ) ;
402- }
403- }
0 commit comments