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  }