github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvclient/kvcoord/send_test.go (about) 1 // Copyright 2015 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 kvcoord 12 13 import ( 14 "context" 15 "net" 16 "reflect" 17 "strconv" 18 "testing" 19 "time" 20 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/rpc" 23 "github.com/cockroachdb/cockroach/pkg/rpc/nodedialer" 24 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 25 "github.com/cockroachdb/cockroach/pkg/util" 26 "github.com/cockroachdb/cockroach/pkg/util/hlc" 27 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 28 "github.com/cockroachdb/cockroach/pkg/util/log" 29 "github.com/cockroachdb/cockroach/pkg/util/netutil" 30 "github.com/cockroachdb/cockroach/pkg/util/stop" 31 "github.com/cockroachdb/cockroach/pkg/util/tracing" 32 ) 33 34 type Node time.Duration 35 36 func (n Node) Batch( 37 ctx context.Context, args *roachpb.BatchRequest, 38 ) (*roachpb.BatchResponse, error) { 39 if n > 0 { 40 time.Sleep(time.Duration(n)) 41 } 42 return &roachpb.BatchResponse{}, nil 43 } 44 45 func (n Node) RangeFeed(_ *roachpb.RangeFeedRequest, _ roachpb.Internal_RangeFeedServer) error { 46 panic("unimplemented") 47 } 48 49 // TestSendToOneClient verifies that Send correctly sends a request 50 // to one server using the heartbeat RPC. 51 func TestSendToOneClient(t *testing.T) { 52 defer leaktest.AfterTest(t)() 53 54 stopper := stop.NewStopper() 55 defer stopper.Stop(context.Background()) 56 57 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 58 rpcContext := rpc.NewInsecureTestingContext(clock, stopper) 59 // This test uses the testing function sendBatch() which does not 60 // support setting the node ID on GRPCDialNode(). Disable Node ID 61 // checks to avoid log.Fatal. 62 rpcContext.TestingAllowNamedRPCToAnonymousServer = true 63 64 s := rpc.NewServer(rpcContext) 65 roachpb.RegisterInternalServer(s, Node(0)) 66 ln, err := netutil.ListenAndServeGRPC(rpcContext.Stopper, s, util.TestAddr) 67 if err != nil { 68 t.Fatal(err) 69 } 70 nodeDialer := nodedialer.New(rpcContext, func(roachpb.NodeID) (net.Addr, error) { 71 return ln.Addr(), nil 72 }) 73 74 reply, err := sendBatch(context.Background(), nil, []net.Addr{ln.Addr()}, rpcContext, nodeDialer) 75 if err != nil { 76 t.Fatal(err) 77 } 78 if reply == nil { 79 t.Errorf("expected reply") 80 } 81 } 82 83 // firstNErrorTransport is a mock transport that sends an error on 84 // requests to the first N addresses, then succeeds. 85 type firstNErrorTransport struct { 86 replicas ReplicaSlice 87 numErrors int 88 numSent int 89 } 90 91 func (f *firstNErrorTransport) IsExhausted() bool { 92 return f.numSent >= len(f.replicas) 93 } 94 95 func (f *firstNErrorTransport) SendNext( 96 _ context.Context, _ roachpb.BatchRequest, 97 ) (*roachpb.BatchResponse, error) { 98 var err error 99 if f.numSent < f.numErrors { 100 err = roachpb.NewSendError("test") 101 } 102 f.numSent++ 103 return &roachpb.BatchResponse{}, err 104 } 105 106 func (f *firstNErrorTransport) NextInternalClient( 107 ctx context.Context, 108 ) (context.Context, roachpb.InternalClient, error) { 109 panic("unimplemented") 110 } 111 112 func (f *firstNErrorTransport) NextReplica() roachpb.ReplicaDescriptor { 113 return roachpb.ReplicaDescriptor{} 114 } 115 116 func (*firstNErrorTransport) MoveToFront(roachpb.ReplicaDescriptor) { 117 } 118 119 // TestComplexScenarios verifies various complex success/failure scenarios by 120 // mocking sendOne. 121 func TestComplexScenarios(t *testing.T) { 122 defer leaktest.AfterTest(t)() 123 124 stopper := stop.NewStopper() 125 defer stopper.Stop(context.Background()) 126 127 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 128 rpcContext := rpc.NewInsecureTestingContext(clock, stopper) 129 // We're going to serve multiple node IDs with that one 130 // context. Disable node ID checks. 131 rpcContext.TestingAllowNamedRPCToAnonymousServer = true 132 nodeDialer := nodedialer.New(rpcContext, nil) 133 134 // TODO(bdarnell): the retryable flag is no longer used for RPC errors. 135 // Rework this test to incorporate application-level errors carried in 136 // the BatchResponse. 137 testCases := []struct { 138 numServers int 139 numErrors int 140 success bool 141 }{ 142 // --- Success scenarios --- 143 {1, 0, true}, 144 {5, 0, true}, 145 // There are some errors, but enough RPCs succeed. 146 {5, 1, true}, 147 {5, 4, true}, 148 {5, 2, true}, 149 150 // --- Failure scenarios --- 151 // All RPCs fail. 152 {5, 5, false}, 153 } 154 for i, test := range testCases { 155 var serverAddrs []net.Addr 156 for j := 0; j < test.numServers; j++ { 157 serverAddrs = append(serverAddrs, util.NewUnresolvedAddr("dummy", 158 strconv.Itoa(j))) 159 } 160 161 reply, err := sendBatch( 162 context.Background(), 163 func( 164 _ SendOptions, 165 _ *nodedialer.Dialer, 166 replicas ReplicaSlice, 167 ) (Transport, error) { 168 return &firstNErrorTransport{ 169 replicas: replicas, 170 numErrors: test.numErrors, 171 }, nil 172 }, 173 serverAddrs, 174 rpcContext, 175 nodeDialer, 176 ) 177 if test.success { 178 if err != nil { 179 t.Errorf("%d: unexpected error: %s", i, err) 180 } 181 if reply == nil { 182 t.Errorf("%d: expected reply", i) 183 } 184 } else { 185 if err == nil { 186 t.Errorf("%d: unexpected success", i) 187 } 188 } 189 } 190 } 191 192 // TestSplitHealthy tests that the splitHealthy helper function sorts healthy 193 // nodes before unhealthy nodes. 194 func TestSplitHealthy(t *testing.T) { 195 defer leaktest.AfterTest(t)() 196 197 testData := []struct { 198 in []batchClient 199 out []batchClient 200 nHealthy int 201 }{ 202 {nil, nil, 0}, 203 { 204 []batchClient{ 205 {replica: roachpb.ReplicaDescriptor{NodeID: 1}, healthy: false}, 206 {replica: roachpb.ReplicaDescriptor{NodeID: 2}, healthy: false}, 207 {replica: roachpb.ReplicaDescriptor{NodeID: 3}, healthy: true}, 208 }, 209 []batchClient{ 210 {replica: roachpb.ReplicaDescriptor{NodeID: 3}, healthy: true}, 211 {replica: roachpb.ReplicaDescriptor{NodeID: 1}, healthy: false}, 212 {replica: roachpb.ReplicaDescriptor{NodeID: 2}, healthy: false}, 213 }, 214 1, 215 }, 216 { 217 []batchClient{ 218 {replica: roachpb.ReplicaDescriptor{NodeID: 1}, healthy: true}, 219 {replica: roachpb.ReplicaDescriptor{NodeID: 2}, healthy: false}, 220 {replica: roachpb.ReplicaDescriptor{NodeID: 3}, healthy: true}, 221 }, 222 []batchClient{ 223 {replica: roachpb.ReplicaDescriptor{NodeID: 1}, healthy: true}, 224 {replica: roachpb.ReplicaDescriptor{NodeID: 3}, healthy: true}, 225 {replica: roachpb.ReplicaDescriptor{NodeID: 2}, healthy: false}, 226 }, 227 2, 228 }, 229 { 230 []batchClient{ 231 {replica: roachpb.ReplicaDescriptor{NodeID: 1}, healthy: true}, 232 {replica: roachpb.ReplicaDescriptor{NodeID: 2}, healthy: true}, 233 {replica: roachpb.ReplicaDescriptor{NodeID: 3}, healthy: true}, 234 }, 235 []batchClient{ 236 {replica: roachpb.ReplicaDescriptor{NodeID: 1}, healthy: true}, 237 {replica: roachpb.ReplicaDescriptor{NodeID: 2}, healthy: true}, 238 {replica: roachpb.ReplicaDescriptor{NodeID: 3}, healthy: true}, 239 }, 240 3, 241 }, 242 } 243 244 for i, td := range testData { 245 nHealthy := splitHealthy(td.in) 246 if nHealthy != td.nHealthy { 247 t.Errorf("%d. splitHealthy(%+v) = %d; not %d", i, td.in, nHealthy, td.nHealthy) 248 } 249 if !reflect.DeepEqual(td.in, td.out) { 250 t.Errorf("%d. splitHealthy(...)\n = %+v;\nnot %+v", i, td.in, td.out) 251 } 252 } 253 } 254 255 func makeReplicas(addrs ...net.Addr) ReplicaSlice { 256 replicas := make(ReplicaSlice, len(addrs)) 257 for i, addr := range addrs { 258 replicas[i].NodeDesc = &roachpb.NodeDescriptor{ 259 Address: util.MakeUnresolvedAddr(addr.Network(), addr.String()), 260 } 261 } 262 return replicas 263 } 264 265 // sendBatch sends Batch requests to specified addresses using send. 266 func sendBatch( 267 ctx context.Context, 268 transportFactory TransportFactory, 269 addrs []net.Addr, 270 rpcContext *rpc.Context, 271 nodeDialer *nodedialer.Dialer, 272 ) (*roachpb.BatchResponse, error) { 273 ds := NewDistSender(DistSenderConfig{ 274 AmbientCtx: log.AmbientContext{Tracer: tracing.NewTracer()}, 275 RPCContext: rpcContext, 276 TestingKnobs: ClientTestingKnobs{ 277 TransportFactory: transportFactory, 278 }, 279 Settings: cluster.MakeTestingClusterSettings(), 280 }, nil) 281 return ds.sendToReplicas( 282 ctx, 283 roachpb.BatchRequest{}, 284 SendOptions{metrics: &ds.metrics}, 285 0, /* rangeID */ 286 makeReplicas(addrs...), 287 nodeDialer, 288 leaseholderInfo{}, 289 false, /* withCommit */ 290 ) 291 }