@@ -381,10 +381,11 @@ func (opts Opts) Clone() Opts {
381
381
// - If opts.Reconnect is zero (default), then connection either already connected
382
382
// or error is returned.
383
383
//
384
- // - If opts.Reconnect is non-zero, then error will be returned only if authorization
385
- // fails. But if Tarantool is not reachable, then it will make an attempt to reconnect later
386
- // and will not finish to make attempts on authorization failures.
387
- func Connect (addr string , opts Opts ) (conn * Connection , err error ) {
384
+ // - If opts.Reconnect is non-zero, then error will be returned if authorization
385
+ // fails, or user has canceled context. If Tarantool is not reachable, then it
386
+ // will make attempts to reconnect and will not finish to make attempts on
387
+ // authorization failures.
388
+ func Connect (ctx context.Context , addr string , opts Opts ) (conn * Connection , err error ) {
388
389
conn = & Connection {
389
390
addr : addr ,
390
391
requestId : 0 ,
@@ -432,25 +433,8 @@ func Connect(addr string, opts Opts) (conn *Connection, err error) {
432
433
433
434
conn .cond = sync .NewCond (& conn .mutex )
434
435
435
- if err = conn .createConnection (false ); err != nil {
436
- ter , ok := err .(Error )
437
- if conn .opts .Reconnect <= 0 {
438
- return nil , err
439
- } else if ok && (ter .Code == iproto .ER_NO_SUCH_USER ||
440
- ter .Code == iproto .ER_CREDS_MISMATCH ) {
441
- // Reported auth errors immediately.
442
- return nil , err
443
- } else {
444
- // Without SkipSchema it is useless.
445
- go func (conn * Connection ) {
446
- conn .mutex .Lock ()
447
- defer conn .mutex .Unlock ()
448
- if err := conn .createConnection (true ); err != nil {
449
- conn .closeConnection (err , true )
450
- }
451
- }(conn )
452
- err = nil
453
- }
436
+ if err = conn .createConnection (ctx , false ); err != nil {
437
+ return nil , err
454
438
}
455
439
456
440
go conn .pinger ()
@@ -534,18 +518,19 @@ func (conn *Connection) cancelFuture(fut *Future, err error) {
534
518
}
535
519
}
536
520
537
- func (conn * Connection ) dial () (err error ) {
521
+ func (conn * Connection ) dial (ctx context. Context ) (err error ) {
538
522
opts := conn .opts
539
523
dialTimeout := opts .Reconnect / 2
540
524
if dialTimeout == 0 {
541
525
dialTimeout = 500 * time .Millisecond
542
526
} else if dialTimeout > 5 * time .Second {
543
527
dialTimeout = 5 * time .Second
544
528
}
529
+ nestedCtx , cancel := context .WithTimeout (ctx , dialTimeout )
530
+ defer cancel ()
545
531
546
532
var c Conn
547
- c , err = conn .opts .Dialer .Dial (conn .addr , DialOpts {
548
- DialTimeout : dialTimeout ,
533
+ c , err = conn .opts .Dialer .Dial (nestedCtx , conn .addr , DialOpts {
549
534
IoTimeout : opts .Timeout ,
550
535
Transport : opts .Transport ,
551
536
Ssl : opts .Ssl ,
@@ -658,34 +643,46 @@ func pack(h *smallWBuf, enc *msgpack.Encoder, reqid uint32,
658
643
return
659
644
}
660
645
661
- func (conn * Connection ) createConnection (reconnect bool ) (err error ) {
646
+ func (conn * Connection ) createConnection (ctx context.Context ,
647
+ reconnect bool ) error {
662
648
var reconnects uint
663
649
for conn .c == nil && conn .state == connDisconnected {
664
650
now := time .Now ()
665
- err = conn .dial ()
651
+ err : = conn .dial (ctx )
666
652
if err == nil || ! reconnect {
667
653
if err == nil {
668
654
conn .notify (Connected )
669
655
}
670
- return
656
+ return err
671
657
}
672
658
if conn .opts .MaxReconnects > 0 && reconnects > conn .opts .MaxReconnects {
673
659
conn .opts .Logger .Report (LogLastReconnectFailed , conn , err )
674
- err = ClientError {ErrConnectionClosed , "last reconnect failed" }
675
660
// mark connection as closed to avoid reopening by another goroutine
676
- return
661
+ return ClientError { ErrConnectionClosed , "last reconnect failed" }
677
662
}
678
663
conn .opts .Logger .Report (LogReconnectFailed , conn , reconnects , err )
679
664
conn .notify (ReconnectFailed )
680
665
reconnects ++
681
666
conn .mutex .Unlock ()
682
- time .Sleep (time .Until (now .Add (conn .opts .Reconnect )))
667
+
668
+ timer := time .NewTimer (time .Until (now .Add (conn .opts .Reconnect )))
669
+ waitLoop:
670
+ for {
671
+ select {
672
+ case <- ctx .Done ():
673
+ conn .mutex .Lock ()
674
+ return ClientError {ErrConnectionClosed , "context is canceled" }
675
+ case <- timer .C :
676
+ break waitLoop
677
+ }
678
+ }
679
+
683
680
conn .mutex .Lock ()
684
681
}
685
682
if conn .state == connClosed {
686
- err = ClientError {ErrConnectionClosed , "using closed connection" }
683
+ return ClientError {ErrConnectionClosed , "using closed connection" }
687
684
}
688
- return
685
+ return nil
689
686
}
690
687
691
688
func (conn * Connection ) closeConnection (neterr error , forever bool ) (err error ) {
@@ -731,7 +728,7 @@ func (conn *Connection) reconnectImpl(neterr error, c Conn) {
731
728
if conn .opts .Reconnect > 0 {
732
729
if c == conn .c {
733
730
conn .closeConnection (neterr , false )
734
- if err := conn .createConnection (true ); err != nil {
731
+ if err := conn .createConnection (context . Background (), true ); err != nil {
735
732
conn .closeConnection (err , true )
736
733
}
737
734
}
0 commit comments