@@ -98,6 +98,7 @@ type Options struct {
9898
9999 PoolFIFO bool
100100 PoolSize int32
101+ MaxConcurrentDials int
101102 DialTimeout time.Duration
102103 PoolTimeout time.Duration
103104 MinIdleConns int32
@@ -126,7 +127,9 @@ type ConnPool struct {
126127 dialErrorsNum uint32 // atomic
127128 lastDialError atomic.Value
128129
129- queue chan struct {}
130+ queue chan struct {}
131+ dialsInProgress chan struct {}
132+ dialsQueue * wantConnQueue
130133
131134 connsMu sync.Mutex
132135 conns map [uint64 ]* Conn
@@ -152,9 +155,11 @@ func NewConnPool(opt *Options) *ConnPool {
152155 p := & ConnPool {
153156 cfg : opt ,
154157
155- queue : make (chan struct {}, opt .PoolSize ),
156- conns : make (map [uint64 ]* Conn ),
157- idleConns : make ([]* Conn , 0 , opt .PoolSize ),
158+ queue : make (chan struct {}, opt .PoolSize ),
159+ conns : make (map [uint64 ]* Conn ),
160+ dialsInProgress : make (chan struct {}, opt .MaxConcurrentDials ),
161+ dialsQueue : newWantConnQueue (),
162+ idleConns : make ([]* Conn , 0 , opt .PoolSize ),
158163 }
159164
160165 // Only create MinIdleConns if explicitly requested (> 0)
@@ -233,6 +238,7 @@ func (p *ConnPool) checkMinIdleConns() {
233238 return
234239 }
235240 }
241+
236242}
237243
238244func (p * ConnPool ) addIdleConn () error {
@@ -491,9 +497,8 @@ func (p *ConnPool) getConn(ctx context.Context) (*Conn, error) {
491497
492498 atomic .AddUint32 (& p .stats .Misses , 1 )
493499
494- newcn , err := p .newConn (ctx , true )
500+ newcn , err := p .queuedNewConn (ctx )
495501 if err != nil {
496- p .freeTurn ()
497502 return nil , err
498503 }
499504
@@ -512,6 +517,99 @@ func (p *ConnPool) getConn(ctx context.Context) (*Conn, error) {
512517 return newcn , nil
513518}
514519
520+ func (p * ConnPool ) queuedNewConn (ctx context.Context ) (* Conn , error ) {
521+ select {
522+ case p .dialsInProgress <- struct {}{}:
523+ // Got permission, proceed to create connection
524+ case <- ctx .Done ():
525+ p .freeTurn ()
526+ return nil , ctx .Err ()
527+ }
528+
529+ dialCtx , cancel := context .WithTimeout (context .Background (), p .cfg .DialTimeout )
530+
531+ w := & wantConn {
532+ ctx : dialCtx ,
533+ cancelCtx : cancel ,
534+ result : make (chan wantConnResult , 1 ),
535+ }
536+ var err error
537+ defer func () {
538+ if err != nil {
539+ if cn := w .cancel (); cn != nil {
540+ p .putIdleConn (ctx , cn )
541+ p .freeTurn ()
542+ }
543+ }
544+ }()
545+
546+ p .dialsQueue .enqueue (w )
547+
548+ go func (w * wantConn ) {
549+ var freeTurnCalled bool
550+ defer func () {
551+ if err := recover (); err != nil {
552+ if ! freeTurnCalled {
553+ p .freeTurn ()
554+ }
555+ internal .Logger .Printf (context .Background (), "queuedNewConn panic: %+v" , err )
556+ }
557+ }()
558+
559+ defer w .cancelCtx ()
560+ defer func () { <- p .dialsInProgress }() // Release connection creation permission
561+
562+ dialCtx := w .getCtxForDial ()
563+ cn , cnErr := p .newConn (dialCtx , true )
564+ delivered := w .tryDeliver (cn , cnErr )
565+ if cnErr == nil && delivered {
566+ return
567+ } else if cnErr == nil && ! delivered {
568+ p .putIdleConn (dialCtx , cn )
569+ p .freeTurn ()
570+ freeTurnCalled = true
571+ } else {
572+ p .freeTurn ()
573+ freeTurnCalled = true
574+ }
575+ }(w )
576+
577+ select {
578+ case <- ctx .Done ():
579+ err = ctx .Err ()
580+ return nil , err
581+ case result := <- w .result :
582+ err = result .err
583+ return result .cn , err
584+ }
585+ }
586+
587+ func (p * ConnPool ) putIdleConn (ctx context.Context , cn * Conn ) {
588+ for {
589+ w , ok := p .dialsQueue .dequeue ()
590+ if ! ok {
591+ break
592+ }
593+ if w .tryDeliver (cn , nil ) {
594+ return
595+ }
596+ }
597+
598+ cn .SetUsable (true )
599+
600+ p .connsMu .Lock ()
601+ defer p .connsMu .Unlock ()
602+
603+ if p .closed () {
604+ _ = cn .Close ()
605+ return
606+ }
607+
608+ // poolSize is increased in newConn
609+ p .idleConns = append (p .idleConns , cn )
610+ p .idleConnsLen .Add (1 )
611+ }
612+
515613func (p * ConnPool ) waitTurn (ctx context.Context ) error {
516614 select {
517615 case <- ctx .Done ():
0 commit comments