diff --git a/stan.go b/stan.go index f8c67c1..659b967 100644 --- a/stan.go +++ b/stan.go @@ -179,6 +179,21 @@ type Options struct { // ConnectionLostCB specifies the handler to be invoked when the connection // is permanently lost. ConnectionLostCB ConnectionLostHandler + + // AllowCloseRetry specifies that a failed connection Close() can be retried. + // + // By default, after the first call to Close(), the underlying NATS connection + // is closed (when owned by the library), regardless if the library gets a + // response from the server or not, and calling Close() again is a no-op. + // With AllowCloseRetry set to true, if the library fails to get a response + // from the close protocol, calling Close() again is possible and the library + // will try to resend the protocol. It means that the underlying NATS connection + // won't be closed until the library successfully gets a response from the server. + // This behavior can have side effects in that the underlying NATS connection + // may stay open (or reconnect) when otherwise it would have been closed after + // calling Close(). So AllowCloseRetry is disabled by default to maintain + // expected default behavior in regard with the underlying NATS connection state. + AllowCloseRetry bool } // GetDefaultOptions returns default configuration options for the client. @@ -308,6 +323,15 @@ func SetConnectionLostHandler(handler ConnectionLostHandler) Option { } } +// AllowCloseRetry is an Option that allows a failed connection close to be retried. +// See option AllowCloseRetry for more information. +func AllowCloseRetry(allow bool) Option { + return func(o *Options) error { + o.AllowCloseRetry = allow + return nil + } +} + // A conn represents a bare connection to a stan cluster. type conn struct { sync.RWMutex @@ -682,6 +706,12 @@ func (sc *conn) Close() error { if !sc.closed { sc.closed = true sc.cleanupOnClose(ErrConnectionClosed) + if !sc.opts.AllowCloseRetry { + sc.fullyClosed = true + if sc.ncOwned { + defer sc.nc.Close() + } + } } req := &pb.CloseRequest{ClientID: sc.clientID} @@ -700,7 +730,7 @@ func (sc *conn) Close() error { } // As long as we got a valid response, we consider the connection fully closed. sc.fullyClosed = true - if sc.ncOwned { + if sc.ncOwned && sc.opts.AllowCloseRetry { sc.nc.Close() } if cr.Error != "" { diff --git a/stan_test.go b/stan_test.go index 03a9bc7..a9bfbc7 100644 --- a/stan_test.go +++ b/stan_test.go @@ -1012,7 +1012,10 @@ func TestConnCloseError(t *testing.T) { s := RunServer(clusterName) defer s.Shutdown() - sc := NewDefaultConnection(t) + sc, err := Connect(clusterName, clientName, AllowCloseRetry(true)) + if err != nil { + t.Fatalf("Error on connect: %v", err) + } defer sc.Close() nc, err := nats.Connect(nats.DefaultURL)