vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vdiff/framework_test.go (about) 1 /* 2 Copyright 2022 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 vdiff 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "os" 24 "regexp" 25 "strings" 26 "sync" 27 "testing" 28 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 32 "vitess.io/vitess/go/mysql" 33 "vitess.io/vitess/go/sqltypes" 34 "vitess.io/vitess/go/vt/binlog/binlogplayer" 35 "vitess.io/vitess/go/vt/grpcclient" 36 "vitess.io/vitess/go/vt/sqlparser" 37 "vitess.io/vitess/go/vt/topo" 38 "vitess.io/vitess/go/vt/vttablet/queryservice" 39 "vitess.io/vitess/go/vt/vttablet/tabletconn" 40 "vitess.io/vitess/go/vt/vttablet/tabletconntest" 41 "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" 42 "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" 43 "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" 44 "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" 45 "vitess.io/vitess/go/vt/vttablet/tmclient" 46 "vitess.io/vitess/go/vt/vttablet/tmclienttest" 47 48 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 49 querypb "vitess.io/vitess/go/vt/proto/query" 50 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 51 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 52 ) 53 54 const ( 55 optionsJS = `{"core_options": {"auto_retry": true}}` 56 vdiffTestCols = "id|vdiff_uuid|workflow|keyspace|shard|db_name|state|options|last_error" 57 vdiffTestColTypes = "int64|varchar|varbinary|varbinary|varchar|varbinary|varbinary|json|varbinary" 58 // This is also the keyspace name used 59 vdiffDBName = "vttest" 60 61 // vdiffSourceGtid should be the position reported by the source side VStreamResults. 62 vdiffSourceGtid = "MySQL56/f69ed286-6909-11ed-8342-0a50724f3211:1-110" 63 vdiffTargetPrimaryPosition = vdiffSourceGtid 64 ) 65 66 var ( 67 vreplSource = fmt.Sprintf(`keyspace:"%s" shard:"0" filter:{rules:{match:"t1" filter:"select * from t1"}}`, vdiffDBName) 68 singleRowAffected = &sqltypes.Result{RowsAffected: 1} 69 noResults = &sqltypes.Result{} 70 testSchema = &tabletmanagerdatapb.SchemaDefinition{ 71 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 72 { 73 Name: "t1", 74 Columns: []string{"c1", "c2"}, 75 PrimaryKeyColumns: []string{"c1"}, 76 Fields: sqltypes.MakeTestFields("c1|c2", "int64|int64"), 77 }, { 78 Name: "nonpktext", 79 Columns: []string{"c1", "textcol"}, 80 PrimaryKeyColumns: []string{"c1"}, 81 Fields: sqltypes.MakeTestFields("c1|textcol", "int64|varchar"), 82 }, { 83 Name: "pktext", 84 Columns: []string{"textcol", "c2"}, 85 PrimaryKeyColumns: []string{"textcol"}, 86 Fields: sqltypes.MakeTestFields("textcol|c2", "varchar|int64"), 87 }, { 88 Name: "multipk", 89 Columns: []string{"c1", "c2"}, 90 PrimaryKeyColumns: []string{"c1", "c2"}, 91 Fields: sqltypes.MakeTestFields("c1|c2", "int64|int64"), 92 }, { 93 Name: "aggr", 94 Columns: []string{"c1", "c2", "c3", "c4"}, 95 PrimaryKeyColumns: []string{"c1"}, 96 Fields: sqltypes.MakeTestFields("c1|c2|c3|c4", "int64|int64|int64|int64"), 97 }, { 98 Name: "datze", 99 Columns: []string{"id", "dt"}, 100 PrimaryKeyColumns: []string{"id"}, 101 Fields: sqltypes.MakeTestFields("id|dt", "int64|datetime"), 102 }, 103 }, 104 } 105 tableDefMap = map[string]int{ 106 "t1": 0, 107 "nonpktext": 1, 108 "pktext": 2, 109 "multipk": 3, 110 "aggr": 4, 111 "datze": 5, 112 } 113 ) 114 115 type testVDiffEnv struct { 116 workflow string 117 se *schema.Engine 118 vse *vstreamer.Engine 119 vre *vreplication.Engine 120 vde *Engine 121 tmc *fakeTMClient 122 opts *tabletmanagerdatapb.VDiffOptions 123 dbClient *binlogplayer.MockDBClient 124 dbClientFactory func() binlogplayer.DBClient 125 tmClientFactory func() tmclient.TabletManagerClient 126 127 mu sync.Mutex 128 tablets map[int]*fakeTabletConn 129 } 130 131 var ( 132 // vdiffenv has to be a global for RegisterDialer to work. 133 vdiffenv *testVDiffEnv 134 tstenv *testenv.Env 135 globalFBC = &fakeBinlogClient{} 136 globalDBQueries = make(chan string, 1000) 137 doNotLogDBQueries = false 138 once sync.Once 139 ) 140 141 type LogExpectation struct { 142 Type string 143 Detail string 144 } 145 146 func init() { 147 tabletconn.RegisterDialer("test", func(tablet *topodatapb.Tablet, failFast grpcclient.FailFast) (queryservice.QueryService, error) { 148 vdiffenv.mu.Lock() 149 defer vdiffenv.mu.Unlock() 150 if qs, ok := vdiffenv.tablets[int(tablet.Alias.Uid)]; ok { 151 return qs, nil 152 } 153 return nil, fmt.Errorf("tablet %d not found", tablet.Alias.Uid) 154 }) 155 // TableDiffer does a default grpc dial just to be sure it can talk to the tablet. 156 tabletconn.RegisterDialer("grpc", func(tablet *topodatapb.Tablet, failFast grpcclient.FailFast) (queryservice.QueryService, error) { 157 vdiffenv.mu.Lock() 158 defer vdiffenv.mu.Unlock() 159 if qs, ok := vdiffenv.tablets[int(tablet.Alias.Uid)]; ok { 160 return qs, nil 161 } 162 return nil, fmt.Errorf("tablet %d not found", tablet.Alias.Uid) 163 }) 164 tabletconntest.SetProtocol("go.vt.vttablet.tabletmanager.vdiff.framework_test", "test") 165 166 binlogplayer.RegisterClientFactory("test", func() binlogplayer.Client { return globalFBC }) 167 binlogplayer.SetProtocol("vdiff_test_framework", "test") 168 169 tmclient.RegisterTabletManagerClientFactory("test", func() tmclient.TabletManagerClient { return vdiffenv.tmc }) 170 tmclienttest.SetProtocol("go.vt.vttablet.tabletmanager.vdiff.framework_test", "test") 171 } 172 173 func TestMain(m *testing.M) { 174 exitCode := func() int { 175 var err error 176 tstenv, err = testenv.Init() 177 if err != nil { 178 fmt.Fprintf(os.Stderr, "%v", err) 179 return 1 180 } 181 defer tstenv.Close() 182 183 return m.Run() 184 }() 185 os.Exit(exitCode) 186 } 187 188 func resetBinlogClient() { 189 globalFBC = &fakeBinlogClient{} 190 } 191 192 // shortCircuitTestAfterQuery is used to short circuit a test after a specific query is executed. 193 // This can be used to end a vdiff, by returning an error from the specified query, once the test 194 // has verified the necessary behavior. 195 func shortCircuitTestAfterQuery(query string, dbClient *binlogplayer.MockDBClient) { 196 dbClient.ExpectRequest(query, singleRowAffected, fmt.Errorf("Short circuiting test")) 197 dbClient.ExpectRequest("update _vt.vdiff set state = 'error', last_error = 'Short circuiting test' where id = 1", singleRowAffected, nil) 198 dbClient.ExpectRequest("insert into _vt.vdiff_log(vdiff_id, message) values (1, 'State changed to: error')", singleRowAffected, nil) 199 dbClient.ExpectRequest("insert into _vt.vdiff_log(vdiff_id, message) values (1, 'Error: Short circuiting test')", singleRowAffected, nil) 200 } 201 202 //-------------------------------------- 203 // Topos and tablets 204 205 // fakeTabletConn implement TabletConn interface. We only care about the 206 // health check part. The state reported by the tablet will depend 207 // on the Tag values "serving" and "healthy". 208 type fakeTabletConn struct { 209 queryservice.QueryService 210 tablet *topodatapb.Tablet 211 } 212 213 // StreamHealth is part of queryservice.QueryService. 214 func (ftc *fakeTabletConn) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error { 215 return callback(&querypb.StreamHealthResponse{ 216 Serving: true, 217 Target: &querypb.Target{ 218 Keyspace: ftc.tablet.Keyspace, 219 Shard: ftc.tablet.Shard, 220 TabletType: ftc.tablet.Type, 221 }, 222 RealtimeStats: &querypb.RealtimeStats{}, 223 }) 224 } 225 226 // vstreamHook allows you to do work just before calling VStream. 227 var vstreamHook func(ctx context.Context) 228 229 // VStream directly calls into the pre-initialized engine. 230 func (ftc *fakeTabletConn) VStream(ctx context.Context, request *binlogdatapb.VStreamRequest, send func([]*binlogdatapb.VEvent) error) error { 231 if request.Target.Keyspace != vdiffDBName { 232 <-ctx.Done() 233 return io.EOF 234 } 235 if vstreamHook != nil { 236 vstreamHook(ctx) 237 } 238 return vdiffenv.vse.Stream(ctx, request.Position, request.TableLastPKs, request.Filter, send) 239 } 240 241 // vstreamRowsHook allows you to do work just before calling VStreamRows. 242 var vstreamRowsHook func(ctx context.Context) 243 244 // vstreamRowsSendHook allows you to do work just before VStreamRows calls send. 245 var vstreamRowsSendHook func(ctx context.Context) 246 247 // VStreamRows directly calls into the pre-initialized engine. 248 func (ftc *fakeTabletConn) VStreamRows(ctx context.Context, request *binlogdatapb.VStreamRowsRequest, send func(*binlogdatapb.VStreamRowsResponse) error) error { 249 if vstreamRowsHook != nil { 250 vstreamRowsHook(ctx) 251 } 252 var row []sqltypes.Value 253 if request.Lastpk != nil { 254 r := sqltypes.Proto3ToResult(request.Lastpk) 255 if len(r.Rows) != 1 { 256 return fmt.Errorf("unexpected lastpk input: %v", request.Lastpk) 257 } 258 row = r.Rows[0] 259 } 260 return vdiffenv.vse.StreamRows(ctx, request.Query, row, func(rows *binlogdatapb.VStreamRowsResponse) error { 261 if vstreamRowsSendHook != nil { 262 vstreamRowsSendHook(ctx) 263 } 264 return send(rows) 265 }) 266 } 267 268 func (ftc *fakeTabletConn) Close(ctx context.Context) error { 269 return nil 270 } 271 272 //-------------------------------------- 273 // Binlog Client to TabletManager 274 275 // fakeBinlogClient satisfies binlogplayer.Client. 276 // Not to be used concurrently. 277 type fakeBinlogClient struct { 278 lastTablet *topodatapb.Tablet 279 lastPos string 280 lastTables []string 281 lastKeyRange *topodatapb.KeyRange 282 lastCharset *binlogdatapb.Charset 283 } 284 285 func (fbc *fakeBinlogClient) Dial(tablet *topodatapb.Tablet) error { 286 fbc.lastTablet = tablet 287 return nil 288 } 289 290 func (fbc *fakeBinlogClient) Close() { 291 } 292 293 func (fbc *fakeBinlogClient) StreamTables(ctx context.Context, position string, tables []string, charset *binlogdatapb.Charset) (binlogplayer.BinlogTransactionStream, error) { 294 fbc.lastPos = position 295 fbc.lastTables = tables 296 fbc.lastCharset = charset 297 return &btStream{ctx: ctx}, nil 298 } 299 300 func (fbc *fakeBinlogClient) StreamKeyRange(ctx context.Context, position string, keyRange *topodatapb.KeyRange, charset *binlogdatapb.Charset) (binlogplayer.BinlogTransactionStream, error) { 301 fbc.lastPos = position 302 fbc.lastKeyRange = keyRange 303 fbc.lastCharset = charset 304 return &btStream{ctx: ctx}, nil 305 } 306 307 // btStream satisfies binlogplayer.BinlogTransactionStream 308 type btStream struct { 309 ctx context.Context 310 sent bool 311 } 312 313 func (bts *btStream) Recv() (*binlogdatapb.BinlogTransaction, error) { 314 if !bts.sent { 315 bts.sent = true 316 return &binlogdatapb.BinlogTransaction{ 317 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 318 { 319 Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, 320 Sql: []byte("insert into t values(1)"), 321 }, 322 }, 323 EventToken: &querypb.EventToken{ 324 Timestamp: 72, 325 Position: "MariaDB/0-1-1235", 326 }, 327 }, nil 328 } 329 <-bts.ctx.Done() 330 return nil, bts.ctx.Err() 331 } 332 333 //-------------------------------------- 334 // DBCLient wrapper 335 336 func realDBClientFactory() binlogplayer.DBClient { 337 return &realDBClient{} 338 } 339 340 type realDBClient struct { 341 conn *mysql.Conn 342 nolog bool 343 } 344 345 func (dbc *realDBClient) DBName() string { 346 return vdiffDBName 347 } 348 349 func (dbc *realDBClient) Connect() error { 350 app, err := tstenv.Dbcfgs.AppWithDB().MysqlParams() 351 if err != nil { 352 return err 353 } 354 app.DbName = vdiffDBName 355 conn, err := mysql.Connect(context.Background(), app) 356 if err != nil { 357 return err 358 } 359 dbc.conn = conn 360 return nil 361 } 362 363 func (dbc *realDBClient) Begin() error { 364 _, err := dbc.ExecuteFetch("begin", 10000) 365 return err 366 } 367 368 func (dbc *realDBClient) Commit() error { 369 _, err := dbc.ExecuteFetch("commit", 10000) 370 return err 371 } 372 373 func (dbc *realDBClient) Rollback() error { 374 _, err := dbc.ExecuteFetch("rollback", 10000) 375 return err 376 } 377 378 func (dbc *realDBClient) Close() { 379 dbc.conn.Close() 380 } 381 382 func (dbc *realDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Result, error) { 383 // Use Clone() because the contents of memory region referenced by 384 // string can change when clients (e.g. vcopier) use unsafe string methods. 385 query = strings.Clone(query) 386 qr, err := dbc.conn.ExecuteFetch(query, 10000, true) 387 if doNotLogDBQueries { 388 return qr, err 389 } 390 if !strings.HasPrefix(query, "select") && !strings.HasPrefix(query, "set") && !dbc.nolog { 391 globalDBQueries <- query 392 } 393 return qr, err 394 } 395 396 //---------------------------------------------- 397 // fakeTMClient 398 399 type fakeTMClient struct { 400 tmclient.TabletManagerClient 401 schema *tabletmanagerdatapb.SchemaDefinition 402 vrQueries map[int]map[string]*querypb.QueryResult 403 waitpos map[int]string 404 vrpos map[int]string 405 pos map[int]string 406 } 407 408 func newFakeTMClient() *fakeTMClient { 409 return &fakeTMClient{ 410 vrQueries: make(map[int]map[string]*querypb.QueryResult), 411 waitpos: make(map[int]string), 412 vrpos: make(map[int]string), 413 pos: make(map[int]string), 414 } 415 } 416 417 func (tmc *fakeTMClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) { 418 return tmc.schema, nil 419 } 420 421 // setVRResults allows you to specify VReplicationExec queries and their results. You can specify 422 // exact strings or strings prefixed with a '/', in which case they will be treated as a valid 423 // regexp. 424 func (tmc *fakeTMClient) setVRResults(tablet *topodatapb.Tablet, query string, result *sqltypes.Result) { 425 queries, ok := tmc.vrQueries[int(tablet.Alias.Uid)] 426 if !ok { 427 queries = make(map[string]*querypb.QueryResult) 428 tmc.vrQueries[int(tablet.Alias.Uid)] = queries 429 } 430 queries[query] = sqltypes.ResultToProto3(result) 431 } 432 433 func (tmc *fakeTMClient) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) { 434 if result, ok := tmc.vrQueries[int(tablet.Alias.Uid)][query]; ok { 435 return result, nil 436 } 437 for qry, res := range tmc.vrQueries[int(tablet.Alias.Uid)] { 438 if strings.HasPrefix(qry, "/") { 439 re := regexp.MustCompile(qry) 440 if re.MatchString(qry) { 441 return res, nil 442 } 443 } 444 } 445 return nil, fmt.Errorf("query %q not found for tablet %d", query, tablet.Alias.Uid) 446 } 447 448 func (tmc *fakeTMClient) WaitForPosition(ctx context.Context, tablet *topodatapb.Tablet, pos string) error { 449 select { 450 case <-ctx.Done(): 451 return ctx.Err() 452 default: 453 } 454 if pos != tmc.waitpos[int(tablet.Alias.Uid)] { 455 return fmt.Errorf("waitpos %s not reached for tablet %d", pos, tablet.Alias.Uid) 456 } 457 return nil 458 } 459 460 func (tmc *fakeTMClient) VReplicationWaitForPos(ctx context.Context, tablet *topodatapb.Tablet, id int, pos string) error { 461 select { 462 case <-ctx.Done(): 463 return ctx.Err() 464 default: 465 } 466 if pos != tmc.vrpos[int(tablet.Alias.Uid)] { 467 return fmt.Errorf("vrpos %s not reached for tablet %d", pos, tablet.Alias.Uid) 468 } 469 return nil 470 } 471 472 func (tmc *fakeTMClient) PrimaryPosition(ctx context.Context, tablet *topodatapb.Tablet) (string, error) { 473 pos, ok := tmc.pos[int(tablet.Alias.Uid)] 474 if !ok { 475 return "", fmt.Errorf("no primary position for %d", tablet.Alias.Uid) 476 } 477 return pos, nil 478 } 479 480 // ---------------------------------------------- 481 // testVDiffEnv 482 483 func newTestVDiffEnv(t *testing.T) *testVDiffEnv { 484 vdiffenv = &testVDiffEnv{ 485 workflow: "testwf", 486 tablets: make(map[int]*fakeTabletConn), 487 tmc: newFakeTMClient(), 488 se: schema.NewEngineForTests(), 489 dbClient: binlogplayer.NewMockDBClient(t), 490 } 491 vdiffenv.dbClientFactory = func() binlogplayer.DBClient { return vdiffenv.dbClient } 492 vdiffenv.tmClientFactory = func() tmclient.TabletManagerClient { return vdiffenv.tmc } 493 494 tstenv.KeyspaceName = vdiffDBName 495 496 vdiffenv.vse = vstreamer.NewEngine(tstenv.TabletEnv, tstenv.SrvTopo, vdiffenv.se, nil, tstenv.Cells[0]) 497 vdiffenv.vse.InitDBConfig(tstenv.KeyspaceName, tstenv.ShardName) 498 vdiffenv.vse.Open() 499 500 once.Do(func() { 501 var ddls []string 502 503 // This is needed for the vstreamer engine and the snapshotConn which 504 // use the real DB started by vttestserver 505 ddls = append(ddls, fmt.Sprintf("create database if not exists %s", vdiffDBName)) 506 ddls = append(ddls, fmt.Sprintf("create table if not exists %s.t1 (c1 bigint primary key, c2 bigint)", vdiffDBName)) 507 508 for _, ddl := range ddls { 509 if err := tstenv.Mysqld.ExecuteSuperQuery(context.Background(), ddl); err != nil { 510 fmt.Fprintf(os.Stderr, "%v", err) 511 } 512 } 513 }) 514 515 vdiffenv.vre = vreplication.NewSimpleTestEngine(tstenv.TopoServ, tstenv.Cells[0], tstenv.Mysqld, realDBClientFactory, realDBClientFactory, vdiffDBName, nil) 516 vdiffenv.vre.Open(context.Background()) 517 518 vdiffenv.tmc.schema = testSchema 519 // We need to add t1, which we use for a full VDiff in TestVDiff, to 520 // the schema engine with the PK val. 521 st := &schema.Table{ 522 Name: sqlparser.NewIdentifierCS(testSchema.TableDefinitions[tableDefMap["t1"]].Name), 523 Fields: testSchema.TableDefinitions[tableDefMap["t1"]].Fields, 524 PKColumns: []int{0}, 525 } 526 vdiffenv.se.SetTableForTests(st) 527 528 tabletID := 100 529 primary := vdiffenv.addTablet(tabletID, tstenv.KeyspaceName, tstenv.ShardName, topodatapb.TabletType_PRIMARY) 530 531 vdiffenv.vde = NewTestEngine(tstenv.TopoServ, primary.tablet, vdiffDBName, vdiffenv.dbClientFactory, vdiffenv.tmClientFactory) 532 require.False(t, vdiffenv.vde.IsOpen()) 533 534 vdiffenv.opts = &tabletmanagerdatapb.VDiffOptions{ 535 CoreOptions: &tabletmanagerdatapb.VDiffCoreOptions{}, 536 ReportOptions: &tabletmanagerdatapb.VDiffReportOptions{ 537 Format: "json", 538 OnlyPks: true, 539 DebugQuery: true, 540 }, 541 } 542 543 // vdiff.syncTargets. This actually happens after stopTargets. 544 // But this is one statement per stream. 545 vdiffenv.tmc.setVRResults( 546 primary.tablet, 547 "/update _vt.vreplication set state='Running', stop_pos='MySQL56/.*', message='synchronizing for vdiff' where id=1", 548 noResults, 549 ) 550 551 // vdiff.stopTargets 552 vdiffenv.tmc.setVRResults(primary.tablet, fmt.Sprintf("update _vt.vreplication set state='Stopped', message='for vdiff' where workflow = '%s' and db_name = '%s'", vdiffenv.workflow, vdiffDBName), singleRowAffected) 553 554 // vdiff.syncTargets (continued) 555 vdiffenv.tmc.vrpos[tabletID] = vdiffSourceGtid 556 vdiffenv.tmc.pos[tabletID] = vdiffTargetPrimaryPosition 557 558 // vdiff.startQueryStreams 559 vdiffenv.tmc.waitpos[tabletID] = vdiffTargetPrimaryPosition 560 561 // vdiff.restartTargets 562 vdiffenv.tmc.setVRResults(primary.tablet, fmt.Sprintf("update _vt.vreplication set state='Running', message='', stop_pos='' where db_name='%s' and workflow='%s'", vdiffDBName, vdiffenv.workflow), singleRowAffected) 563 564 vdiffenv.dbClient.ExpectRequest("select * from _vt.vdiff where state in ('started','pending')", noResults, nil) 565 vdiffenv.vde.Open(context.Background(), vdiffenv.vre) 566 assert.True(t, vdiffenv.vde.IsOpen()) 567 assert.Equal(t, 0, len(vdiffenv.vde.controllers)) 568 569 resetBinlogClient() 570 571 return vdiffenv 572 } 573 574 func (tvde *testVDiffEnv) close() { 575 tvde.mu.Lock() 576 defer tvde.mu.Unlock() 577 for _, t := range tvde.tablets { 578 tstenv.TopoServ.DeleteTablet(context.Background(), t.tablet.Alias) 579 topo.DeleteTabletReplicationData(context.Background(), tstenv.TopoServ, t.tablet) 580 tstenv.SchemaEngine.Reload(context.Background()) 581 } 582 tvde.tablets = nil 583 vdiffenv.vse.Close() 584 vdiffenv.vre.Close() 585 vdiffenv.vde.Close() 586 vdiffenv.dbClient.Close() 587 } 588 589 func (tvde *testVDiffEnv) addTablet(id int, keyspace, shard string, tabletType topodatapb.TabletType) *fakeTabletConn { 590 tvde.mu.Lock() 591 defer tvde.mu.Unlock() 592 tablet := &topodatapb.Tablet{ 593 Alias: &topodatapb.TabletAlias{ 594 Cell: tstenv.Cells[0], 595 Uid: uint32(id), 596 }, 597 Keyspace: keyspace, 598 Shard: shard, 599 KeyRange: &topodatapb.KeyRange{}, 600 Type: tabletType, 601 PortMap: map[string]int32{ 602 "test": int32(id), 603 }, 604 } 605 tvde.tablets[id] = &fakeTabletConn{tablet: tablet} 606 if err := tstenv.TopoServ.InitTablet(context.Background(), tablet, false /* allowPrimaryOverride */, true /* createShardAndKeyspace */, false /* allowUpdate */); err != nil { 607 panic(err) 608 } 609 if tabletType == topodatapb.TabletType_PRIMARY { 610 _, err := tstenv.TopoServ.UpdateShardFields(context.Background(), keyspace, shard, func(si *topo.ShardInfo) error { 611 si.PrimaryAlias = tablet.Alias 612 return nil 613 }) 614 if err != nil { 615 panic(err) 616 } 617 } 618 tstenv.SchemaEngine.Reload(context.Background()) 619 return tvde.tablets[id] 620 }