github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/rpc/stats_handler_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package rpc 12 13 import ( 14 "context" 15 "fmt" 16 "testing" 17 "time" 18 19 "github.com/cockroachdb/cockroach/pkg/testutils" 20 "github.com/cockroachdb/cockroach/pkg/util" 21 "github.com/cockroachdb/cockroach/pkg/util/hlc" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 "github.com/cockroachdb/cockroach/pkg/util/log" 24 "github.com/cockroachdb/cockroach/pkg/util/netutil" 25 "github.com/cockroachdb/cockroach/pkg/util/stop" 26 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 27 "github.com/cockroachdb/cockroach/pkg/util/uuid" 28 "github.com/cockroachdb/errors" 29 "google.golang.org/grpc/stats" 30 ) 31 32 func TestStatsHandlerBasic(t *testing.T) { 33 defer leaktest.AfterTest(t)() 34 35 expResults := map[string]*Stats{ 36 "10.10.1.3:26257": {}, 37 "10.10.1.4:26257": {}, 38 } 39 var sh StatsHandler 40 41 ctx := context.Background() 42 ctx = sh.TagConn(ctx, &stats.ConnTagInfo{ 43 RemoteAddr: util.NewUnresolvedAddr("tcp", "10.10.1.3:26257"), 44 }) 45 sh.HandleRPC(ctx, &stats.InHeader{WireLength: 2}) 46 sh.HandleRPC(ctx, &stats.InPayload{WireLength: 3}) 47 sh.HandleRPC(ctx, &stats.InTrailer{WireLength: 5}) 48 sh.HandleRPC(ctx, &stats.End{}) 49 // Note that we must add 5 bytes here to account for an inaccuracy 50 // in the grpc stats computations. See the comment in stats_handler.go. 51 expResults["10.10.1.3:26257"].incoming += 15 52 expResults["10.10.1.3:26257"].count++ 53 54 ctx = context.Background() 55 ctx = sh.TagConn(ctx, &stats.ConnTagInfo{ 56 RemoteAddr: util.NewUnresolvedAddr("tcp", "10.10.1.4:26257"), 57 }) 58 sh.HandleRPC(ctx, &stats.OutPayload{WireLength: 7}) 59 sh.HandleRPC(ctx, &stats.OutTrailer{WireLength: 11}) 60 expResults["10.10.1.4:26257"].outgoing += 18 61 62 cStats1 := sh.newClient("10.10.1.3:26257") 63 cStats1.HandleRPC(ctx, &stats.InHeader{WireLength: 13}) 64 cStats1.HandleRPC(ctx, &stats.InPayload{WireLength: 17}) 65 cStats1.HandleRPC(ctx, &stats.InTrailer{WireLength: 19}) 66 // See comment above for why we must add 5 bytes here. 67 expResults["10.10.1.3:26257"].incoming += 54 68 69 cStats2 := sh.newClient("10.10.1.4:26257") 70 cStats2.HandleRPC(ctx, &stats.OutPayload{WireLength: 23}) 71 cStats2.HandleRPC(ctx, &stats.OutTrailer{WireLength: 29}) 72 expResults["10.10.1.4:26257"].outgoing += 52 73 74 // Verify the expected results. 75 sh.stats.Range(func(k, v interface{}) bool { 76 key := k.(string) 77 value := v.(*Stats) 78 if e, a := expResults[key].Incoming(), value.Incoming(); e != a { 79 t.Errorf("for target=%s, expected Incoming=%d, got %d", key, e, a) 80 } 81 if e, a := expResults[key].Outgoing(), value.Outgoing(); e != a { 82 t.Errorf("for target=%s, expected Outgoing=%d, got %d", key, e, a) 83 } 84 if e, a := expResults[key].Count(), value.Count(); e != a { 85 t.Errorf("for target=%s, expected Count=%d, got %d", key, e, a) 86 } 87 return true 88 }) 89 } 90 91 // TestStatsHandlerWithHeartbeats verifies the stats handler captures 92 // incoming and outgoing traffic with real server and client connections. 93 func TestStatsHandlerWithHeartbeats(t *testing.T) { 94 defer leaktest.AfterTest(t)() 95 96 // Can't be zero because that'd be an empty offset. 97 clock := hlc.NewClock(timeutil.Unix(0, 1).UnixNano, time.Nanosecond) 98 stopper := stop.NewStopper() 99 defer stopper.Stop(context.Background()) 100 101 // Shared cluster ID by all RPC peers (this ensures that the peers 102 // don't talk to servers from unrelated tests by accident). 103 clusterID := uuid.MakeV4() 104 105 serverCtx := newTestContext(clusterID, clock, stopper) 106 const serverNodeID = 1 107 serverCtx.NodeID.Set(context.Background(), serverNodeID) 108 s := newTestServer(t, serverCtx) 109 110 heartbeat := &ManualHeartbeatService{ 111 ready: make(chan error), 112 stopper: stopper, 113 clock: clock, 114 remoteClockMonitor: serverCtx.RemoteClocks, 115 settings: serverCtx.settings, 116 nodeID: &serverCtx.NodeID, 117 } 118 RegisterHeartbeatServer(s, heartbeat) 119 120 ln, err := netutil.ListenAndServeGRPC(serverCtx.Stopper, s, util.TestAddr) 121 if err != nil { 122 t.Fatal(err) 123 } 124 remoteAddr := ln.Addr().String() 125 126 clientCtx := newTestContext(clusterID, clock, stopper) 127 // Make the interval shorter to speed up the test. 128 clientCtx.heartbeatInterval = 1 * time.Millisecond 129 go func() { heartbeat.ready <- nil }() 130 if _, err := clientCtx.GRPCDialNode(remoteAddr, serverNodeID, DefaultClass). 131 Connect(context.Background()); err != nil { 132 t.Fatal(err) 133 } 134 135 // Wait for the connection & successful heartbeat. 136 testutils.SucceedsSoon(t, func() error { 137 err := clientCtx.TestingConnHealth(remoteAddr, serverNodeID) 138 if err != nil && !errors.Is(err, ErrNotHeartbeated) { 139 t.Fatal(err) 140 } 141 return err 142 }) 143 144 // Verify server and client stats in a SucceedsSoon loop to avoid 145 // timing-related stats counting problems. 146 testutils.SucceedsSoon(t, func() error { 147 // Get server stats. 148 serverSM := serverCtx.GetStatsMap() 149 var serverVal interface{} 150 serverSM.Range(func(k, v interface{}) bool { 151 serverVal = v 152 return true 153 }) 154 if serverVal == nil { 155 return fmt.Errorf("expected server map to contain stats for one client connection") 156 } 157 // Get client stats. 158 clientSM := clientCtx.GetStatsMap() 159 clientVal, ok := clientSM.Load(remoteAddr) 160 if !ok { 161 return fmt.Errorf("expected map to contain stats for remote addr %s", remoteAddr) 162 } 163 164 // Verify that server stats mirror client stats. Note that because 165 // GRPC is no longer reporting outgoing header wire lengths, we 166 // can't compare incoming and outgoing stats for equality, but are 167 // forced to verify one is less than the other. 168 if s, c := serverVal.(*Stats).Incoming(), clientVal.(*Stats).Outgoing(); s == 0 || c == 0 || s <= c { 169 return fmt.Errorf("expected server.incoming > client.outgoing; got %d, %d", s, c) 170 } 171 if s, c := serverVal.(*Stats).Outgoing(), clientVal.(*Stats).Incoming(); s == 0 || c == 0 || s > c { 172 return fmt.Errorf("expected server.outgoing < client.incoming; got %d, %d", s, c) 173 } 174 log.Infof(context.Background(), "server incoming = %v, server outgoing = %v, client incoming = %v, client outgoing = %v", 175 serverVal.(*Stats).Incoming(), serverVal.(*Stats).Outgoing(), clientVal.(*Stats).Incoming(), clientVal.(*Stats).Outgoing()) 176 return nil 177 }) 178 }