vitess.io/vitess@v0.16.2/go/vt/wrangler/wrangler_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 "math/rand" 23 "sync" 24 "testing" 25 26 "vitess.io/vitess/go/sqltypes" 27 "vitess.io/vitess/go/vt/grpcclient" 28 "vitess.io/vitess/go/vt/log" 29 "vitess.io/vitess/go/vt/logutil" 30 "vitess.io/vitess/go/vt/topo" 31 "vitess.io/vitess/go/vt/topo/memorytopo" 32 "vitess.io/vitess/go/vt/vttablet/queryservice" 33 "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" 34 "vitess.io/vitess/go/vt/vttablet/tabletconn" 35 "vitess.io/vitess/go/vt/vttablet/tabletconntest" 36 "vitess.io/vitess/go/vt/vttablet/tmclient" 37 38 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 39 querypb "vitess.io/vitess/go/vt/proto/query" 40 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 41 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 42 ) 43 44 const ( 45 testStopPosition = "MariaDB/5-456-892" 46 testSourceGtid = "MariaDB/5-456-893" 47 testTargetPrimaryPosition = "MariaDB/6-456-892" 48 ) 49 50 type testWranglerEnv struct { 51 wr *Wrangler 52 workflow string 53 topoServ *topo.Server 54 cell string 55 tabletType topodatapb.TabletType 56 tmc *testWranglerTMClient 57 mu sync.Mutex 58 } 59 60 //---------------------------------------------- 61 // testWranglerEnv 62 63 func newWranglerTestEnv(t testing.TB, sourceShards, targetShards []string, query string, positions map[string]string, timeUpdated int64) *testWranglerEnv { 64 env := &testWranglerEnv{ 65 workflow: "wrWorkflow", 66 topoServ: memorytopo.NewServer("zone1"), 67 cell: "zone1", 68 tabletType: topodatapb.TabletType_REPLICA, 69 tmc: newTestWranglerTMClient(), 70 } 71 env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) 72 env.tmc.tablets = make(map[int]*testWranglerTablet) 73 74 // Generate a unique dialer name. 75 dialerName := fmt.Sprintf("WranglerTest-%s-%d", t.Name(), rand.Intn(1000000000)) 76 tabletconn.RegisterDialer(dialerName, func(tablet *topodatapb.Tablet, failFast grpcclient.FailFast) (queryservice.QueryService, error) { 77 env.mu.Lock() 78 defer env.mu.Unlock() 79 if qs, ok := env.tmc.tablets[int(tablet.Alias.Uid)]; ok { 80 return qs, nil 81 } 82 // some tests don't require the query service. Earlier we were returning an error for such cases but the tablet picker 83 // now logs a warning and spams the logs. Hence we return a fake service instead 84 return newFakeTestWranglerTablet(), nil 85 }) 86 tabletconntest.SetProtocol("go.vt.wrangler.wrangler_env_test", dialerName) 87 88 tabletID := 100 89 for _, shard := range sourceShards { 90 _ = env.addTablet(tabletID, "source", shard, topodatapb.TabletType_PRIMARY) 91 _ = env.addTablet(tabletID+1, "source", shard, topodatapb.TabletType_REPLICA) 92 env.tmc.waitpos[tabletID+1] = testStopPosition 93 94 tabletID += 10 95 } 96 tabletID = 200 97 for _, shard := range targetShards { 98 primary := env.addTablet(tabletID, "target", shard, topodatapb.TabletType_PRIMARY) 99 _ = env.addTablet(tabletID+1, "target", shard, topodatapb.TabletType_REPLICA) 100 101 var rows []string 102 var posRows []string 103 var bls *binlogdatapb.BinlogSource 104 for j, sourceShard := range sourceShards { 105 bls = &binlogdatapb.BinlogSource{ 106 Keyspace: "source", 107 Shard: sourceShard, 108 Filter: &binlogdatapb.Filter{ 109 Rules: []*binlogdatapb.Rule{{ 110 Match: "t1", 111 Filter: query, 112 }}, 113 }, 114 } 115 rows = append(rows, fmt.Sprintf("%d|%v||||0|0|0", j+1, bls)) 116 position := testStopPosition 117 if pos := positions[sourceShard+shard]; pos != "" { 118 position = pos 119 } 120 posRows = append(posRows, fmt.Sprintf("%v|%s", bls, position)) 121 122 env.tmc.setVRResults( 123 primary.tablet, 124 fmt.Sprintf("update _vt.vreplication set state='Running', stop_pos='%s', message='synchronizing for wrangler test' where id=%d", testSourceGtid, j+1), 125 &sqltypes.Result{}, 126 ) 127 } 128 // migrater buildMigrationTargets 129 env.tmc.setVRResults( 130 primary.tablet, 131 "select id, source, message, cell, tablet_types, workflow_type, workflow_sub_type, defer_secondary_keys from _vt.vreplication where db_name = 'vt_target' and workflow = 'wrWorkflow'", 132 sqltypes.MakeTestResult(sqltypes.MakeTestFields( 133 "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", 134 "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), 135 rows..., 136 ), 137 ) 138 139 env.tmc.setVRResults(primary.tablet, "update _vt.vreplication set state = 'Stopped', message = 'for wrangler test' where db_name = 'vt_target' and workflow = 'wrWorkflow'", &sqltypes.Result{RowsAffected: 1}) 140 env.tmc.setVRResults(primary.tablet, "update _vt.vreplication set state = 'Stopped' where db_name = 'vt_target' and workflow = 'wrWorkflow'", &sqltypes.Result{RowsAffected: 1}) 141 env.tmc.setVRResults(primary.tablet, "delete from _vt.vreplication where message != '' and db_name = 'vt_target' and workflow = 'wrWorkflow'", &sqltypes.Result{RowsAffected: 1}) 142 env.tmc.setVRResults(primary.tablet, "insert into _vt.vreplication(state, workflow, db_name) values ('Running', 'wk1', 'ks1'), ('Stopped', 'wk1', 'ks1')", &sqltypes.Result{RowsAffected: 2}) 143 144 result := sqltypes.MakeTestResult(sqltypes.MakeTestFields( 145 "id|source|pos|stop_pos|max_replication_lag|state|db_name|time_updated|transaction_timestamp|time_heartbeat|time_throttled|component_throttled|message|tags|workflow_type|workflow_sub_type", 146 "int64|varchar|varchar|varchar|int64|varchar|varchar|int64|int64|int64|int64|int64|varchar|varchar|varchar|int64|int64"), 147 fmt.Sprintf("1|%v|MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-3||0|Running|vt_target|%d|0|%d|0|||||", bls, timeUpdated, timeUpdated), 148 ) 149 env.tmc.setVRResults(primary.tablet, "select id, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, time_heartbeat, time_throttled, component_throttled, message, tags, workflow_type, workflow_sub_type, defer_secondary_keys from _vt.vreplication where db_name = 'vt_target' and workflow = 'wrWorkflow'", result) 150 env.tmc.setVRResults( 151 primary.tablet, 152 "select source, pos from _vt.vreplication where db_name='vt_target' and workflow='wrWorkflow'", 153 sqltypes.MakeTestResult(sqltypes.MakeTestFields( 154 "source|pos", 155 "varchar|varchar"), 156 posRows..., 157 ), 158 ) 159 result = sqltypes.MakeTestResult(sqltypes.MakeTestFields( 160 "workflow", 161 "varchar"), 162 "wrWorkflow", 163 ) 164 env.tmc.setVRResults(primary.tablet, "select distinct workflow from _vt.vreplication where state != 'Stopped' and db_name = 'vt_target'", result) 165 166 result = sqltypes.MakeTestResult(sqltypes.MakeTestFields( 167 "table|lastpk", 168 "varchar|varchar"), 169 "t1|pk1", 170 ) 171 172 env.tmc.setVRResults(primary.tablet, "select table_name, lastpk from _vt.copy_state where vrepl_id = 1 and id in (select max(id) from _vt.copy_state where vrepl_id = 1 group by vrepl_id, table_name)", result) 173 174 env.tmc.setVRResults(primary.tablet, "select id, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, time_heartbeat, time_throttled, component_throttled, message, tags from _vt.vreplication where db_name = 'vt_target' and workflow = 'bad'", &sqltypes.Result{}) 175 176 env.tmc.setVRResults(primary.tablet, "select id, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, time_heartbeat, time_throttled, component_throttled, message, tags from _vt.vreplication where db_name = 'vt_target' and workflow = 'badwf'", &sqltypes.Result{}) 177 env.tmc.vrpos[tabletID] = testSourceGtid 178 env.tmc.pos[tabletID] = testTargetPrimaryPosition 179 180 env.tmc.waitpos[tabletID+1] = testTargetPrimaryPosition 181 182 env.tmc.setVRResults(primary.tablet, "update _vt.vreplication set state='Running', message='', stop_pos='' where db_name='vt_target' and workflow='wrWorkflow'", &sqltypes.Result{}) 183 184 result = sqltypes.MakeTestResult(sqltypes.MakeTestFields( 185 "workflow", 186 "varchar"), 187 "wrWorkflow", "wrWorkflow2", 188 ) 189 env.tmc.setVRResults(primary.tablet, "select distinct workflow from _vt.vreplication where db_name = 'vt_target'", result) 190 tabletID += 10 191 } 192 primary := env.addTablet(300, "target2", "0", topodatapb.TabletType_PRIMARY) 193 result := sqltypes.MakeTestResult(sqltypes.MakeTestFields( 194 "workflow", 195 "varchar"), 196 "wrWorkflow", "wrWorkflow2", 197 ) 198 env.tmc.setVRResults(primary.tablet, "select distinct workflow from _vt.vreplication where db_name = 'vt_target2'", result) 199 return env 200 } 201 202 func (env *testWranglerEnv) close() { 203 env.mu.Lock() 204 defer env.mu.Unlock() 205 for _, t := range env.tmc.tablets { 206 env.topoServ.DeleteTablet(context.Background(), t.tablet.Alias) 207 } 208 env.tmc.tablets = nil 209 } 210 211 func newFakeTestWranglerTablet() *testWranglerTablet { 212 id := 999 213 tablet := &topodatapb.Tablet{ 214 Alias: &topodatapb.TabletAlias{ 215 Cell: "fake", 216 Uid: uint32(id), 217 }, 218 Keyspace: "fake", 219 Shard: "fake", 220 KeyRange: &topodatapb.KeyRange{}, 221 Type: topodatapb.TabletType_PRIMARY, 222 PortMap: map[string]int32{ 223 "test": int32(id), 224 }, 225 } 226 return newTestWranglerTablet(tablet) 227 } 228 229 func (env *testWranglerEnv) addTablet(id int, keyspace, shard string, tabletType topodatapb.TabletType) *testWranglerTablet { 230 env.mu.Lock() 231 defer env.mu.Unlock() 232 tablet := &topodatapb.Tablet{ 233 Alias: &topodatapb.TabletAlias{ 234 Cell: env.cell, 235 Uid: uint32(id), 236 }, 237 Keyspace: keyspace, 238 Shard: shard, 239 KeyRange: &topodatapb.KeyRange{}, 240 Type: tabletType, 241 PortMap: map[string]int32{ 242 "test": int32(id), 243 }, 244 } 245 env.tmc.tablets[id] = newTestWranglerTablet(tablet) 246 if err := env.wr.TopoServer().InitTablet(context.Background(), tablet, false /* allowPrimaryOverride */, true /* createShardAndKeyspace */, false /* allowUpdate */); err != nil { 247 panic(err) 248 } 249 if tabletType == topodatapb.TabletType_PRIMARY { 250 _, err := env.wr.ts.UpdateShardFields(context.Background(), keyspace, shard, func(si *topo.ShardInfo) error { 251 si.PrimaryAlias = tablet.Alias 252 return nil 253 }) 254 if err != nil { 255 panic(err) 256 } 257 } 258 env.tmc.tablets[id].queryResults = make(map[string]*querypb.QueryResult) 259 return env.tmc.tablets[id] 260 } 261 262 //---------------------------------------------- 263 // testWranglerTablet 264 265 type testWranglerTablet struct { 266 queryservice.QueryService 267 tablet *topodatapb.Tablet 268 queryResults map[string]*querypb.QueryResult 269 gotQueries []string 270 } 271 272 func newTestWranglerTablet(tablet *topodatapb.Tablet) *testWranglerTablet { 273 return &testWranglerTablet{ 274 QueryService: fakes.ErrorQueryService, 275 tablet: tablet, 276 } 277 } 278 279 func (tvt *testWranglerTablet) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error { 280 return callback(&querypb.StreamHealthResponse{ 281 Serving: true, 282 Target: &querypb.Target{ 283 Keyspace: tvt.tablet.Keyspace, 284 Shard: tvt.tablet.Shard, 285 TabletType: tvt.tablet.Type, 286 }, 287 RealtimeStats: &querypb.RealtimeStats{}, 288 }) 289 } 290 291 //---------------------------------------------- 292 // testWranglerTMClient 293 294 type testWranglerTMClient struct { 295 tmclient.TabletManagerClient 296 tablets map[int]*testWranglerTablet 297 schema *tabletmanagerdatapb.SchemaDefinition 298 vrQueries map[int]map[string]*querypb.QueryResult 299 waitpos map[int]string 300 vrpos map[int]string 301 pos map[int]string 302 } 303 304 func newTestWranglerTMClient() *testWranglerTMClient { 305 return &testWranglerTMClient{ 306 vrQueries: make(map[int]map[string]*querypb.QueryResult), 307 waitpos: make(map[int]string), 308 vrpos: make(map[int]string), 309 pos: make(map[int]string), 310 } 311 } 312 313 func (tmc *testWranglerTMClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) { 314 return tmc.schema, nil 315 } 316 317 func (tmc *testWranglerTMClient) setVRResults(tablet *topodatapb.Tablet, query string, result *sqltypes.Result) { 318 queries, ok := tmc.vrQueries[int(tablet.Alias.Uid)] 319 if !ok { 320 queries = make(map[string]*querypb.QueryResult) 321 tmc.vrQueries[int(tablet.Alias.Uid)] = queries 322 } 323 queries[query] = sqltypes.ResultToProto3(result) 324 } 325 326 func (tmc *testWranglerTMClient) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) { 327 result, ok := tmc.vrQueries[int(tablet.Alias.Uid)][query] 328 if !ok { 329 return nil, fmt.Errorf("query %q not found for tablet %d", query, tablet.Alias.Uid) 330 } 331 return result, nil 332 } 333 334 func (tmc *testWranglerTMClient) ExecuteFetchAsApp(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsAppRequest) (*querypb.QueryResult, error) { 335 t := tmc.tablets[int(tablet.Alias.Uid)] 336 t.gotQueries = append(t.gotQueries, string(req.Query)) 337 result, ok := t.queryResults[string(req.Query)] 338 if !ok { 339 result = &querypb.QueryResult{} 340 log.Errorf("Query: %s, Result :%v\n", string(req.Query), result) 341 } 342 return result, nil 343 }