@@ -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
@@ -129,7 +130,9 @@ type ConnPool struct {
129130 dialErrorsNum uint32 // atomic
130131 lastDialError atomic.Value
131132
132- queue chan struct {}
133+ queue chan struct {}
134+ dialsInProgress chan struct {}
135+ dialsQueue * wantConnQueue
133136
134137 connsMu sync.Mutex
135138 conns map [uint64 ]* Conn
@@ -155,9 +158,11 @@ func NewConnPool(opt *Options) *ConnPool {
155158 p := & ConnPool {
156159 cfg : opt ,
157160
158- queue : make (chan struct {}, opt .PoolSize ),
159- conns : make (map [uint64 ]* Conn ),
160- idleConns : make ([]* Conn , 0 , opt .PoolSize ),
161+ queue : make (chan struct {}, opt .PoolSize ),
162+ conns : make (map [uint64 ]* Conn ),
163+ dialsInProgress : make (chan struct {}, opt .MaxConcurrentDials ),
164+ dialsQueue : newWantConnQueue (),
165+ idleConns : make ([]* Conn , 0 , opt .PoolSize ),
161166 }
162167
163168 // Only create MinIdleConns if explicitly requested (> 0)
@@ -236,6 +241,7 @@ func (p *ConnPool) checkMinIdleConns() {
236241 return
237242 }
238243 }
244+
239245}
240246
241247func (p * ConnPool ) addIdleConn () error {
@@ -494,9 +500,8 @@ func (p *ConnPool) getConn(ctx context.Context) (*Conn, error) {
494500
495501 atomic .AddUint32 (& p .stats .Misses , 1 )
496502
497- newcn , err := p .newConn (ctx , true )
503+ newcn , err := p .queuedNewConn (ctx )
498504 if err != nil {
499- p .freeTurn ()
500505 return nil , err
501506 }
502507
@@ -515,6 +520,99 @@ func (p *ConnPool) getConn(ctx context.Context) (*Conn, error) {
515520 return newcn , nil
516521}
517522
523+ func (p * ConnPool ) queuedNewConn (ctx context.Context ) (* Conn , error ) {
524+ select {
525+ case p .dialsInProgress <- struct {}{}:
526+ // Got permission, proceed to create connection
527+ case <- ctx .Done ():
528+ p .freeTurn ()
529+ return nil , ctx .Err ()
530+ }
531+
532+ dialCtx , cancel := context .WithTimeout (context .Background (), p .cfg .DialTimeout )
533+
534+ w := & wantConn {
535+ ctx : dialCtx ,
536+ cancelCtx : cancel ,
537+ result : make (chan wantConnResult , 1 ),
538+ }
539+ var err error
540+ defer func () {
541+ if err != nil {
542+ if cn := w .cancel (); cn != nil {
543+ p .putIdleConn (ctx , cn )
544+ p .freeTurn ()
545+ }
546+ }
547+ }()
548+
549+ p .dialsQueue .enqueue (w )
550+
551+ go func (w * wantConn ) {
552+ var freeTurnCalled bool
553+ defer func () {
554+ if err := recover (); err != nil {
555+ if ! freeTurnCalled {
556+ p .freeTurn ()
557+ }
558+ internal .Logger .Printf (context .Background (), "queuedNewConn panic: %+v" , err )
559+ }
560+ }()
561+
562+ defer w .cancelCtx ()
563+ defer func () { <- p .dialsInProgress }() // Release connection creation permission
564+
565+ dialCtx := w .getCtxForDial ()
566+ cn , cnErr := p .newConn (dialCtx , true )
567+ delivered := w .tryDeliver (cn , cnErr )
568+ if cnErr == nil && delivered {
569+ return
570+ } else if cnErr == nil && ! delivered {
571+ p .putIdleConn (dialCtx , cn )
572+ p .freeTurn ()
573+ freeTurnCalled = true
574+ } else {
575+ p .freeTurn ()
576+ freeTurnCalled = true
577+ }
578+ }(w )
579+
580+ select {
581+ case <- ctx .Done ():
582+ err = ctx .Err ()
583+ return nil , err
584+ case result := <- w .result :
585+ err = result .err
586+ return result .cn , err
587+ }
588+ }
589+
590+ func (p * ConnPool ) putIdleConn (ctx context.Context , cn * Conn ) {
591+ for {
592+ w , ok := p .dialsQueue .dequeue ()
593+ if ! ok {
594+ break
595+ }
596+ if w .tryDeliver (cn , nil ) {
597+ return
598+ }
599+ }
600+
601+ cn .SetUsable (true )
602+
603+ p .connsMu .Lock ()
604+ defer p .connsMu .Unlock ()
605+
606+ if p .closed () {
607+ _ = cn .Close ()
608+ return
609+ }
610+
611+ // poolSize is increased in newConn
612+ p .idleConns = append (p .idleConns , cn )
613+ p .idleConnsLen .Add (1 )
614+ }
615+
518616func (p * ConnPool ) waitTurn (ctx context.Context ) error {
519617 select {
520618 case <- ctx .Done ():
0 commit comments