From e6039e0a8b4f2cf77df6e672ea75aa193d7e38eb Mon Sep 17 00:00:00 2001 From: Ivan Kozlovic Date: Tue, 16 Aug 2016 10:50:49 -0600 Subject: [PATCH] [FIXED] Server panic when poll for Varz and others concurrently Resolves #327 --- server/monitor.go | 7 ++++++- server/monitor_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/server/monitor.go b/server/monitor.go index 238f787d13..f4b36da96a 100644 --- a/server/monitor.go +++ b/server/monitor.go @@ -480,7 +480,12 @@ func (s *Server) HandleVarz(w http.ResponseWriter, r *http.Request) { v.SlowConsumers = s.slowConsumers v.Subscriptions = s.sl.Count() s.httpReqStats[VarzPath]++ - v.HTTPReqStats = s.httpReqStats + // Need a copy here since s.httpReqStas can change while doing + // the marshaling down below. + v.HTTPReqStats = make(map[string]uint64, len(s.httpReqStats)) + for key, val := range s.httpReqStats { + v.HTTPReqStats[key] = val + } s.mu.Unlock() b, err := json.MarshalIndent(v, "", " ") diff --git a/server/monitor_test.go b/server/monitor_test.go index 93239f342c..b6cf21220e 100644 --- a/server/monitor_test.go +++ b/server/monitor_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/nats-io/nats" + "sync" ) const CLIENT_PORT = 11224 @@ -1283,3 +1284,39 @@ func TestStacksz(t *testing.T) { } defer respj.Body.Close() } + +func TestConcurrentMonitoring(t *testing.T) { + s := runMonitorServer() + defer s.Shutdown() + + url := fmt.Sprintf("http://localhost:%d/", MONITOR_PORT) + // Get some endpoints. Make sure we have at least varz, + // and the more the merrier. + endpoints := []string{"varz", "varz", "varz", "connz", "connz", "subsz", "subsz", "routez", "routez"} + wg := &sync.WaitGroup{} + wg.Add(len(endpoints)) + for _, e := range endpoints { + go func(endpoint string) { + defer wg.Done() + for i := 0; i < 150; i++ { + resp, err := http.Get(url + endpoint) + if err != nil { + t.Fatalf("Expected no error: Got %v\n", err) + } + if resp.StatusCode != 200 { + t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) + } + ct := resp.Header.Get("Content-Type") + if ct != "application/json" { + t.Fatalf("Expected application/json content-type, got %s\n", ct) + } + defer resp.Body.Close() + if _, err := ioutil.ReadAll(resp.Body); err != nil { + t.Fatalf("Got an error reading the body: %v\n", err) + } + resp.Body.Close() + } + }(e) + } + wg.Wait() +}