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 }