github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvclient/kvcoord/replica_slice_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  	"fmt"
    15  	"reflect"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    21  	"github.com/cockroachdb/cockroach/pkg/util"
    22  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    23  )
    24  
    25  func getStores(rs ReplicaSlice) (r []roachpb.StoreID) {
    26  	for i := range rs {
    27  		r = append(r, rs[i].StoreID)
    28  	}
    29  	return
    30  }
    31  
    32  func createReplicaSlice() ReplicaSlice {
    33  	rs := ReplicaSlice(nil)
    34  	for i := 0; i < 5; i++ {
    35  		rs = append(rs, ReplicaInfo{ReplicaDescriptor: roachpb.ReplicaDescriptor{StoreID: roachpb.StoreID(i + 1)}})
    36  	}
    37  	return rs
    38  }
    39  
    40  func TestReplicaSliceMoveToFront(t *testing.T) {
    41  	defer leaktest.AfterTest(t)()
    42  	rs := createReplicaSlice()
    43  	rs.MoveToFront(0)
    44  	exp := []roachpb.StoreID{1, 2, 3, 4, 5}
    45  	if stores := getStores(rs); !reflect.DeepEqual(stores, exp) {
    46  		t.Errorf("expected order %s, got %s", exp, stores)
    47  	}
    48  	rs.MoveToFront(2)
    49  	exp = []roachpb.StoreID{3, 1, 2, 4, 5}
    50  	if stores := getStores(rs); !reflect.DeepEqual(stores, exp) {
    51  		t.Errorf("expected order %s, got %s", exp, stores)
    52  	}
    53  	rs.MoveToFront(4)
    54  	exp = []roachpb.StoreID{5, 3, 1, 2, 4}
    55  	if stores := getStores(rs); !reflect.DeepEqual(stores, exp) {
    56  		t.Errorf("expected order %s, got %s", exp, stores)
    57  	}
    58  }
    59  
    60  func desc(nid roachpb.NodeID, sid roachpb.StoreID) roachpb.ReplicaDescriptor {
    61  	return roachpb.ReplicaDescriptor{NodeID: nid, StoreID: sid}
    62  }
    63  
    64  func addr(nid roachpb.NodeID, sid roachpb.StoreID) util.UnresolvedAddr {
    65  	return util.MakeUnresolvedAddr("tcp", fmt.Sprintf("%d:%d", nid, sid))
    66  }
    67  
    68  func locality(t *testing.T, locStrs []string) roachpb.Locality {
    69  	var locality roachpb.Locality
    70  	for _, l := range locStrs {
    71  		idx := strings.IndexByte(l, '=')
    72  		if idx == -1 {
    73  			t.Fatalf("locality %s not specified as <key>=<value>", l)
    74  		}
    75  		tier := roachpb.Tier{
    76  			Key:   l[:idx],
    77  			Value: l[idx+1:],
    78  		}
    79  		locality.Tiers = append(locality.Tiers, tier)
    80  	}
    81  	return locality
    82  }
    83  
    84  func nodeDesc(
    85  	t *testing.T, nid roachpb.NodeID, sid roachpb.StoreID, locStrs []string,
    86  ) *roachpb.NodeDescriptor {
    87  	return &roachpb.NodeDescriptor{
    88  		Locality: locality(t, locStrs),
    89  		Address:  addr(nid, sid),
    90  	}
    91  }
    92  
    93  func info(t *testing.T, nid roachpb.NodeID, sid roachpb.StoreID, locStrs []string) ReplicaInfo {
    94  	return ReplicaInfo{
    95  		ReplicaDescriptor: desc(nid, sid),
    96  		NodeDesc:          nodeDesc(t, nid, sid, locStrs),
    97  	}
    98  }
    99  
   100  func TestReplicaSliceOptimizeReplicaOrder(t *testing.T) {
   101  	defer leaktest.AfterTest(t)()
   102  	testCases := []struct {
   103  		name       string
   104  		node       *roachpb.NodeDescriptor
   105  		latencies  map[string]time.Duration
   106  		slice      ReplicaSlice
   107  		expOrdered ReplicaSlice
   108  	}{
   109  		{
   110  			name: "order by locality matching",
   111  			node: nodeDesc(t, 1, 1, []string{"country=us", "region=west", "city=la"}),
   112  			slice: ReplicaSlice{
   113  				info(t, 2, 2, []string{"country=us", "region=west", "city=sf"}),
   114  				info(t, 3, 3, []string{"country=uk", "city=london"}),
   115  				info(t, 4, 4, []string{"country=us", "region=east", "city=ny"}),
   116  			},
   117  			expOrdered: ReplicaSlice{
   118  				info(t, 2, 2, []string{"country=us", "region=west", "city=sf"}),
   119  				info(t, 4, 4, []string{"country=us", "region=east", "city=ny"}),
   120  				info(t, 3, 3, []string{"country=uk", "city=london"}),
   121  			},
   122  		},
   123  		{
   124  			name: "order by locality matching, put node first",
   125  			node: nodeDesc(t, 1, 1, []string{"country=us", "region=west", "city=la"}),
   126  			slice: ReplicaSlice{
   127  				info(t, 1, 1, []string{"country=us", "region=west", "city=la"}),
   128  				info(t, 2, 2, []string{"country=us", "region=west", "city=sf"}),
   129  				info(t, 3, 3, []string{"country=uk", "city=london"}),
   130  				info(t, 4, 4, []string{"country=us", "region=east", "city=ny"}),
   131  			},
   132  			expOrdered: ReplicaSlice{
   133  				info(t, 1, 1, []string{"country=us", "region=west", "city=la"}),
   134  				info(t, 2, 2, []string{"country=us", "region=west", "city=sf"}),
   135  				info(t, 4, 4, []string{"country=us", "region=east", "city=ny"}),
   136  				info(t, 3, 3, []string{"country=uk", "city=london"}),
   137  			},
   138  		},
   139  		{
   140  			name: "order by latency",
   141  			node: nodeDesc(t, 1, 1, []string{"country=us", "region=west", "city=la"}),
   142  			latencies: map[string]time.Duration{
   143  				"2:2": time.Hour,
   144  				"3:3": time.Minute,
   145  				"4:4": time.Second,
   146  			},
   147  			slice: ReplicaSlice{
   148  				info(t, 2, 2, []string{"country=us", "region=west", "city=sf"}),
   149  				info(t, 4, 4, []string{"country=us", "region=east", "city=ny"}),
   150  				info(t, 3, 3, []string{"country=uk", "city=london"}),
   151  			},
   152  			expOrdered: ReplicaSlice{
   153  				info(t, 4, 4, []string{"country=us", "region=east", "city=ny"}),
   154  				info(t, 3, 3, []string{"country=uk", "city=london"}),
   155  				info(t, 2, 2, []string{"country=us", "region=west", "city=sf"}),
   156  			},
   157  		},
   158  	}
   159  	for _, test := range testCases {
   160  		t.Run(test.name, func(t *testing.T) {
   161  			var latencyFn LatencyFunc
   162  			if test.latencies != nil {
   163  				latencyFn = func(addr string) (time.Duration, bool) {
   164  					lat, ok := test.latencies[addr]
   165  					return lat, ok
   166  				}
   167  			}
   168  			test.slice.OptimizeReplicaOrder(test.node, latencyFn)
   169  			if !reflect.DeepEqual(test.slice, test.expOrdered) {
   170  				t.Errorf("expected order %+v; got %+v", test.expOrdered, test.slice)
   171  			}
   172  		})
   173  	}
   174  }