16
16
checkout /4 ,
17
17
checkin /2 ]).
18
18
19
- -export ([start_pool /2 ,
19
+ -export ([
20
+ get_stats /1 ,
21
+ start_pool /2 ,
20
22
stop_pool /1 ,
21
23
find_pool /1 ,
22
- notify /2 ]).
24
+ notify /2
25
+ ]).
23
26
24
27
25
28
-export ([count /1 , count /2 ,
45
48
max_connections ,
46
49
timeout ,
47
50
clients = dict :new (),
48
- queues = dict :new (), % Dest => queue of Froms
51
+ queues = dict :new (), % Dest => queue of Froms,
52
+ pending = dict :new (),
49
53
connections = dict :new (),
50
54
sockets = dict :new (),
51
55
nb_waiters = 0 }).
@@ -99,6 +103,9 @@ checkin({_Name, Ref, Dest, Owner, Transport}, Socket) ->
99
103
ok
100
104
end .
101
105
106
+ get_stats (Pool ) ->
107
+ gen_server :call (find_pool (Pool ), stats ).
108
+
102
109
103
110
% % @doc start a pool
104
111
start_pool (Name , Options ) ->
@@ -235,6 +242,8 @@ init([Name, Options]) ->
235
242
{ok , # state {name = Name , metrics = Engine , max_connections = MaxConn ,
236
243
timeout = Timeout }}.
237
244
245
+ handle_call (stats , _From , State ) ->
246
+ {reply , handle_stats (State ), State };
238
247
handle_call (count , _From , # state {sockets = Sockets }= State ) ->
239
248
{reply , dict :size (Sockets ), State };
240
249
handle_call (timeout , _From , # state {timeout = Timeout }= State ) ->
@@ -247,7 +256,7 @@ handle_call({checkout, Dest, Pid, RequestRef}, From, State) ->
247
256
max_connections = MaxConn ,
248
257
clients = Clients ,
249
258
queues = Queues ,
250
- nb_waiters = NbWaiters } = State ,
259
+ pending = Pending } = State ,
251
260
252
261
{Reply , State2 } = find_connection (Dest , Pid , State ),
253
262
case Reply of
@@ -259,12 +268,11 @@ handle_call({checkout, Dest, Pid, RequestRef}, From, State) ->
259
268
case dict :size (Clients ) >= MaxConn of
260
269
true ->
261
270
Queues2 = add_to_queue (Dest , From , RequestRef , Queues ),
262
- NbWaiters2 = NbWaiters + 1 ,
263
- _ = metrics :update_histogram (Engine ,
264
- [hackney_pool , PoolName , queue_count ],
265
- NbWaiters2 ),
266
- {noreply , State2 # state {queues = Queues2 ,
267
- nb_waiters = NbWaiters2 }};
271
+ Pending2 = add_pending (RequestRef , From , Dest , Pending ),
272
+ _ = metrics :update_histogram (
273
+ Engine , [hackney_pool , PoolName , queue_count ], dict :size (Pending2 )
274
+ ),
275
+ {noreply , State2 # state {queues = Queues2 , pending = Pending2 }};
268
276
false ->
269
277
State3 = monitor_client (Dest , RequestRef , State2 ),
270
278
ok = update_usage (State3 ),
@@ -307,11 +315,12 @@ handle_cast({set_timeout, NewTimeout}, State) ->
307
315
{noreply , State # state {timeout = NewTimeout }};
308
316
309
317
handle_cast ({checkout_cancel , Dest , Ref }, State ) ->
310
- {Queues , Removed } = del_from_queue (Dest , Ref , State # state .queues ),
318
+ # state {queues = Queues , pending = Pending } = State ,
319
+ {Queues2 , Removed } = del_from_queue (Dest , Ref , Queues ),
311
320
case Removed of
312
321
true ->
313
- NbWaiters = State # state . nb_waiters - 1 ,
314
- {noreply , State # state {queues = Queues , nb_waiters = NbWaiters }};
322
+ Pending2 = del_pending ( Ref , Pending ) ,
323
+ {noreply , State # state {queues = Queues2 , nb_waiters = Pending2 }};
315
324
false ->
316
325
% we leak the socket here but 'DOWN' will mop up for us when it times out
317
326
{noreply , dequeue (Dest , Ref , State )}
@@ -338,7 +347,8 @@ handle_info({'DOWN', Ref, request, _Pid, _Reason}, State) ->
338
347
{ok , Dest } ->
339
348
{noreply , dequeue (Dest , Ref , State )};
340
349
error ->
341
- {noreply , State }
350
+ NewState = remove_pending (Ref , State ),
351
+ {noreply , NewState }
342
352
end ;
343
353
handle_info (_ , State ) ->
344
354
{noreply , State }.
@@ -360,17 +370,18 @@ terminate(_Reason, #state{name=PoolName, metrics=Engine, sockets=Sockets}) ->
360
370
% % internals
361
371
362
372
dequeue (Dest , Ref , State ) ->
363
- Clients2 = dict :erase (Ref , State # state .clients ),
364
- case queue_out (Dest , State # state .queues ) of
373
+ # state {clients = Clients , queues = Queues , pending = Pending } = State ,
374
+ Clients2 = dict :erase (Ref , Clients ),
375
+ case queue_out (Dest , Queues ) of
365
376
empty ->
366
377
State # state {clients = Clients2 };
367
378
{ok , {From , Ref2 }, Queues2 } ->
368
- NbWaiters = State # state .nb_waiters - 1 ,
369
- _ = metrics :update_histogram (State # state .metrics ,
370
- [hackney_pool , State # state .name , queue_count ], NbWaiters ),
379
+ Pending2 = del_pending (Ref , Pending ),
380
+ _ = metrics :update_histogram (
381
+ State # state .metrics , [hackney_pool , State # state .name , queue_count ], dict :size (Pending2 )
382
+ ),
371
383
gen_server :reply (From , {error , no_socket , self ()}),
372
- State2 = State # state {queues = Queues2 , clients = Clients2 ,
373
- nb_waiters = NbWaiters },
384
+ State2 = State # state {queues = Queues2 , clients = Clients2 , pending = Pending2 },
374
385
monitor_client (Dest , Ref2 , State2 )
375
386
end .
376
387
@@ -387,8 +398,7 @@ find_connection({_Host, _Port, Transport}=Dest, Pid,
387
398
cancel_timer (S , Timer ),
388
399
NewConns = update_connections (Rest , Dest , Conns ),
389
400
NewSockets = dict :erase (S , Sockets ),
390
- NewState = State # state {connections = NewConns ,
391
- sockets = NewSockets },
401
+ NewState = State # state {connections = NewConns , sockets = NewSockets },
392
402
{{ok , S , self ()}, NewState };
393
403
{error , badarg } ->
394
404
% % something happened here normally the PID died,
@@ -509,26 +519,25 @@ queue_out({_Host, _Port, _Transport} = Dest, Queues) ->
509
519
% % @private
510
520
% %------------------------------------------------------------------------------
511
521
deliver_socket (Socket , {_ , _ , Transport } = Dest , State ) ->
512
- case queue_out (Dest , State # state .queues ) of
522
+ # state {queues = Queues , pending = Pending } = State ,
523
+ case queue_out (Dest , Queues ) of
513
524
empty ->
514
525
store_socket (Dest , Socket , State );
515
526
{ok , {{PidWaiter , _ } = FromWaiter , Ref }, Queues2 } ->
516
- NbWaiters = State # state . nb_waiters - 1 ,
517
- _ = metrics :update_histogram (State # state . metrics ,
518
- [hackney_pool , State # state .name , queue_count ],
519
- NbWaiters ),
527
+ Pending2 = del_pending ( Ref , Pending ) ,
528
+ _ = metrics :update_histogram (
529
+ State # state . metrics , [hackney_pool , State # state .name , queue_count ], dict : size ( Pending2 )
530
+ ),
520
531
case Transport :controlling_process (Socket , PidWaiter ) of
521
532
ok ->
522
533
gen_server :reply (FromWaiter , {ok , Socket , self ()}),
523
- monitor_client (Dest , Ref ,
524
- State # state {queues = Queues2 ,
525
- nb_waiters = NbWaiters });
534
+ monitor_client (Dest , Ref , State # state {queues = Queues2 , pending = Pending2 });
526
535
_Error ->
527
536
% Something wrong, close the socket
528
- catch Transport :close (Socket ),
537
+ _ = ( catch Transport :close (Socket ) ),
529
538
% % and let the waiter connect to a new one
530
539
gen_server :reply (FromWaiter , {error , no_socket , self ()}),
531
- State # state {queues = Queues2 , nb_waiters = NbWaiters }
540
+ State # state {queues = Queues2 , pending = Pending2 }
532
541
end
533
542
end .
534
543
@@ -544,6 +553,37 @@ sync_socket(Transport, Socket) ->
544
553
true
545
554
end .
546
555
556
+
557
+ add_pending (Ref , From , Dest , Pending ) ->
558
+ dict :store (Ref , {From , Dest }, Pending ).
559
+
560
+ del_pending (Ref , Pending ) ->
561
+ dict :erase (Ref , Pending ).
562
+
563
+ remove_pending (Ref , # state {queues = Queues0 , pending = Pending0 } = State ) ->
564
+ case dict :find (Ref , Pending0 ) of
565
+ {ok , {From , Dest }} ->
566
+ Pending1 = dict :erase (Ref , Pending0 ),
567
+ Queues1 = case dict :find (Dest , Queues0 ) of
568
+ {ok , Q0 } ->
569
+ Q1 = queue :filter (
570
+ fun
571
+ (PendingReq ) when PendingReq =:= {From , Ref } -> false ;
572
+ (_ ) -> true
573
+ end ,
574
+ Q0
575
+ ),
576
+ dict :store (Dest , Q1 , Queues0 );
577
+ error ->
578
+ Queues0
579
+ end ,
580
+ State # state {queues = Queues1 , pending = Pending1 };
581
+ error ->
582
+ State
583
+ end .
584
+
585
+
586
+
547
587
% ------------------------------------------------------------------------------
548
588
% % @private
549
589
% %------------------------------------------------------------------------------
@@ -581,3 +621,12 @@ update_usage(
581
621
_ = metrics :update_histogram (Engine , [hackney_pool , PoolName , free_count ],
582
622
dict :size (Sockets ) - 1 ),
583
623
ok .
624
+
625
+
626
+ handle_stats (State ) ->
627
+ # state {name = PoolName , max_connections = Max , sockets = Sockets , clients = Clients , pending = Pending } = State ,
628
+ [{name , PoolName },
629
+ {max , Max },
630
+ {in_use_count , dict :size (Clients )},
631
+ {free_count , dict :size (Sockets )},
632
+ {queue_count , dict :size (Pending )}].
0 commit comments