diff --git a/server/events.go b/server/events.go index 0f761a47..601ed85a 100644 --- a/server/events.go +++ b/server/events.go @@ -1598,14 +1598,16 @@ func (s *Server) shutdownEventing() { s.mu.Lock() clearTimer(&s.sys.sweeper) clearTimer(&s.sys.stmr) - sys := s.sys + rc := s.sys.resetCh + s.sys.resetCh = nil + wg := &s.sys.wg s.mu.Unlock() // We will queue up a shutdown event and wait for the // internal send loop to exit. s.sendShutdownEvent() - sys.wg.Wait() - close(sys.resetCh) + wg.Wait() + close(rc) s.mu.Lock() defer s.mu.Unlock() diff --git a/server/jetstream.go b/server/jetstream.go index 756e75a5..e029f4cd 100644 --- a/server/jetstream.go +++ b/server/jetstream.go @@ -1038,6 +1038,10 @@ func (a *Account) EnableJetStream(limits map[string]JetStreamAccountLimits) erro } s.mu.RLock() + if s.sys == nil { + s.mu.RUnlock() + return ErrServerNotRunning + } sendq := s.sys.sendq s.mu.RUnlock() diff --git a/server/reload_test.go b/server/reload_test.go index 9f0410c7..4fe67a55 100644 --- a/server/reload_test.go +++ b/server/reload_test.go @@ -6100,3 +6100,41 @@ func TestConfigReloadLeafNodeCompressionS2Auto(t *testing.T) { t.Fatalf("Expected compression info to have stayed the same, was %q - %p, got %q - %p", cm, cw, ncm, ncw) } } + +func TestConfigReloadNoPanicOnShutdown(t *testing.T) { + tmpl := ` + port: -1 + jetstream: true + accounts { + A { + users: [{user: A, password: pwd}] + %s + } + B { + users: [{user: B, password: pwd}] + } + } + ` + for i := 0; i < 50; i++ { + conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, _EMPTY_))) + s, _ := RunServerWithConfig(conf) + // Don't use a defer s.Shutdown() here since it would prevent the panic + // to be reported (but the test would still fail because of a runtime timeout). + + err := os.WriteFile(conf, []byte(fmt.Sprintf(tmpl, "jetstream: true")), 0666) + require_NoError(t, err) + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + time.Sleep(10 * time.Millisecond) + s.Shutdown() + }() + + time.Sleep(8 * time.Millisecond) + err = s.Reload() + require_NoError(t, err) + wg.Wait() + } +}