4
4
"context"
5
5
"errors"
6
6
"net"
7
+ "runtime"
8
+ "sync"
7
9
"sync/atomic"
8
10
"testing"
9
11
"time"
@@ -119,6 +121,8 @@ func TestPool(t *testing.T) {
119
121
t .Errorf ("Should have closed 3 connections, but didn't. got %d; want %d" , d .lenclosed (), 3 )
120
122
}
121
123
close (cleanup )
124
+ err = conns [2 ].Close ()
125
+ noerr (t , err )
122
126
ok := p .(* pool ).sem .TryAcquire (int64 (p .(* pool ).capacity ))
123
127
if ! ok {
124
128
t .Errorf ("clean shutdown should acquire and release semaphore, but semaphore still held" )
@@ -478,6 +482,9 @@ func TestPool(t *testing.T) {
478
482
err = p .Drain ()
479
483
noerr (t , err )
480
484
485
+ err = conns [1 ].Close ()
486
+ noerr (t , err )
487
+
481
488
ctx , cancel = context .WithTimeout (context .Background (), 10 * time .Millisecond )
482
489
defer cancel ()
483
490
c , _ , err = p .Get (ctx )
@@ -487,6 +494,129 @@ func TestPool(t *testing.T) {
487
494
}
488
495
close (cleanup )
489
496
})
497
+ t .Run ("Cannot starve connection request" , func (t * testing.T ) {
498
+ cleanup := make (chan struct {})
499
+ address := bootstrapConnections (t , 3 , func (nc net.Conn ) {
500
+ <- cleanup
501
+ nc .Close ()
502
+ })
503
+ d := newdialer (& net.Dialer {})
504
+ p , err := NewPool (addr .Addr (address .String ()), 1 , 1 , WithDialer (func (Dialer ) Dialer { return d }))
505
+ noerr (t , err )
506
+ err = p .Connect (context .Background ())
507
+ noerr (t , err )
508
+ conn , _ , err := p .Get (context .Background ())
509
+ if d .lenopened () != 1 {
510
+ t .Errorf ("Should have opened 1 connections, but didn't. got %d; want %d" , d .lenopened (), 1 )
511
+ }
512
+
513
+ var wg sync.WaitGroup
514
+
515
+ wg .Add (1 )
516
+ ch := make (chan struct {})
517
+ go func () {
518
+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Second )
519
+ defer cancel ()
520
+ ch <- struct {}{}
521
+ _ , _ , err := p .Get (ctx )
522
+ if err != nil {
523
+ t .Errorf ("Should not be able to starve connection request, but got error: %v" , err )
524
+ }
525
+ wg .Done ()
526
+ }()
527
+ <- ch
528
+ runtime .Gosched ()
529
+ err = conn .Close ()
530
+ noerr (t , err )
531
+ wg .Wait ()
532
+ close (cleanup )
533
+ })
534
+ t .Run ("Does not leak permit from failure to dial connection" , func (t * testing.T ) {
535
+ cleanup := make (chan struct {})
536
+ address := bootstrapConnections (t , 0 , func (nc net.Conn ) {
537
+ <- cleanup
538
+ nc .Close ()
539
+ })
540
+ close (cleanup )
541
+ want := errors .New ("dialing error" )
542
+ p , err := NewPool (
543
+ addr .Addr (address .String ()), 1 , 2 ,
544
+ WithDialer (
545
+ func (Dialer ) Dialer {
546
+ return DialerFunc (func (ctx context.Context , network , address string ) (net.Conn , error ) {
547
+ return nil , want
548
+ })
549
+ }),
550
+ )
551
+ noerr (t , err )
552
+ err = p .Connect (context .Background ())
553
+ noerr (t , err )
554
+ _ , _ , err = p .Get (context .Background ())
555
+ if err != want {
556
+ t .Errorf ("Expected dial failure but got: %v" , err )
557
+ }
558
+ ok := p .(* pool ).sem .TryAcquire (int64 (p .(* pool ).capacity ))
559
+ if ! ok {
560
+ t .Errorf ("Dial failure should not leak semaphore permit" )
561
+ } else {
562
+ p .(* pool ).sem .Release (int64 (p .(* pool ).capacity ))
563
+ }
564
+ })
565
+ t .Run ("Does not leak permit from cancelled context" , func (t * testing.T ) {
566
+ cleanup := make (chan struct {})
567
+ address := bootstrapConnections (t , 1 , func (nc net.Conn ) {
568
+ <- cleanup
569
+ nc .Close ()
570
+ })
571
+ close (cleanup )
572
+ d := newdialer (& net.Dialer {})
573
+ p , err := NewPool (addr .Addr (address .String ()), 1 , 2 , WithDialer (func (Dialer ) Dialer { return d }))
574
+ noerr (t , err )
575
+ err = p .Connect (context .Background ())
576
+ noerr (t , err )
577
+ ctx , cancel := context .WithCancel (context .Background ())
578
+ cancel ()
579
+ _ , _ , err = p .Get (ctx )
580
+ if err != context .Canceled {
581
+ t .Errorf ("Expected context cancelled error. got %v; want %v" , err , context .Canceled )
582
+ }
583
+ ok := p .(* pool ).sem .TryAcquire (int64 (p .(* pool ).capacity ))
584
+ if ! ok {
585
+ t .Errorf ("Canceled context should not leak semaphore permit" )
586
+ } else {
587
+ p .(* pool ).sem .Release (int64 (p .(* pool ).capacity ))
588
+ }
589
+ })
590
+ t .Run ("Get does not acquire multiple permits" , func (t * testing.T ) {
591
+ cleanup := make (chan struct {})
592
+ address := bootstrapConnections (t , 2 , func (nc net.Conn ) {
593
+ <- cleanup
594
+ nc .Close ()
595
+ })
596
+ close (cleanup )
597
+ d := newdialer (& net.Dialer {})
598
+ p , err := NewPool (addr .Addr (address .String ()), 1 , 2 , WithDialer (func (Dialer ) Dialer { return d }))
599
+ noerr (t , err )
600
+ err = p .Connect (context .Background ())
601
+ noerr (t , err )
602
+ c , _ , err := p .Get (context .Background ())
603
+ noerr (t , err )
604
+ err = c .Close ()
605
+ noerr (t , err )
606
+
607
+ p .Drain ()
608
+
609
+ c , _ , err = p .Get (context .Background ())
610
+ noerr (t , err )
611
+ err = c .Close ()
612
+ noerr (t , err )
613
+ ok := p .(* pool ).sem .TryAcquire (int64 (p .(* pool ).capacity ))
614
+ if ! ok {
615
+ t .Errorf ("Get should not acquire multiple permits (when expired conn in idle pool)" )
616
+ } else {
617
+ p .(* pool ).sem .Release (int64 (p .(* pool ).capacity ))
618
+ }
619
+ })
490
620
})
491
621
t .Run ("Connection" , func (t * testing.T ) {
492
622
t .Run ("Connection Close Does Not Error After Pool Is Disconnected" , func (t * testing.T ) {
0 commit comments