func TestHotSpotReconnect(t *testing.T) { s1 := RunServerOnPort(1222) defer s1.Shutdown() numClients := 32 clients := []*nats.Conn{} wg := &sync.WaitGroup{} wg.Add(numClients) opts := []nats.Option{ nats.ReconnectWait(50 * time.Millisecond), nats.ReconnectHandler(func(_ *nats.Conn) { wg.Done() }), } for i := 0; i < numClients; i++ { // nc, err := opts.Connect() nc, err := nats.Connect(servers, opts...) if err != nil { t.Fatalf("Expected to connect, got err: %v\n", err) } defer nc.Close() if nc.ConnectedUrl() != testServers[0] { t.Fatalf("Connected to incorrect server: %v\n", nc.ConnectedUrl()) } clients = append(clients, nc) } s2 := RunServerOnPort(1224) defer s2.Shutdown() s3 := RunServerOnPort(1226) defer s3.Shutdown() s1.Shutdown() numServers := 2 // Wait on all reconnects wg.Wait() // Walk the clients and calculate how many of each.. cs := make(map[string]int) for _, nc := range clients { cs[nc.ConnectedUrl()]++ nc.Close() } if len(cs) != numServers { t.Fatalf("Wrong number of reported servers: %d vs %d\n", len(cs), numServers) } expected := numClients / numServers v := uint(float32(expected) * 0.40) // Check that each item is within acceptable range for s, total := range cs { delta := uint(math.Abs(float64(expected - total))) if delta > v { t.Fatalf("Connected clients to server: %s out of range: %d\n", s, total) } } }
func (fm *FileMgr) setupNatsOptions() { if !fm.certs.skipTLS { err := fm.certs.certLoad() if err != nil { panic(err) } } o := []nats.Option{} o = append(o, nats.MaxReconnects(-1)) // -1 => keep trying forever o = append(o, nats.ReconnectWait(2*time.Second)) o = append(o, nats.Name("archiver")) o = append(o, nats.ErrorHandler(func(c *nats.Conn, s *nats.Subscription, e error) { fm.NatsAsyncErrCh <- asyncErr{conn: c, sub: s, err: e} })) o = append(o, nats.DisconnectHandler(func(conn *nats.Conn) { fm.NatsConnDisconCh <- conn })) o = append(o, nats.ReconnectHandler(func(conn *nats.Conn) { fm.NatsConnReconCh <- conn })) o = append(o, nats.ClosedHandler(func(conn *nats.Conn) { fm.NatsConnClosedCh <- conn })) if !fm.certs.skipTLS { o = append(o, nats.Secure(&fm.certs.tlsConfig)) o = append(o, fm.certs.rootCA) } fm.opts = o }
func TestAutoUnsubAndReconnect(t *testing.T) { s := RunDefaultServer() defer s.Shutdown() rch := make(chan bool) nc, err := nats.Connect(nats.DefaultURL, nats.ReconnectWait(50*time.Millisecond), nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true })) if err != nil { t.Fatalf("Unable to connect: %v", err) } defer nc.Close() received := int32(0) max := int32(10) sub, err := nc.Subscribe("foo", func(_ *nats.Msg) { atomic.AddInt32(&received, 1) }) if err != nil { t.Fatalf("Failed to subscribe: %v", err) } sub.AutoUnsubscribe(int(max)) // Send less than the max total := int(max / 2) for i := 0; i < total; i++ { nc.Publish("foo", []byte("Hello")) } nc.Flush() // Restart the server s.Shutdown() s = RunDefaultServer() defer s.Shutdown() // and wait to reconnect if err := Wait(rch); err != nil { t.Fatal("Failed to get the reconnect cb") } // Now send more than the total max. total = int(3 * max) for i := 0; i < total; i++ { nc.Publish("foo", []byte("Hello")) } nc.Flush() // Wait a bit before checking. time.Sleep(50 * time.Millisecond) // We should have received only up-to-max messages. if atomic.LoadInt32(&received) != max { t.Fatalf("Received %d msgs, wanted only %d\n", received, max) } }
func TestBasicClusterReconnect(t *testing.T) { s1 := RunServerOnPort(1222) defer s1.Shutdown() s2 := RunServerOnPort(1224) defer s2.Shutdown() dch := make(chan bool) rch := make(chan bool) opts := []nats.Option{nats.DontRandomize(), nats.DisconnectHandler(func(nc *nats.Conn) { // Suppress any additional callbacks nc.SetDisconnectHandler(nil) dch <- true }), nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true }), } nc, err := nats.Connect(servers, opts...) if err != nil { t.Fatalf("Expected to connect, got err: %v\n", err) } defer nc.Close() s1.Shutdown() // wait for disconnect if e := WaitTime(dch, 2*time.Second); e != nil { t.Fatal("Did not receive a disconnect callback message") } reconnectTimeStart := time.Now() // wait for reconnect if e := WaitTime(rch, 2*time.Second); e != nil { t.Fatal("Did not receive a reconnect callback message") } if nc.ConnectedUrl() != testServers[2] { t.Fatalf("Does not report correct connection: %s\n", nc.ConnectedUrl()) } // Make sure we did not wait on reconnect for default time. // Reconnect should be fast since it will be a switch to the // second server and not be dependent on server restart time. reconnectTime := time.Since(reconnectTimeStart) if reconnectTime > (100 * time.Millisecond) { t.Fatalf("Took longer than expected to reconnect: %v\n", reconnectTime) } }
func TestCallbacksOrder(t *testing.T) { authS, authSOpts := RunServerWithConfig("./configs/tls.conf") defer authS.Shutdown() s := RunDefaultServer() defer s.Shutdown() firstDisconnect := true dtime1 := time.Time{} dtime2 := time.Time{} rtime := time.Time{} atime1 := time.Time{} atime2 := time.Time{} ctime := time.Time{} cbErrors := make(chan error, 20) reconnected := make(chan bool) closed := make(chan bool) asyncErr := make(chan bool, 2) recvCh := make(chan bool, 2) recvCh1 := make(chan bool) recvCh2 := make(chan bool) dch := func(nc *nats.Conn) { if err := isRunningInAsyncCBDispatcher(); err != nil { cbErrors <- err return } time.Sleep(100 * time.Millisecond) if firstDisconnect { firstDisconnect = false dtime1 = time.Now() } else { dtime2 = time.Now() } } rch := func(nc *nats.Conn) { if err := isRunningInAsyncCBDispatcher(); err != nil { cbErrors <- err reconnected <- true return } time.Sleep(50 * time.Millisecond) rtime = time.Now() reconnected <- true } ech := func(nc *nats.Conn, sub *nats.Subscription, err error) { if err := isRunningInAsyncCBDispatcher(); err != nil { cbErrors <- err asyncErr <- true return } if sub.Subject == "foo" { time.Sleep(20 * time.Millisecond) atime1 = time.Now() } else { atime2 = time.Now() } asyncErr <- true } cch := func(nc *nats.Conn) { if err := isRunningInAsyncCBDispatcher(); err != nil { cbErrors <- err closed <- true return } ctime = time.Now() closed <- true } url := net.JoinHostPort(authSOpts.Host, strconv.Itoa(authSOpts.Port)) url = "nats://" + url + "," + nats.DefaultURL nc, err := nats.Connect(url, nats.DisconnectHandler(dch), nats.ReconnectHandler(rch), nats.ClosedHandler(cch), nats.ErrorHandler(ech), nats.ReconnectWait(50*time.Millisecond), nats.DontRandomize()) if err != nil { t.Fatalf("Unable to connect: %v\n", err) } defer nc.Close() ncp, err := nats.Connect(nats.DefaultURL, nats.ReconnectWait(50*time.Millisecond)) if err != nil { t.Fatalf("Unable to connect: %v\n", err) } defer ncp.Close() // Wait to make sure that if we have closed (incorrectly) the // asyncCBDispatcher during the connect process, this is caught here. time.Sleep(time.Second) s.Shutdown() s = RunDefaultServer() defer s.Shutdown() if err := Wait(reconnected); err != nil { t.Fatal("Did not get the reconnected callback") } var sub1 *nats.Subscription var sub2 *nats.Subscription recv := func(m *nats.Msg) { // Signal that one message is received recvCh <- true // We will now block if m.Subject == "foo" { <-recvCh1 } else { <-recvCh2 } m.Sub.Unsubscribe() } sub1, err = nc.Subscribe("foo", recv) if err != nil { t.Fatalf("Unable to create subscription: %v\n", err) } sub1.SetPendingLimits(1, 100000) sub2, err = nc.Subscribe("bar", recv) if err != nil { t.Fatalf("Unable to create subscription: %v\n", err) } sub2.SetPendingLimits(1, 100000) nc.Flush() ncp.Publish("foo", []byte("test")) ncp.Publish("bar", []byte("test")) ncp.Flush() // Wait notification that message were received err = Wait(recvCh) if err == nil { err = Wait(recvCh) } if err != nil { t.Fatal("Did not receive message") } for i := 0; i < 2; i++ { ncp.Publish("foo", []byte("test")) ncp.Publish("bar", []byte("test")) } ncp.Flush() if err := Wait(asyncErr); err != nil { t.Fatal("Did not get the async callback") } if err := Wait(asyncErr); err != nil { t.Fatal("Did not get the async callback") } close(recvCh1) close(recvCh2) nc.Close() if err := Wait(closed); err != nil { t.Fatal("Did not get the close callback") } if len(cbErrors) > 0 { t.Fatalf("%v", <-cbErrors) } if (dtime1 == time.Time{}) || (dtime2 == time.Time{}) || (rtime == time.Time{}) || (atime1 == time.Time{}) || (atime2 == time.Time{}) || (ctime == time.Time{}) { t.Fatalf("Some callbacks did not fire:\n%v\n%v\n%v\n%v\n%v\n%v", dtime1, rtime, atime1, atime2, dtime2, ctime) } if rtime.Before(dtime1) || dtime2.Before(rtime) || atime2.Before(atime1) || ctime.Before(atime2) { t.Fatalf("Wrong callback order:\n%v\n%v\n%v\n%v\n%v\n%v", dtime1, rtime, atime1, atime2, dtime2, ctime) } }
func TestAutoUnsubWithParallelNextMsgCalls(t *testing.T) { s := RunDefaultServer() defer s.Shutdown() rch := make(chan bool, 1) nc, err := nats.Connect(nats.DefaultURL, nats.ReconnectWait(50*time.Millisecond), nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true })) if err != nil { t.Fatalf("Unable to connect: %v", err) } defer nc.Close() numRoutines := 3 max := 100 total := max * 2 received := int64(0) var wg sync.WaitGroup sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatalf("Failed to subscribe: %v", err) } sub.AutoUnsubscribe(int(max)) nc.Flush() wg.Add(numRoutines) for i := 0; i < numRoutines; i++ { go func(s *nats.Subscription, idx int) { for { // The first to reach the max delivered will cause the // subscription to be removed, which will kick out all // other calls to NextMsg. So don't be afraid of the long // timeout. _, err := s.NextMsg(3 * time.Second) if err != nil { break } atomic.AddInt64(&received, 1) } wg.Done() }(sub, i) } msg := []byte("Hello") for i := 0; i < max/2; i++ { nc.Publish("foo", msg) } nc.Flush() s.Shutdown() s = RunDefaultServer() defer s.Shutdown() // Make sure we got the reconnected cb if err := Wait(rch); err != nil { t.Fatal("Failed to get reconnected cb") } for i := 0; i < total; i++ { nc.Publish("foo", msg) } nc.Flush() wg.Wait() if atomic.LoadInt64(&received) != int64(max) { t.Fatalf("Wrong number of received msg: %v instead of %v", atomic.LoadInt64(&received), max) } }