vitess.io/vitess@v0.16.2/go/vt/srvtopo/resolver_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package srvtopo 18 19 import ( 20 "testing" 21 22 "github.com/stretchr/testify/require" 23 24 "context" 25 26 "vitess.io/vitess/go/sqltypes" 27 "vitess.io/vitess/go/vt/key" 28 "vitess.io/vitess/go/vt/logutil" 29 "vitess.io/vitess/go/vt/topo/memorytopo" 30 "vitess.io/vitess/go/vt/topotools" 31 "vitess.io/vitess/go/vt/vttablet/tabletconntest" 32 33 querypb "vitess.io/vitess/go/vt/proto/query" 34 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 35 ) 36 37 func initResolver(t *testing.T, name string) *Resolver { 38 ctx := context.Background() 39 cell := "cell1" 40 ts := memorytopo.NewServer(cell) 41 rs := NewResilientServer(ts, name) 42 43 // Create sharded keyspace and shards. 44 if err := ts.CreateKeyspace(ctx, "sks", &topodatapb.Keyspace{}); err != nil { 45 t.Fatalf("CreateKeyspace(sks) failed: %v", err) 46 } 47 shardKrArray, err := key.ParseShardingSpec("-20-40-60-80-a0-c0-e0-") 48 if err != nil { 49 t.Fatalf("key.ParseShardingSpec failed: %v", err) 50 } 51 for _, kr := range shardKrArray { 52 shard := key.KeyRangeString(kr) 53 if err := ts.CreateShard(ctx, "sks", shard); err != nil { 54 t.Fatalf("CreateShard(\"%v\") failed: %v", shard, err) 55 } 56 } 57 58 // Create unsharded keyspace and shard. 59 if err := ts.CreateKeyspace(ctx, "uks", &topodatapb.Keyspace{}); err != nil { 60 t.Fatalf("CreateKeyspace(uks) failed: %v", err) 61 } 62 if err := ts.CreateShard(ctx, "uks", "0"); err != nil { 63 t.Fatalf("CreateShard(0) failed: %v", err) 64 } 65 66 // And rebuild both. 67 for _, keyspace := range []string{"sks", "uks"} { 68 if err := topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, keyspace, []string{cell}, false); err != nil { 69 t.Fatalf("RebuildKeyspace(%v) failed: %v", keyspace, err) 70 } 71 } 72 73 // Create snapshot keyspace and shard. 74 err = ts.CreateKeyspace(ctx, "rks", &topodatapb.Keyspace{KeyspaceType: topodatapb.KeyspaceType_SNAPSHOT}) 75 require.NoError(t, err, "CreateKeyspace(rks) failed: %v") 76 err = ts.CreateShard(ctx, "rks", "-80") 77 require.NoError(t, err, "CreateShard(-80) failed: %v") 78 79 // Rebuild should error because allowPartial is false and shard does not cover full keyrange 80 err = topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, "rks", []string{cell}, false) 81 require.Error(t, err, "RebuildKeyspace(rks) failed") 82 require.EqualError(t, err, "keyspace partition for PRIMARY in cell cell1 does not end with max key") 83 84 // Rebuild should succeed with allowPartial true 85 err = topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, "rks", []string{cell}, true) 86 require.NoError(t, err, "RebuildKeyspace(rks) failed") 87 88 // Create missing shard 89 err = ts.CreateShard(ctx, "rks", "80-") 90 require.NoError(t, err, "CreateShard(80-) failed: %v") 91 92 // Rebuild should now succeed even with allowPartial false 93 err = topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, "rks", []string{cell}, false) 94 require.NoError(t, err, "RebuildKeyspace(rks) failed") 95 96 return NewResolver(rs, &tabletconntest.FakeQueryService{}, cell) 97 } 98 99 func TestResolveDestinations(t *testing.T) { 100 resolver := initResolver(t, "TestResolveDestinations") 101 102 id1 := &querypb.Value{ 103 Type: sqltypes.VarChar, 104 Value: []byte("1"), 105 } 106 id2 := &querypb.Value{ 107 Type: sqltypes.VarChar, 108 Value: []byte("2"), 109 } 110 111 kr2040 := &topodatapb.KeyRange{ 112 Start: []byte{0x20}, 113 End: []byte{0x40}, 114 } 115 kr80a0 := &topodatapb.KeyRange{ 116 Start: []byte{0x80}, 117 End: []byte{0xa0}, 118 } 119 kr2830 := &topodatapb.KeyRange{ 120 Start: []byte{0x28}, 121 End: []byte{0x30}, 122 } 123 124 var testCases = []struct { 125 name string 126 keyspace string 127 ids []*querypb.Value 128 destinations []key.Destination 129 errString string 130 expectedShards []string 131 expectedValues [][]*querypb.Value 132 }{ 133 { 134 name: "unsharded keyspace, regular shard, no ids", 135 keyspace: "uks", 136 destinations: []key.Destination{ 137 key.DestinationShard("0"), 138 }, 139 expectedShards: []string{"0"}, 140 }, 141 { 142 name: "unsharded keyspace, regular shard, with ids", 143 keyspace: "uks", 144 ids: []*querypb.Value{id1, id2}, 145 destinations: []key.Destination{ 146 key.DestinationShard("0"), 147 key.DestinationShard("0"), 148 }, 149 expectedShards: []string{"0"}, 150 expectedValues: [][]*querypb.Value{ 151 {id1, id2}, 152 }, 153 }, 154 { 155 name: "sharded keyspace, keyrange destinations, with ids", 156 keyspace: "sks", 157 ids: []*querypb.Value{id1, id2}, 158 destinations: []key.Destination{ 159 key.DestinationExactKeyRange{KeyRange: kr2040}, 160 key.DestinationExactKeyRange{KeyRange: kr80a0}, 161 }, 162 expectedShards: []string{"20-40", "80-a0"}, 163 expectedValues: [][]*querypb.Value{ 164 {id1}, 165 {id2}, 166 }, 167 }, 168 { 169 name: "sharded keyspace, keyspace id destinations, with ids", 170 keyspace: "sks", 171 ids: []*querypb.Value{id1, id2}, 172 destinations: []key.Destination{ 173 key.DestinationKeyspaceID{0x28}, 174 key.DestinationKeyspaceID{0x78, 0x23}, 175 }, 176 expectedShards: []string{"20-40", "60-80"}, 177 expectedValues: [][]*querypb.Value{ 178 {id1}, 179 {id2}, 180 }, 181 }, 182 { 183 name: "sharded keyspace, multi keyspace id destinations, with ids", 184 keyspace: "sks", 185 ids: []*querypb.Value{id1, id2}, 186 destinations: []key.Destination{ 187 key.DestinationKeyspaceIDs{ 188 {0x28}, 189 {0x47}, 190 }, 191 key.DestinationKeyspaceIDs{ 192 {0x78}, 193 {0x23}, 194 }, 195 }, 196 expectedShards: []string{"20-40", "40-60", "60-80"}, 197 expectedValues: [][]*querypb.Value{ 198 {id1, id2}, 199 {id1}, 200 {id2}, 201 }, 202 }, 203 { 204 name: "using non-mapping keyranges should fail", 205 keyspace: "sks", 206 destinations: []key.Destination{ 207 key.DestinationExactKeyRange{ 208 KeyRange: kr2830, 209 }, 210 }, 211 errString: "keyrange 28-30 does not exactly match shards", 212 }, 213 } 214 for _, testCase := range testCases { 215 ctx := context.Background() 216 rss, values, err := resolver.ResolveDestinations(ctx, testCase.keyspace, topodatapb.TabletType_REPLICA, testCase.ids, testCase.destinations) 217 if err != nil { 218 if testCase.errString == "" { 219 t.Errorf("%v: expected success but got error: %v", testCase.name, err) 220 } else { 221 if err.Error() != testCase.errString { 222 t.Errorf("%v: expected error '%v' but got error: %v", testCase.name, testCase.errString, err) 223 } 224 } 225 continue 226 } 227 228 if testCase.errString != "" { 229 t.Errorf("%v: expected error '%v' but got success", testCase.name, testCase.errString) 230 continue 231 } 232 233 // Check the ResolvedShard are correct. 234 if len(rss) != len(testCase.expectedShards) { 235 t.Errorf("%v: expected %v ResolvedShard, but got: %v", testCase.name, len(testCase.expectedShards), rss) 236 continue 237 } 238 badShards := false 239 for i, rs := range rss { 240 if rs.Target.Shard != testCase.expectedShards[i] { 241 t.Errorf("%v: expected rss[%v] to be '%v', but got: %v", testCase.name, i, testCase.expectedShards[i], rs.Target.Shard) 242 badShards = true 243 } 244 } 245 if badShards { 246 continue 247 } 248 249 // Check the values are correct, if we passed some in. 250 if testCase.ids == nil { 251 continue 252 } 253 if len(values) != len(rss) { 254 t.Errorf("%v: len(values) != len(rss): %v != %v", testCase.name, len(values), len(rss)) 255 } 256 if !ValuesEqual(values, testCase.expectedValues) { 257 t.Errorf("%v: values != testCase.expectedValues: got values=%v", testCase.name, values) 258 } 259 } 260 }