vitess.io/vitess@v0.16.2/go/vt/wrangler/resharder_env_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 wrangler 18 19 import ( 20 "context" 21 "fmt" 22 "regexp" 23 "runtime/debug" 24 "strings" 25 "sync" 26 "testing" 27 28 "github.com/stretchr/testify/require" 29 30 "vitess.io/vitess/go/sqltypes" 31 "vitess.io/vitess/go/vt/key" 32 "vitess.io/vitess/go/vt/logutil" 33 "vitess.io/vitess/go/vt/topo" 34 "vitess.io/vitess/go/vt/topo/memorytopo" 35 "vitess.io/vitess/go/vt/vttablet/tmclient" 36 37 querypb "vitess.io/vitess/go/vt/proto/query" 38 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 39 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 40 ) 41 42 type testResharderEnv struct { 43 wr *Wrangler 44 keyspace string 45 workflow string 46 sources []string 47 targets []string 48 tablets map[int]*topodatapb.Tablet 49 topoServ *topo.Server 50 cell string 51 tmc *testResharderTMClient 52 } 53 54 var ( 55 testMode = "" // "debug" 56 ) 57 58 //---------------------------------------------- 59 // testResharderEnv 60 61 func getPartition(t *testing.T, shards []string) *topodatapb.SrvKeyspace_KeyspacePartition { 62 partition := &topodatapb.SrvKeyspace_KeyspacePartition{ 63 ServedType: topodatapb.TabletType_PRIMARY, 64 ShardReferences: []*topodatapb.ShardReference{}, 65 } 66 for _, shard := range shards { 67 keyRange, err := key.ParseShardingSpec(shard) 68 require.NoError(t, err) 69 require.Equal(t, 1, len(keyRange)) 70 partition.ShardReferences = append(partition.ShardReferences, &topodatapb.ShardReference{ 71 Name: shard, 72 KeyRange: keyRange[0], 73 }) 74 } 75 return partition 76 } 77 func initTopo(t *testing.T, topo *topo.Server, keyspace string, sources, targets, cells []string) { 78 ctx := context.Background() 79 srvKeyspace := &topodatapb.SrvKeyspace{ 80 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{}, 81 } 82 srvKeyspace.Partitions = append(srvKeyspace.Partitions, getPartition(t, sources)) 83 srvKeyspace.Partitions = append(srvKeyspace.Partitions, getPartition(t, targets)) 84 for _, cell := range cells { 85 topo.UpdateSrvKeyspace(ctx, cell, keyspace, srvKeyspace) 86 } 87 topo.ValidateSrvKeyspace(ctx, keyspace, strings.Join(cells, ",")) 88 } 89 90 func newTestResharderEnv(t *testing.T, sources, targets []string) *testResharderEnv { 91 env := &testResharderEnv{ 92 keyspace: "ks", 93 workflow: "resharderTest", 94 sources: sources, 95 targets: targets, 96 tablets: make(map[int]*topodatapb.Tablet), 97 topoServ: memorytopo.NewServer("cell"), 98 cell: "cell", 99 tmc: newTestResharderTMClient(), 100 } 101 env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) 102 initTopo(t, env.topoServ, "ks", sources, targets, []string{"cell"}) 103 tabletID := 100 104 for _, shard := range sources { 105 _ = env.addTablet(tabletID, env.keyspace, shard, topodatapb.TabletType_PRIMARY) 106 tabletID += 10 107 } 108 tabletID = 200 109 for _, shard := range targets { 110 _ = env.addTablet(tabletID, env.keyspace, shard, topodatapb.TabletType_PRIMARY) 111 tabletID += 10 112 } 113 return env 114 } 115 116 func (env *testResharderEnv) expectValidation() { 117 for _, tablet := range env.tablets { 118 tabletID := int(tablet.Alias.Uid) 119 // wr.validateNewWorkflow 120 env.tmc.expectVRQuery(tabletID, fmt.Sprintf("select 1 from _vt.vreplication where db_name='vt_%s' and workflow='%s'", env.keyspace, env.workflow), &sqltypes.Result{}) 121 env.tmc.expectVRQuery(tabletID, rsSelectFrozenQuery, &sqltypes.Result{}) 122 123 if tabletID >= 200 { 124 // validateTargets 125 env.tmc.expectVRQuery(tabletID, fmt.Sprintf("select 1 from _vt.vreplication where db_name='vt_%s'", env.keyspace), &sqltypes.Result{}) 126 } 127 } 128 } 129 130 func (env *testResharderEnv) expectNoRefStream() { 131 for _, tablet := range env.tablets { 132 tabletID := int(tablet.Alias.Uid) 133 if tabletID < 200 { 134 // readRefStreams 135 env.tmc.expectVRQuery(tabletID, fmt.Sprintf("select workflow, source, cell, tablet_types from _vt.vreplication where db_name='vt_%s' and message != 'FROZEN'", env.keyspace), &sqltypes.Result{}) 136 } 137 } 138 } 139 140 func (env *testResharderEnv) close() { 141 for _, t := range env.tablets { 142 env.deleteTablet(t) 143 } 144 } 145 146 func (env *testResharderEnv) addTablet(id int, keyspace, shard string, tabletType topodatapb.TabletType) *topodatapb.Tablet { 147 tablet := &topodatapb.Tablet{ 148 Alias: &topodatapb.TabletAlias{ 149 Cell: env.cell, 150 Uid: uint32(id), 151 }, 152 Keyspace: keyspace, 153 Shard: shard, 154 KeyRange: &topodatapb.KeyRange{}, 155 Type: tabletType, 156 PortMap: map[string]int32{ 157 "test": int32(id), 158 }, 159 } 160 env.tablets[id] = tablet 161 if err := env.wr.TopoServer().InitTablet(context.Background(), tablet, false /* allowPrimaryOverride */, true /* createShardAndKeyspace */, false /* allowUpdate */); err != nil { 162 panic(err) 163 } 164 if tabletType == topodatapb.TabletType_PRIMARY { 165 _, err := env.wr.ts.UpdateShardFields(context.Background(), keyspace, shard, func(si *topo.ShardInfo) error { 166 si.PrimaryAlias = tablet.Alias 167 return nil 168 }) 169 if err != nil { 170 panic(err) 171 } 172 } 173 return tablet 174 } 175 176 func (env *testResharderEnv) deleteTablet(tablet *topodatapb.Tablet) { 177 env.topoServ.DeleteTablet(context.Background(), tablet.Alias) 178 delete(env.tablets, int(tablet.Alias.Uid)) 179 } 180 181 //---------------------------------------------- 182 // testResharderTMClient 183 184 type testResharderTMClient struct { 185 tmclient.TabletManagerClient 186 schema *tabletmanagerdatapb.SchemaDefinition 187 188 mu sync.Mutex 189 vrQueries map[int][]*queryResult 190 } 191 192 type queryResult struct { 193 query string 194 result *querypb.QueryResult 195 } 196 197 func newTestResharderTMClient() *testResharderTMClient { 198 return &testResharderTMClient{ 199 vrQueries: make(map[int][]*queryResult), 200 } 201 } 202 203 func (tmc *testResharderTMClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) { 204 return tmc.schema, nil 205 } 206 207 func (tmc *testResharderTMClient) expectVRQuery(tabletID int, query string, result *sqltypes.Result) { 208 tmc.mu.Lock() 209 defer tmc.mu.Unlock() 210 211 tmc.vrQueries[tabletID] = append(tmc.vrQueries[tabletID], &queryResult{ 212 query: query, 213 result: sqltypes.ResultToProto3(result), 214 }) 215 } 216 217 func (tmc *testResharderTMClient) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) { 218 tmc.mu.Lock() 219 defer tmc.mu.Unlock() 220 if testMode == "debug" { 221 fmt.Printf("Got: %d:%s\n", tablet.Alias.Uid, query) 222 } 223 qrs := tmc.vrQueries[int(tablet.Alias.Uid)] 224 if len(qrs) == 0 { 225 if testMode == "debug" { 226 fmt.Printf("Want: %d:%s, Stack:\n%v\n", tablet.Alias.Uid, query, debug.Stack()) 227 } 228 return nil, fmt.Errorf("tablet %v does not expect any more queries: %s", tablet, query) 229 } 230 matched := false 231 if qrs[0].query[0] == '/' { 232 matched = regexp.MustCompile(qrs[0].query[1:]).MatchString(query) 233 } else { 234 matched = query == qrs[0].query 235 } 236 if !matched { 237 return nil, fmt.Errorf("tablet %v: unexpected query %s, want: %s", tablet, query, qrs[0].query) 238 } 239 tmc.vrQueries[int(tablet.Alias.Uid)] = qrs[1:] 240 return qrs[0].result, nil 241 } 242 243 func (tmc *testResharderTMClient) ExecuteFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsDbaRequest) (*querypb.QueryResult, error) { 244 // Reuse VReplicationExec 245 return tmc.VReplicationExec(ctx, tablet, string(req.Query)) 246 } 247 248 func (tmc *testResharderTMClient) verifyQueries(t *testing.T) { 249 t.Helper() 250 251 tmc.mu.Lock() 252 defer tmc.mu.Unlock() 253 254 for tabletID, qrs := range tmc.vrQueries { 255 if len(qrs) != 0 { 256 var list []string 257 for _, qr := range qrs { 258 list = append(list, qr.query) 259 } 260 t.Errorf("tablet %v: following queries were not run during the test: \n%v", tabletID, strings.Join(list, "\n")) 261 } 262 } 263 }