vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_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 vreplication 18 19 import ( 20 "context" 21 "fmt" 22 "math" 23 "os" 24 "strconv" 25 "strings" 26 "sync" 27 "testing" 28 "time" 29 30 "vitess.io/vitess/go/mysql" 31 32 "github.com/spyzhov/ajson" 33 "github.com/stretchr/testify/require" 34 35 "vitess.io/vitess/go/vt/log" 36 37 "vitess.io/vitess/go/sqltypes" 38 "vitess.io/vitess/go/vt/binlog/binlogplayer" 39 40 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 41 qh "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication/queryhistory" 42 ) 43 44 func TestPlayerInvisibleColumns(t *testing.T) { 45 if !supportsInvisibleColumns() { 46 t.Skip() 47 } 48 defer deleteTablet(addTablet(100)) 49 50 execStatements(t, []string{ 51 "create table t1(id int, val varchar(20), id2 int invisible, pk2 int invisible, primary key(id, pk2))", 52 fmt.Sprintf("create table %s.t1(id int, val varchar(20), id2 int invisible, pk2 int invisible, primary key(id, pk2))", vrepldb), 53 }) 54 defer execStatements(t, []string{ 55 "drop table t1", 56 fmt.Sprintf("drop table %s.t1", vrepldb), 57 }) 58 env.SchemaEngine.Reload(context.Background()) 59 60 filter := &binlogdatapb.Filter{ 61 Rules: []*binlogdatapb.Rule{{ 62 Match: "t1", 63 Filter: "select * from t1", 64 }}, 65 } 66 bls := &binlogdatapb.BinlogSource{ 67 Keyspace: env.KeyspaceName, 68 Shard: env.ShardName, 69 Filter: filter, 70 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 71 } 72 cancel, _ := startVReplication(t, bls, "") 73 defer cancel() 74 75 testcases := []struct { 76 input string 77 output string 78 table string 79 data [][]string 80 query string 81 queryResult [][]string 82 }{{ 83 input: "insert into t1(id,val,id2,pk2) values (1,'aaa',10,100)", 84 output: "insert into t1(id,val,id2,pk2) values (1,'aaa',10,100)", 85 table: "t1", 86 data: [][]string{ 87 {"1", "aaa"}, 88 }, 89 query: "select id, val, id2, pk2 from t1", 90 queryResult: [][]string{ 91 {"1", "aaa", "10", "100"}, 92 }, 93 }} 94 95 for _, tcases := range testcases { 96 execStatements(t, []string{tcases.input}) 97 output := qh.Expect(tcases.output) 98 expectNontxQueries(t, output) 99 time.Sleep(1 * time.Second) 100 log.Flush() 101 if tcases.table != "" { 102 expectData(t, tcases.table, tcases.data) 103 } 104 if tcases.query != "" { 105 expectQueryResult(t, tcases.query, tcases.queryResult) 106 } 107 } 108 } 109 110 func TestHeartbeatFrequencyFlag(t *testing.T) { 111 origVReplicationHeartbeatUpdateInterval := vreplicationHeartbeatUpdateInterval 112 defer func() { 113 vreplicationHeartbeatUpdateInterval = origVReplicationHeartbeatUpdateInterval 114 }() 115 116 stats := binlogplayer.NewStats() 117 vp := &vplayer{vr: &vreplicator{dbClient: newVDBClient(realDBClientFactory(), stats), stats: stats}} 118 119 type testcount struct { 120 count int 121 mustUpdate bool 122 } 123 type testcase struct { 124 name string 125 interval int 126 counts []testcount 127 } 128 testcases := []*testcase{ 129 {"default frequency", 1, []testcount{{count: 0, mustUpdate: false}, {1, true}}}, 130 {"custom frequency", 4, []testcount{{count: 0, mustUpdate: false}, {count: 3, mustUpdate: false}, {4, true}}}, 131 {"minumum frequency", 61, []testcount{{count: 59, mustUpdate: false}, {count: 60, mustUpdate: true}, {61, true}}}, 132 } 133 for _, tcase := range testcases { 134 t.Run(tcase.name, func(t *testing.T) { 135 vreplicationHeartbeatUpdateInterval = tcase.interval 136 for _, tcount := range tcase.counts { 137 vp.numAccumulatedHeartbeats = tcount.count 138 require.Equal(t, tcount.mustUpdate, vp.mustUpdateHeartbeat()) 139 } 140 }) 141 } 142 } 143 144 func TestVReplicationTimeUpdated(t *testing.T) { 145 ctx := context.Background() 146 defer deleteTablet(addTablet(100)) 147 execStatements(t, []string{ 148 "create table t1(id int, val varbinary(128), primary key(id))", 149 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 150 }) 151 defer execStatements(t, []string{ 152 "drop table t1", 153 fmt.Sprintf("drop table %s.t1", vrepldb), 154 }) 155 env.SchemaEngine.Reload(context.Background()) 156 157 filter := &binlogdatapb.Filter{ 158 Rules: []*binlogdatapb.Rule{{ 159 Match: "/.*", 160 }}, 161 } 162 bls := &binlogdatapb.BinlogSource{ 163 Keyspace: env.KeyspaceName, 164 Shard: env.ShardName, 165 Filter: filter, 166 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 167 } 168 cancel, _ := startVReplication(t, bls, "") 169 defer cancel() 170 171 execStatements(t, []string{ 172 "insert into t1 values(1, 'aaa')", 173 }) 174 175 var getTimestamps = func() (int64, int64, int64) { 176 qr, err := env.Mysqld.FetchSuperQuery(ctx, "select time_updated, transaction_timestamp, time_heartbeat from _vt.vreplication") 177 require.NoError(t, err) 178 require.NotNil(t, qr) 179 require.Equal(t, 1, len(qr.Rows)) 180 row := qr.Named().Row() 181 timeUpdated, err := row.ToInt64("time_updated") 182 require.NoError(t, err) 183 transactionTimestamp, err := row.ToInt64("transaction_timestamp") 184 require.NoError(t, err) 185 timeHeartbeat, err := row.ToInt64("time_heartbeat") 186 require.NoError(t, err) 187 return timeUpdated, transactionTimestamp, timeHeartbeat 188 } 189 expectNontxQueries(t, qh.Expect("insert into t1(id,val) values (1,'aaa')")) 190 time.Sleep(1 * time.Second) 191 timeUpdated1, transactionTimestamp1, timeHeartbeat1 := getTimestamps() 192 time.Sleep(2 * time.Second) 193 timeUpdated2, _, timeHeartbeat2 := getTimestamps() 194 require.Greater(t, timeUpdated2, timeUpdated1, "time_updated not updated") 195 require.Greater(t, timeUpdated2, transactionTimestamp1, "transaction_timestamp should not be < time_updated") 196 require.Greater(t, timeHeartbeat2, timeHeartbeat1, "time_heartbeat not updated") 197 } 198 199 func TestCharPK(t *testing.T) { 200 defer deleteTablet(addTablet(100)) 201 202 execStatements(t, []string{ 203 "create table t1(id int, val binary(2), primary key(val))", 204 fmt.Sprintf("create table %s.t1(id int, val binary(2), primary key(val))", vrepldb), 205 "create table t2(id int, val char(2), primary key(val))", 206 fmt.Sprintf("create table %s.t2(id int, val char(2), primary key(val))", vrepldb), 207 "create table t3(id int, val varbinary(2), primary key(val))", 208 fmt.Sprintf("create table %s.t3(id int, val varbinary(2), primary key(val))", vrepldb), 209 "create table t4(id int, val varchar(2), primary key(val))", 210 fmt.Sprintf("create table %s.t4(id int, val varchar(2), primary key(val))", vrepldb), 211 }) 212 defer execStatements(t, []string{ 213 "drop table t1", 214 fmt.Sprintf("drop table %s.t1", vrepldb), 215 "drop table t2", 216 fmt.Sprintf("drop table %s.t2", vrepldb), 217 "drop table t3", 218 fmt.Sprintf("drop table %s.t3", vrepldb), 219 "drop table t4", 220 fmt.Sprintf("drop table %s.t4", vrepldb), 221 }) 222 env.SchemaEngine.Reload(context.Background()) 223 224 filter := &binlogdatapb.Filter{ 225 Rules: []*binlogdatapb.Rule{{ 226 Match: "t1", 227 Filter: "select * from t1", 228 }, { 229 Match: "t2", 230 Filter: "select * from t2", 231 }, { 232 Match: "t3", 233 Filter: "select * from t3", 234 }, { 235 Match: "t4", 236 Filter: "select * from t4", 237 }}, 238 } 239 bls := &binlogdatapb.BinlogSource{ 240 Keyspace: env.KeyspaceName, 241 Shard: env.ShardName, 242 Filter: filter, 243 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 244 } 245 cancel, _ := startVReplication(t, bls, "") 246 defer cancel() 247 248 testcases := []struct { 249 input string 250 output string 251 table string 252 data [][]string 253 }{{ //binary(2) 254 input: "insert into t1 values(1, 'a')", 255 output: "insert into t1(id,val) values (1,'a\\0')", 256 table: "t1", 257 data: [][]string{ 258 {"1", "a\000"}, 259 }, 260 }, { 261 input: "update t1 set id = 2 where val = 'a\000'", 262 output: "update t1 set id=2 where val='a\\0'", 263 table: "t1", 264 data: [][]string{ 265 {"2", "a\000"}, 266 }, 267 }, { //char(2) 268 input: "insert into t2 values(1, 'a')", 269 output: "insert into t2(id,val) values (1,'a')", 270 table: "t2", 271 data: [][]string{ 272 {"1", "a"}, 273 }, 274 }, { 275 input: "update t2 set id = 2 where val = 'a'", 276 output: "update t2 set id=2 where val='a'", 277 table: "t2", 278 data: [][]string{ 279 {"2", "a"}, 280 }, 281 }, { //varbinary(2) 282 input: "insert into t3 values(1, 'a')", 283 output: "insert into t3(id,val) values (1,'a')", 284 table: "t3", 285 data: [][]string{ 286 {"1", "a"}, 287 }, 288 }, { 289 input: "update t3 set id = 2 where val = 'a'", 290 output: "update t3 set id=2 where val='a'", 291 table: "t3", 292 data: [][]string{ 293 {"2", "a"}, 294 }, 295 }, { //varchar(2) 296 input: "insert into t4 values(1, 'a')", 297 output: "insert into t4(id,val) values (1,'a')", 298 table: "t4", 299 data: [][]string{ 300 {"1", "a"}, 301 }, 302 }, { 303 input: "update t4 set id = 2 where val = 'a'", 304 output: "update t4 set id=2 where val='a'", 305 table: "t4", 306 data: [][]string{ 307 {"2", "a"}, 308 }, 309 }} 310 311 for _, tcases := range testcases { 312 execStatements(t, []string{tcases.input}) 313 output := qh.Expect( 314 "begin", 315 tcases.output, 316 "/update _vt.vreplication set pos", 317 "commit", 318 ) 319 expectDBClientQueries(t, output) 320 if tcases.table != "" { 321 expectData(t, tcases.table, tcases.data) 322 } 323 } 324 } 325 326 func TestRollup(t *testing.T) { 327 defer deleteTablet(addTablet(100)) 328 329 execStatements(t, []string{ 330 "create table t1(id int, val varchar(20), primary key(id))", 331 fmt.Sprintf("create table %s.t1(rollupname varchar(20), kount int, primary key(rollupname))", vrepldb), 332 }) 333 defer execStatements(t, []string{ 334 "drop table t1", 335 fmt.Sprintf("drop table %s.t1", vrepldb), 336 }) 337 env.SchemaEngine.Reload(context.Background()) 338 339 filter := &binlogdatapb.Filter{ 340 Rules: []*binlogdatapb.Rule{{ 341 Match: "t1", 342 Filter: "select 'total' as rollupname, count(*) as kount from t1 group by rollupname", 343 }}, 344 } 345 bls := &binlogdatapb.BinlogSource{ 346 Keyspace: env.KeyspaceName, 347 Shard: env.ShardName, 348 Filter: filter, 349 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 350 } 351 cancel, _ := startVReplication(t, bls, "") 352 defer cancel() 353 354 testcases := []struct { 355 input string 356 output string 357 table string 358 data [][]string 359 }{{ 360 // Start with all nulls 361 input: "insert into t1 values(1, 'a')", 362 output: "insert into t1(rollupname,kount) values ('total',1) on duplicate key update kount=kount+1", 363 table: "t1", 364 data: [][]string{ 365 {"total", "1"}, 366 }, 367 }} 368 369 for _, tcases := range testcases { 370 execStatements(t, []string{tcases.input}) 371 output := qh.Expect( 372 "begin", 373 tcases.output, 374 "/update _vt.vreplication set pos", 375 "commit", 376 ) 377 expectDBClientQueries(t, output) 378 if tcases.table != "" { 379 expectData(t, tcases.table, tcases.data) 380 } 381 } 382 } 383 384 func TestPlayerSavepoint(t *testing.T) { 385 defer deleteTablet(addTablet(100)) 386 execStatements(t, []string{ 387 "create table t1(id int, primary key(id))", 388 fmt.Sprintf("create table %s.t1(id int, primary key(id))", vrepldb), 389 }) 390 defer execStatements(t, []string{ 391 "drop table t1", 392 fmt.Sprintf("drop table %s.t1", vrepldb), 393 }) 394 env.SchemaEngine.Reload(context.Background()) 395 396 filter := &binlogdatapb.Filter{ 397 Rules: []*binlogdatapb.Rule{{ 398 Match: "/.*", 399 }}, 400 } 401 bls := &binlogdatapb.BinlogSource{ 402 Keyspace: env.KeyspaceName, 403 Shard: env.ShardName, 404 Filter: filter, 405 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 406 } 407 cancel, _ := startVReplication(t, bls, "") 408 // Issue a dummy change to ensure vreplication is initialized. Otherwise there 409 // is a race between the DDLs and the schema loader of vstreamer. 410 // Root cause seems to be with MySQL where t1 shows up in information_schema before 411 // the actual table is created. 412 execStatements(t, []string{"insert into t1 values(1)"}) 413 expectDBClientQueries(t, qh.Expect( 414 "begin", 415 "insert into t1(id) values (1)", 416 "/update _vt.vreplication set pos=", 417 "commit", 418 )) 419 420 execStatements(t, []string{ 421 "begin", 422 "savepoint vrepl_a", 423 "insert into t1(id) values (2)", 424 "savepoint vrepl_b", 425 "insert into t1(id) values (3)", 426 "release savepoint vrepl_b", 427 "savepoint vrepl_a", 428 "insert into t1(id) values (42)", 429 "rollback work to savepoint vrepl_a", 430 "commit", 431 }) 432 expectDBClientQueries(t, qh.Expect( 433 "begin", 434 "/insert into t1.*2.*", 435 "/insert into t1.*3.*", 436 "/update _vt.vreplication set pos=", 437 "commit", 438 )) 439 cancel() 440 } 441 442 func TestPlayerStatementModeWithFilter(t *testing.T) { 443 defer deleteTablet(addTablet(100)) 444 445 execStatements(t, []string{ 446 "create table src1(id int, val varbinary(128), primary key(id))", 447 }) 448 defer execStatements(t, []string{ 449 "drop table src1", 450 }) 451 env.SchemaEngine.Reload(context.Background()) 452 453 filter := &binlogdatapb.Filter{ 454 Rules: []*binlogdatapb.Rule{{ 455 Match: "dst1", 456 Filter: "select * from src1", 457 }}, 458 } 459 bls := &binlogdatapb.BinlogSource{ 460 Keyspace: env.KeyspaceName, 461 Shard: env.ShardName, 462 Filter: filter, 463 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 464 } 465 cancel, _ := startVReplication(t, bls, "") 466 defer cancel() 467 468 input := []string{ 469 "set @@session.binlog_format='STATEMENT'", 470 "insert into src1 values(1, 'aaa')", 471 "set @@session.binlog_format='ROW'", 472 } 473 474 // It does not work when filter is enabled 475 output := qh.Expect( 476 "begin", 477 "rollback", 478 "/update _vt.vreplication set message='filter rules are not supported for SBR", 479 ) 480 481 execStatements(t, input) 482 expectDBClientQueries(t, output) 483 } 484 485 func TestPlayerStatementMode(t *testing.T) { 486 defer deleteTablet(addTablet(100)) 487 488 execStatements(t, []string{ 489 "create table src1(id int, val varbinary(128), primary key(id))", 490 fmt.Sprintf("create table %s.src1(id int, val varbinary(128), primary key(id))", vrepldb), 491 }) 492 defer execStatements(t, []string{ 493 "drop table src1", 494 fmt.Sprintf("drop table %s.src1", vrepldb), 495 }) 496 env.SchemaEngine.Reload(context.Background()) 497 498 filter := &binlogdatapb.Filter{ 499 Rules: []*binlogdatapb.Rule{{ 500 Match: "/.*", 501 Filter: "", 502 }}, 503 } 504 bls := &binlogdatapb.BinlogSource{ 505 Keyspace: env.KeyspaceName, 506 Shard: env.ShardName, 507 Filter: filter, 508 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 509 } 510 cancel, _ := startVReplication(t, bls, "") 511 defer cancel() 512 513 input := []string{ 514 "set @@session.binlog_format='STATEMENT'", 515 "insert into src1 values(1, 'aaa')", 516 "set @@session.binlog_format='ROW'", 517 } 518 519 output := qh.Expect( 520 "begin", 521 "insert into src1 values(1, 'aaa')", 522 "/update _vt.vreplication set pos=", 523 "commit", 524 ) 525 526 execStatements(t, input) 527 expectDBClientQueries(t, output) 528 } 529 530 func TestPlayerFilters(t *testing.T) { 531 defer deleteTablet(addTablet(100)) 532 533 execStatements(t, []string{ 534 "create table src1(id int, val varbinary(128), primary key(id))", 535 fmt.Sprintf("create table %s.dst1(id int, val varbinary(128), primary key(id))", vrepldb), 536 "create table src2(id int, val1 int, val2 int, primary key(id))", 537 fmt.Sprintf("create table %s.dst2(id int, val1 int, sval2 int, rcount int, primary key(id))", vrepldb), 538 "create table src3(id int, val varbinary(128), primary key(id))", 539 fmt.Sprintf("create table %s.dst3(id int, val varbinary(128), primary key(id))", vrepldb), 540 "create table yes(id int, val varbinary(128), primary key(id))", 541 fmt.Sprintf("create table %s.yes(id int, val varbinary(128), primary key(id))", vrepldb), 542 "create table no(id int, val varbinary(128), primary key(id))", 543 "create table nopk(id int, val varbinary(128))", 544 fmt.Sprintf("create table %s.nopk(id int, val varbinary(128))", vrepldb), 545 "create table src4(id1 int, id2 int, val varbinary(128), primary key(id1))", 546 fmt.Sprintf("create table %s.dst4(id1 int, val varbinary(128), primary key(id1))", vrepldb), 547 "create table src5(id1 int, id2 int, val varbinary(128), primary key(id1))", 548 fmt.Sprintf("create table %s.dst5(id1 int, val varbinary(128), primary key(id1))", vrepldb), 549 "create table srcCharset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))", 550 fmt.Sprintf("create table %s.dstCharset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, val2 varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))", vrepldb), 551 }) 552 defer execStatements(t, []string{ 553 "drop table src1", 554 fmt.Sprintf("drop table %s.dst1", vrepldb), 555 "drop table src2", 556 fmt.Sprintf("drop table %s.dst2", vrepldb), 557 "drop table src3", 558 fmt.Sprintf("drop table %s.dst3", vrepldb), 559 "drop table yes", 560 fmt.Sprintf("drop table %s.yes", vrepldb), 561 "drop table no", 562 "drop table nopk", 563 fmt.Sprintf("drop table %s.nopk", vrepldb), 564 "drop table src4", 565 fmt.Sprintf("drop table %s.dst4", vrepldb), 566 "drop table src5", 567 fmt.Sprintf("drop table %s.dst5", vrepldb), 568 "drop table srcCharset", 569 fmt.Sprintf("drop table %s.dstCharset", vrepldb), 570 }) 571 env.SchemaEngine.Reload(context.Background()) 572 573 filter := &binlogdatapb.Filter{ 574 Rules: []*binlogdatapb.Rule{{ 575 Match: "dst1", 576 Filter: "select * from src1", 577 }, { 578 Match: "dst2", 579 Filter: "select id, val1, sum(val2) as sval2, count(*) as rcount from src2 group by id", 580 }, { 581 Match: "dst3", 582 Filter: "select id, val from src3 group by id, val", 583 }, { 584 Match: "/yes", 585 }, { 586 Match: "/nopk", 587 }, { 588 Match: "dst4", 589 Filter: "select id1, val from src4 where id2 = 100", 590 }, { 591 Match: "dst5", 592 Filter: "select id1, val from src5 where val = 'abc'", 593 }, { 594 Match: "dstCharset", 595 Filter: "select id1, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val2 from srcCharset", 596 }}, 597 } 598 bls := &binlogdatapb.BinlogSource{ 599 Keyspace: env.KeyspaceName, 600 Shard: env.ShardName, 601 Filter: filter, 602 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 603 } 604 605 cancel, _ := startVReplication(t, bls, "") 606 defer cancel() 607 608 testcases := []struct { 609 input string 610 output qh.ExpectationSequence 611 table string 612 data [][]string 613 logs []LogExpectation // logs are defined for a few testcases since they are enough to test all log events 614 }{{ 615 // insert with insertNormal 616 input: "insert into src1 values(1, 'aaa')", 617 output: qh.Expect( 618 "begin", 619 "insert into dst1(id,val) values (1,'aaa')", 620 "/update _vt.vreplication set pos=", 621 "commit", 622 ), 623 table: "dst1", 624 data: [][]string{ 625 {"1", "aaa"}, 626 }, 627 logs: []LogExpectation{ 628 {"FIELD", "/src1.*id.*INT32.*val.*VARBINARY.*"}, 629 {"ROWCHANGE", "insert into dst1(id,val) values (1,'aaa')"}, 630 {"ROW", "/src1.*3.*1aaa.*"}, 631 }, 632 }, { 633 // update with insertNormal 634 input: "update src1 set val='bbb'", 635 output: qh.Expect( 636 "begin", 637 "update dst1 set val='bbb' where id=1", 638 "/update _vt.vreplication set pos=", 639 "commit", 640 ), 641 table: "dst1", 642 data: [][]string{ 643 {"1", "bbb"}, 644 }, 645 logs: []LogExpectation{ 646 {"ROWCHANGE", "update dst1 set val='bbb' where id=1"}, 647 {"ROW", "/src1.*3.*1aaa.*"}, 648 }, 649 }, { 650 // delete with insertNormal 651 input: "delete from src1 where id=1", 652 output: qh.Expect( 653 "begin", 654 "delete from dst1 where id=1", 655 "/update _vt.vreplication set pos=", 656 "commit", 657 ), 658 table: "dst1", 659 data: [][]string{}, 660 logs: []LogExpectation{ 661 {"ROWCHANGE", "delete from dst1 where id=1"}, 662 {"ROW", "/src1.*3.*1bbb.*"}, 663 }, 664 }, { 665 // insert with insertOnDup 666 input: "insert into src2 values(1, 2, 3)", 667 output: qh.Expect( 668 "begin", 669 "insert into dst2(id,val1,sval2,rcount) values (1,2,ifnull(3, 0),1) on duplicate key update val1=values(val1), sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", 670 "/update _vt.vreplication set pos=", 671 "commit", 672 ), 673 table: "dst2", 674 data: [][]string{ 675 {"1", "2", "3", "1"}, 676 }, 677 logs: []LogExpectation{ 678 {"FIELD", "/src2.*id.*val1.*val2.*"}, 679 {"ROWCHANGE", "insert into dst2(id,val1,sval2,rcount) values (1,2,ifnull(3, 0),1) on duplicate key update val1=values(val1), sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1"}, 680 }, 681 }, { 682 // update with insertOnDup 683 input: "update src2 set val1=5, val2=1 where id=1", 684 output: qh.Expect( 685 "begin", 686 "update dst2 set val1=5, sval2=sval2-ifnull(3, 0)+ifnull(1, 0), rcount=rcount where id=1", 687 "/update _vt.vreplication set pos=", 688 "commit", 689 ), 690 table: "dst2", 691 data: [][]string{ 692 {"1", "5", "1", "1"}, 693 }, 694 logs: []LogExpectation{ 695 {"ROWCHANGE", "update dst2 set val1=5, sval2=sval2-ifnull(3, 0)+ifnull(1, 0), rcount=rcount where id=1"}, 696 {"ROW", "/src2.*123.*"}, 697 }, 698 }, { 699 // delete with insertOnDup 700 input: "delete from src2 where id=1", 701 output: qh.Expect( 702 "begin", 703 "update dst2 set val1=null, sval2=sval2-ifnull(1, 0), rcount=rcount-1 where id=1", 704 "/update _vt.vreplication set pos=", 705 "commit", 706 ), 707 table: "dst2", 708 data: [][]string{ 709 {"1", "", "0", "0"}, 710 }, 711 }, { 712 // insert with insertIgnore 713 input: "insert into src3 values(1, 'aaa')", 714 output: qh.Expect( 715 "begin", 716 "insert ignore into dst3(id,val) values (1,'aaa')", 717 "/update _vt.vreplication set pos=", 718 "commit", 719 ), 720 table: "dst3", 721 data: [][]string{ 722 {"1", "aaa"}, 723 }, 724 }, { 725 // update with insertIgnore 726 input: "update src3 set val='bbb'", 727 output: qh.Expect( 728 "begin", 729 "insert ignore into dst3(id,val) values (1,'bbb')", 730 "/update _vt.vreplication set pos=", 731 "commit", 732 ), 733 table: "dst3", 734 data: [][]string{ 735 {"1", "aaa"}, 736 }, 737 }, { 738 // delete with insertIgnore 739 input: "delete from src3 where id=1", 740 output: qh.Expect( 741 "begin", 742 "/update _vt.vreplication set pos=", 743 "commit", 744 ), 745 table: "dst3", 746 data: [][]string{ 747 {"1", "aaa"}, 748 }, 749 }, { 750 // insert: regular expression filter 751 input: "insert into yes values(1, 'aaa')", 752 output: qh.Expect( 753 "begin", 754 "insert into yes(id,val) values (1,'aaa')", 755 "/update _vt.vreplication set pos=", 756 "commit", 757 ), 758 table: "yes", 759 data: [][]string{ 760 {"1", "aaa"}, 761 }, 762 }, { 763 // update: regular expression filter 764 input: "update yes set val='bbb'", 765 output: qh.Expect( 766 "begin", 767 "update yes set val='bbb' where id=1", 768 "/update _vt.vreplication set pos=", 769 "commit", 770 ), 771 table: "yes", 772 data: [][]string{ 773 {"1", "bbb"}, 774 }, 775 }, { 776 // table should not match a rule 777 input: "insert into no values(1, 'aaa')", 778 output: qh.ExpectNone(), 779 }, { 780 // nopk: insert 781 input: "insert into nopk values(1, 'aaa')", 782 output: qh.Expect( 783 "begin", 784 "insert into nopk(id,val) values (1,'aaa')", 785 "/update _vt.vreplication set pos=", 786 "commit", 787 ), 788 table: "nopk", 789 data: [][]string{ 790 {"1", "aaa"}, 791 }, 792 }, { 793 // nopk: update 794 input: "update nopk set val='bbb' where id=1", 795 output: qh.Expect( 796 "begin", 797 "delete from nopk where id=1 and val='aaa'", 798 "insert into nopk(id,val) values (1,'bbb')", 799 "/update _vt.vreplication set pos=", 800 "commit", 801 ), 802 table: "nopk", 803 data: [][]string{ 804 {"1", "bbb"}, 805 }, 806 }, { 807 // nopk: delete 808 input: "delete from nopk where id=1", 809 output: qh.Expect( 810 "begin", 811 "delete from nopk where id=1 and val='bbb'", 812 "/update _vt.vreplication set pos=", 813 "commit", 814 ), 815 table: "nopk", 816 data: [][]string{}, 817 }, { 818 // filter by int 819 input: "insert into src4 values (1,100,'aaa'),(2,200,'bbb'),(3,100,'ccc')", 820 output: qh.Expect( 821 "begin", 822 "insert into dst4(id1,val) values (1,'aaa')", 823 "insert into dst4(id1,val) values (3,'ccc')", 824 "/update _vt.vreplication set pos=", 825 "commit", 826 ), 827 table: "dst4", 828 data: [][]string{{"1", "aaa"}, {"3", "ccc"}}, 829 }, { 830 // filter by int 831 input: "insert into src5 values (1,100,'abc'),(2,200,'xyz'),(3,100,'xyz'),(4,300,'abc'),(5,200,'xyz')", 832 output: qh.Expect( 833 "begin", 834 "insert into dst5(id1,val) values (1,'abc')", 835 "insert into dst5(id1,val) values (4,'abc')", 836 "/update _vt.vreplication set pos=", 837 "commit", 838 ), 839 table: "dst5", 840 data: [][]string{{"1", "abc"}, {"4", "abc"}}, 841 }, { 842 // test collation + filter 843 input: "insert into srcCharset values (1,'木元')", 844 output: qh.Expect( 845 "begin", 846 "insert into dstCharset(id1,val,val2) values (1,concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'),concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'))", 847 "/update _vt.vreplication set pos=", 848 "commit", 849 ), 850 table: "dstCharset", 851 data: [][]string{{"1", "木abcxyz", "木abcxyz"}}, 852 }} 853 854 for _, tcase := range testcases { 855 t.Run(tcase.input, func(t *testing.T) { 856 if tcase.logs != nil { 857 logch := vrLogStatsLogger.Subscribe("vrlogstats") 858 defer expectLogsAndUnsubscribe(t, tcase.logs, logch) 859 } 860 execStatements(t, []string{tcase.input}) 861 expectDBClientQueries(t, tcase.output) 862 if tcase.table != "" { 863 expectData(t, tcase.table, tcase.data) 864 } 865 }) 866 } 867 } 868 869 func TestPlayerKeywordNames(t *testing.T) { 870 defer deleteTablet(addTablet(100)) 871 872 execStatements(t, []string{ 873 "create table `begin`(`primary` int, `column` varbinary(128), primary key(`primary`))", 874 fmt.Sprintf("create table %s.`begin`(`primary` int, `column` varbinary(128), primary key(`primary`))", vrepldb), 875 "create table `rollback`(`primary` int, `column` varbinary(128), primary key(`primary`))", 876 fmt.Sprintf("create table %s.`rollback`(`primary` int, `column` varbinary(128), primary key(`primary`))", vrepldb), 877 "create table `commit`(`primary` int, `column` varbinary(128), primary key(`primary`))", 878 fmt.Sprintf("create table %s.`commit`(`primary` int, `column` varbinary(128), primary key(`primary`))", vrepldb), 879 }) 880 defer execStatements(t, []string{ 881 "drop table `begin`", 882 fmt.Sprintf("drop table %s.`begin`", vrepldb), 883 "drop table `rollback`", 884 fmt.Sprintf("drop table %s.`rollback`", vrepldb), 885 "drop table `commit`", 886 fmt.Sprintf("drop table %s.`commit`", vrepldb), 887 }) 888 env.SchemaEngine.Reload(context.Background()) 889 890 filter := &binlogdatapb.Filter{ 891 Rules: []*binlogdatapb.Rule{{ 892 Match: "begin", 893 Filter: "select * from `begin`", 894 }, { 895 Match: "rollback", 896 Filter: "select `primary`, `column` from `rollback`", 897 }, { 898 Match: "commit", 899 Filter: "select `primary`+1 as `primary`, concat(`column`, 'a') as `column` from `commit`", 900 }}, 901 } 902 903 bls := &binlogdatapb.BinlogSource{ 904 Keyspace: env.KeyspaceName, 905 Shard: env.ShardName, 906 Filter: filter, 907 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 908 } 909 910 cancel, _ := startVReplication(t, bls, "") 911 defer cancel() 912 913 testcases := []struct { 914 input string 915 output qh.ExpectationSequence 916 table string 917 data [][]string 918 }{{ 919 input: "insert into `begin` values(1, 'aaa')", 920 output: qh.Expect( 921 "begin", 922 "insert into `begin`(`primary`,`column`) values (1,'aaa')", 923 "/update _vt.vreplication set pos=", 924 "commit", 925 ), 926 table: "begin", 927 data: [][]string{ 928 {"1", "aaa"}, 929 }, 930 }, { 931 input: "update `begin` set `column`='bbb'", 932 output: qh.Expect( 933 "begin", 934 "update `begin` set `column`='bbb' where `primary`=1", 935 "/update _vt.vreplication set pos=", 936 "commit", 937 ), 938 table: "begin", 939 data: [][]string{ 940 {"1", "bbb"}, 941 }, 942 }, { 943 input: "delete from `begin` where `primary`=1", 944 output: qh.Expect( 945 "begin", 946 "delete from `begin` where `primary`=1", 947 "/update _vt.vreplication set pos=", 948 "commit", 949 ), 950 table: "begin", 951 data: [][]string{}, 952 }, { 953 input: "insert into `rollback` values(1, 'aaa')", 954 output: qh.Expect( 955 "begin", 956 "insert into `rollback`(`primary`,`column`) values (1,'aaa')", 957 "/update _vt.vreplication set pos=", 958 "commit", 959 ), 960 table: "rollback", 961 data: [][]string{ 962 {"1", "aaa"}, 963 }, 964 }, { 965 input: "update `rollback` set `column`='bbb'", 966 output: qh.Expect( 967 "begin", 968 "update `rollback` set `column`='bbb' where `primary`=1", 969 "/update _vt.vreplication set pos=", 970 "commit", 971 ), 972 table: "rollback", 973 data: [][]string{ 974 {"1", "bbb"}, 975 }, 976 }, { 977 input: "delete from `rollback` where `primary`=1", 978 output: qh.Expect( 979 "begin", 980 "delete from `rollback` where `primary`=1", 981 "/update _vt.vreplication set pos=", 982 "commit", 983 ), 984 table: "rollback", 985 data: [][]string{}, 986 }, { 987 input: "insert into `commit` values(1, 'aaa')", 988 output: qh.Expect( 989 "begin", 990 "insert into `commit`(`primary`,`column`) values (1 + 1,concat('aaa', 'a'))", 991 "/update _vt.vreplication set pos=", 992 "commit", 993 ), 994 table: "commit", 995 data: [][]string{ 996 {"2", "aaaa"}, 997 }, 998 }, { 999 input: "update `commit` set `column`='bbb' where `primary`=1", 1000 output: qh.Expect( 1001 "begin", 1002 "update `commit` set `column`=concat('bbb', 'a') where `primary`=(1 + 1)", 1003 "/update _vt.vreplication set pos=", 1004 "commit", 1005 ), 1006 table: "commit", 1007 data: [][]string{ 1008 {"2", "bbba"}, 1009 }, 1010 }, { 1011 input: "update `commit` set `primary`=2 where `primary`=1", 1012 output: qh.Expect( 1013 "begin", 1014 "delete from `commit` where `primary`=(1 + 1)", 1015 "insert into `commit`(`primary`,`column`) values (2 + 1,concat('bbb', 'a'))", 1016 "/update _vt.vreplication set pos=", 1017 "commit", 1018 ), 1019 table: "commit", 1020 data: [][]string{ 1021 {"3", "bbba"}, 1022 }, 1023 }, { 1024 input: "delete from `commit` where `primary`=2", 1025 output: qh.Expect( 1026 "begin", 1027 "delete from `commit` where `primary`=(2 + 1)", 1028 "/update _vt.vreplication set pos=", 1029 "commit", 1030 ), 1031 table: "commit", 1032 data: [][]string{}, 1033 }} 1034 1035 for _, tcases := range testcases { 1036 execStatements(t, []string{tcases.input}) 1037 expectDBClientQueries(t, tcases.output) 1038 if tcases.table != "" { 1039 expectData(t, tcases.table, tcases.data) 1040 } 1041 } 1042 } 1043 1044 var shardedVSchema = `{ 1045 "sharded": true, 1046 "vindexes": { 1047 "hash": { 1048 "type": "hash" 1049 } 1050 }, 1051 "tables": { 1052 "src1": { 1053 "column_vindexes": [ 1054 { 1055 "column": "id", 1056 "name": "hash" 1057 } 1058 ] 1059 } 1060 } 1061 }` 1062 1063 func TestPlayerKeyspaceID(t *testing.T) { 1064 defer deleteTablet(addTablet(100)) 1065 1066 execStatements(t, []string{ 1067 "create table src1(id int, val varbinary(128), primary key(id))", 1068 fmt.Sprintf("create table %s.dst1(id int, val varbinary(128), primary key(id))", vrepldb), 1069 }) 1070 defer execStatements(t, []string{ 1071 "drop table src1", 1072 fmt.Sprintf("drop table %s.dst1", vrepldb), 1073 }) 1074 env.SchemaEngine.Reload(context.Background()) 1075 1076 if err := env.SetVSchema(shardedVSchema); err != nil { 1077 t.Fatal(err) 1078 } 1079 defer env.SetVSchema("{}") 1080 1081 filter := &binlogdatapb.Filter{ 1082 Rules: []*binlogdatapb.Rule{{ 1083 Match: "dst1", 1084 Filter: "select id, keyspace_id() as val from src1", 1085 }}, 1086 } 1087 bls := &binlogdatapb.BinlogSource{ 1088 Keyspace: env.KeyspaceName, 1089 Shard: env.ShardName, 1090 Filter: filter, 1091 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1092 } 1093 cancel, _ := startVReplication(t, bls, "") 1094 defer cancel() 1095 1096 testcases := []struct { 1097 input string 1098 output qh.ExpectationSequence 1099 table string 1100 data [][]string 1101 }{{ 1102 // insert with insertNormal 1103 input: "insert into src1 values(1, 'aaa')", 1104 output: qh.Expect( 1105 "begin", 1106 "insert into dst1(id,val) values (1,'\x16k@\xb4J\xbaK\xd6')", 1107 "/update _vt.vreplication set pos=", 1108 "commit", 1109 ), 1110 table: "dst1", 1111 data: [][]string{ 1112 {"1", "\x16k@\xb4J\xbaK\xd6"}, 1113 }, 1114 }} 1115 1116 for _, tcases := range testcases { 1117 execStatements(t, []string{tcases.input}) 1118 expectDBClientQueries(t, tcases.output) 1119 if tcases.table != "" { 1120 expectData(t, tcases.table, tcases.data) 1121 } 1122 } 1123 } 1124 1125 func TestUnicode(t *testing.T) { 1126 defer deleteTablet(addTablet(100)) 1127 1128 execStatements(t, []string{ 1129 "create table src1(id int, val varchar(128) COLLATE utf8_unicode_ci, primary key(id))", 1130 fmt.Sprintf("create table %s.dst1(id int, val varchar(128) COLLATE utf8_unicode_ci, primary key(id)) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", vrepldb), 1131 }) 1132 defer execStatements(t, []string{ 1133 "drop table src1", 1134 fmt.Sprintf("drop table %s.dst1", vrepldb), 1135 }) 1136 env.SchemaEngine.Reload(context.Background()) 1137 1138 filter := &binlogdatapb.Filter{ 1139 Rules: []*binlogdatapb.Rule{{ 1140 Match: "dst1", 1141 Filter: "select * from src1", 1142 }}, 1143 } 1144 bls := &binlogdatapb.BinlogSource{ 1145 Keyspace: env.KeyspaceName, 1146 Shard: env.ShardName, 1147 Filter: filter, 1148 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1149 } 1150 cancel, _ := startVReplication(t, bls, "") 1151 defer cancel() 1152 1153 testcases := []struct { 1154 input string 1155 output qh.ExpectationSequence 1156 table string 1157 data [][]string 1158 }{{ 1159 // insert with insertNormal 1160 input: "insert into src1 values(1, '👍')", 1161 output: qh.Expect( 1162 "begin", 1163 // We should expect the "Mojibaked" version. 1164 "insert into dst1(id,val) values (1,'ðŸ‘\u008d')", 1165 "/update _vt.vreplication set pos=", 1166 "commit", 1167 ), 1168 table: "dst1", 1169 data: [][]string{ 1170 {"1", "👍"}, 1171 }, 1172 }} 1173 1174 // We need a latin1 connection. 1175 conn, err := env.Mysqld.GetDbaConnection(context.Background()) 1176 if err != nil { 1177 t.Fatal(err) 1178 } 1179 defer conn.Close() 1180 1181 if _, err := conn.ExecuteFetch("set names latin1", 10000, false); err != nil { 1182 t.Fatal(err) 1183 } 1184 1185 for _, tcases := range testcases { 1186 if _, err := conn.ExecuteFetch(tcases.input, 10000, false); err != nil { 1187 t.Error(err) 1188 } 1189 expectDBClientQueries(t, tcases.output) 1190 if tcases.table != "" { 1191 customExpectData(t, tcases.table, tcases.data, func(ctx context.Context, query string) (*sqltypes.Result, error) { 1192 return conn.ExecuteFetch(query, 10000, true) 1193 }) 1194 } 1195 } 1196 } 1197 1198 func TestPlayerUpdates(t *testing.T) { 1199 defer deleteTablet(addTablet(100)) 1200 1201 execStatements(t, []string{ 1202 "create table t1(id int, grouped int, ungrouped int, summed int, primary key(id))", 1203 fmt.Sprintf("create table %s.t1(id int, grouped int, ungrouped int, summed int, rcount int, primary key(id))", vrepldb), 1204 }) 1205 defer execStatements(t, []string{ 1206 "drop table t1", 1207 fmt.Sprintf("drop table %s.t1", vrepldb), 1208 }) 1209 env.SchemaEngine.Reload(context.Background()) 1210 1211 filter := &binlogdatapb.Filter{ 1212 Rules: []*binlogdatapb.Rule{{ 1213 Match: "t1", 1214 Filter: "select id, grouped, ungrouped, sum(summed) as summed, count(*) as rcount from t1 group by id, grouped", 1215 }}, 1216 } 1217 bls := &binlogdatapb.BinlogSource{ 1218 Keyspace: env.KeyspaceName, 1219 Shard: env.ShardName, 1220 Filter: filter, 1221 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1222 } 1223 cancel, _ := startVReplication(t, bls, "") 1224 defer cancel() 1225 1226 testcases := []struct { 1227 input string 1228 output string 1229 table string 1230 data [][]string 1231 }{{ 1232 // Start with all nulls 1233 input: "insert into t1 values(1, null, null, null)", 1234 output: "insert into t1(id,grouped,ungrouped,summed,rcount) values (1,null,null,ifnull(null, 0),1) on duplicate key update ungrouped=values(ungrouped), summed=summed+ifnull(values(summed), 0), rcount=rcount+1", 1235 table: "t1", 1236 data: [][]string{ 1237 {"1", "", "", "0", "1"}, 1238 }, 1239 }, { 1240 // null to null values 1241 input: "update t1 set grouped=1 where id=1", 1242 output: "update t1 set ungrouped=null, summed=summed-ifnull(null, 0)+ifnull(null, 0), rcount=rcount where id=1", 1243 table: "t1", 1244 data: [][]string{ 1245 {"1", "", "", "0", "1"}, 1246 }, 1247 }, { 1248 // null to non-null values 1249 input: "update t1 set ungrouped=1, summed=1 where id=1", 1250 output: "update t1 set ungrouped=1, summed=summed-ifnull(null, 0)+ifnull(1, 0), rcount=rcount where id=1", 1251 table: "t1", 1252 data: [][]string{ 1253 {"1", "", "1", "1", "1"}, 1254 }, 1255 }, { 1256 // non-null to non-null values 1257 input: "update t1 set ungrouped=2, summed=2 where id=1", 1258 output: "update t1 set ungrouped=2, summed=summed-ifnull(1, 0)+ifnull(2, 0), rcount=rcount where id=1", 1259 table: "t1", 1260 data: [][]string{ 1261 {"1", "", "2", "2", "1"}, 1262 }, 1263 }, { 1264 // non-null to null values 1265 input: "update t1 set ungrouped=null, summed=null where id=1", 1266 output: "update t1 set ungrouped=null, summed=summed-ifnull(2, 0)+ifnull(null, 0), rcount=rcount where id=1", 1267 table: "t1", 1268 data: [][]string{ 1269 {"1", "", "", "0", "1"}, 1270 }, 1271 }, { 1272 // insert non-null values 1273 input: "insert into t1 values(2, 2, 3, 4)", 1274 output: "insert into t1(id,grouped,ungrouped,summed,rcount) values (2,2,3,ifnull(4, 0),1) on duplicate key update ungrouped=values(ungrouped), summed=summed+ifnull(values(summed), 0), rcount=rcount+1", 1275 table: "t1", 1276 data: [][]string{ 1277 {"1", "", "", "0", "1"}, 1278 {"2", "2", "3", "4", "1"}, 1279 }, 1280 }, { 1281 // delete non-null values 1282 input: "delete from t1 where id=2", 1283 output: "update t1 set ungrouped=null, summed=summed-ifnull(4, 0), rcount=rcount-1 where id=2", 1284 table: "t1", 1285 data: [][]string{ 1286 {"1", "", "", "0", "1"}, 1287 {"2", "2", "", "0", "0"}, 1288 }, 1289 }} 1290 1291 for _, tcases := range testcases { 1292 execStatements(t, []string{tcases.input}) 1293 output := qh.Expect( 1294 "begin", 1295 tcases.output, 1296 "/update _vt.vreplication set pos=", 1297 "commit", 1298 ) 1299 if tcases.output == "" { 1300 output = qh.Expect( 1301 "begin", 1302 "/update _vt.vreplication set pos=", 1303 "commit", 1304 ) 1305 } 1306 expectDBClientQueries(t, output) 1307 if tcases.table != "" { 1308 expectData(t, tcases.table, tcases.data) 1309 } 1310 } 1311 validateQueryCountStat(t, "replicate", 7) 1312 } 1313 1314 func TestPlayerRowMove(t *testing.T) { 1315 defer deleteTablet(addTablet(100)) 1316 1317 execStatements(t, []string{ 1318 "create table src(id int, val1 int, val2 int, primary key(id))", 1319 fmt.Sprintf("create table %s.dst(val1 int, sval2 int, rcount int, primary key(val1))", vrepldb), 1320 }) 1321 defer execStatements(t, []string{ 1322 "drop table src", 1323 fmt.Sprintf("drop table %s.dst", vrepldb), 1324 }) 1325 env.SchemaEngine.Reload(context.Background()) 1326 1327 filter := &binlogdatapb.Filter{ 1328 Rules: []*binlogdatapb.Rule{{ 1329 Match: "dst", 1330 Filter: "select val1, sum(val2) as sval2, count(*) as rcount from src group by val1", 1331 }}, 1332 } 1333 bls := &binlogdatapb.BinlogSource{ 1334 Keyspace: env.KeyspaceName, 1335 Shard: env.ShardName, 1336 Filter: filter, 1337 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1338 } 1339 cancel, _ := startVReplication(t, bls, "") 1340 defer cancel() 1341 1342 execStatements(t, []string{ 1343 "insert into src values(1, 1, 1), (2, 2, 2), (3, 2, 3)", 1344 }) 1345 expectDBClientQueries(t, qh.Expect( 1346 "begin", 1347 "insert into dst(val1,sval2,rcount) values (1,ifnull(1, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", 1348 "insert into dst(val1,sval2,rcount) values (2,ifnull(2, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", 1349 "insert into dst(val1,sval2,rcount) values (2,ifnull(3, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", 1350 "/update _vt.vreplication set pos=", 1351 "commit", 1352 )) 1353 expectData(t, "dst", [][]string{ 1354 {"1", "1", "1"}, 1355 {"2", "5", "2"}, 1356 }) 1357 validateQueryCountStat(t, "replicate", 3) 1358 1359 execStatements(t, []string{ 1360 "update src set val1=1, val2=4 where id=3", 1361 }) 1362 expectDBClientQueries(t, qh.Expect( 1363 "begin", 1364 "update dst set sval2=sval2-ifnull(3, 0), rcount=rcount-1 where val1=2", 1365 "insert into dst(val1,sval2,rcount) values (1,ifnull(4, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", 1366 "/update _vt.vreplication set pos=", 1367 "commit", 1368 )) 1369 expectData(t, "dst", [][]string{ 1370 {"1", "5", "2"}, 1371 {"2", "2", "1"}, 1372 }) 1373 validateQueryCountStat(t, "replicate", 5) 1374 } 1375 1376 func TestPlayerTypes(t *testing.T) { 1377 log.Errorf("TestPlayerTypes: flavor is %s", env.Flavor) 1378 enableJSONColumnTesting := false 1379 flavor := strings.ToLower(env.Flavor) 1380 // Disable tests on percona and mariadb platforms in CI since they 1381 // either don't support JSON or JSON support is not enabled by default 1382 if strings.Contains(flavor, "mysql57") || strings.Contains(flavor, "mysql80") { 1383 log.Infof("Running JSON column type tests on flavor %s", flavor) 1384 enableJSONColumnTesting = true 1385 } else { 1386 log.Warningf("Not running JSON column type tests on flavor %s", flavor) 1387 } 1388 defer deleteTablet(addTablet(100)) 1389 1390 execStatements(t, []string{ 1391 "create table vitess_ints(tiny tinyint, tinyu tinyint unsigned, small smallint, smallu smallint unsigned, medium mediumint, mediumu mediumint unsigned, normal int, normalu int unsigned, big bigint, bigu bigint unsigned, y year, primary key(tiny))", 1392 fmt.Sprintf("create table %s.vitess_ints(tiny tinyint, tinyu tinyint unsigned, small smallint, smallu smallint unsigned, medium mediumint, mediumu mediumint unsigned, normal int, normalu int unsigned, big bigint, bigu bigint unsigned, y year, primary key(tiny))", vrepldb), 1393 "create table vitess_fracts(id int, deci decimal(5,2), num numeric(5,2), f float, d double, primary key(id))", 1394 fmt.Sprintf("create table %s.vitess_fracts(id int, deci decimal(5,2), num numeric(5,2), f float, d double, primary key(id))", vrepldb), 1395 "create table vitess_strings(vb varbinary(16), c char(16), vc varchar(16), b binary(5), tb tinyblob, bl blob, ttx tinytext, tx text, en enum('a','b'), s set('a','b'), primary key(vb))", 1396 fmt.Sprintf("create table %s.vitess_strings(vb varbinary(16), c char(16), vc varchar(16), b binary(5), tb tinyblob, bl blob, ttx tinytext, tx text, en enum('a','b'), s set('a','b'), primary key(vb))", vrepldb), 1397 "create table vitess_misc(id int, b bit(8), d date, dt datetime, t time, g geometry, primary key(id))", 1398 fmt.Sprintf("create table %s.vitess_misc(id int, b bit(8), d date, dt datetime, t time, g geometry, primary key(id))", vrepldb), 1399 "create table vitess_null(id int, val varbinary(128), primary key(id))", 1400 fmt.Sprintf("create table %s.vitess_null(id int, val varbinary(128), primary key(id))", vrepldb), 1401 "create table src1(id int, val varbinary(128), primary key(id))", 1402 fmt.Sprintf("create table %s.src1(id int, val varbinary(128), primary key(id))", vrepldb), 1403 "create table binary_pk(b binary(4), val varbinary(4), primary key(b))", 1404 fmt.Sprintf("create table %s.binary_pk(b binary(4), val varbinary(4), primary key(b))", vrepldb), 1405 "create table vitess_decimal(id int, d1 decimal(8,0) default null, d2 decimal(8,0) default null, d3 decimal(8,0) default null, d4 decimal(8, 1), d5 decimal(8, 1), d6 decimal(8, 1), primary key(id))", 1406 fmt.Sprintf("create table %s.vitess_decimal(id int, d1 decimal(8,0) default null, d2 decimal(8,0) default null, d3 decimal(8,0) default null, d4 decimal(8, 1), d5 decimal(8, 1), d6 decimal(8, 1), primary key(id))", vrepldb), 1407 }) 1408 defer execStatements(t, []string{ 1409 "drop table vitess_ints", 1410 fmt.Sprintf("drop table %s.vitess_ints", vrepldb), 1411 "drop table vitess_fracts", 1412 fmt.Sprintf("drop table %s.vitess_fracts", vrepldb), 1413 "drop table vitess_strings", 1414 fmt.Sprintf("drop table %s.vitess_strings", vrepldb), 1415 "drop table vitess_misc", 1416 fmt.Sprintf("drop table %s.vitess_misc", vrepldb), 1417 "drop table vitess_null", 1418 fmt.Sprintf("drop table %s.vitess_null", vrepldb), 1419 "drop table src1", 1420 fmt.Sprintf("drop table %s.src1", vrepldb), 1421 "drop table binary_pk", 1422 fmt.Sprintf("drop table %s.binary_pk", vrepldb), 1423 "drop table vitess_decimal", 1424 fmt.Sprintf("drop table %s.vitess_decimal", vrepldb), 1425 }) 1426 if enableJSONColumnTesting { 1427 execStatements(t, []string{ 1428 "create table vitess_json(id int auto_increment, val1 json, val2 json, val3 json, val4 json, val5 json, primary key(id))", 1429 fmt.Sprintf("create table %s.vitess_json(id int, val1 json, val2 json, val3 json, val4 json, val5 json, primary key(id))", vrepldb), 1430 }) 1431 defer execStatements(t, []string{ 1432 "drop table vitess_json", 1433 fmt.Sprintf("drop table %s.vitess_json", vrepldb), 1434 }) 1435 1436 } 1437 env.SchemaEngine.Reload(context.Background()) 1438 1439 filter := &binlogdatapb.Filter{ 1440 Rules: []*binlogdatapb.Rule{{ 1441 Match: "/.*", 1442 }}, 1443 } 1444 bls := &binlogdatapb.BinlogSource{ 1445 Keyspace: env.KeyspaceName, 1446 Shard: env.ShardName, 1447 Filter: filter, 1448 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1449 } 1450 cancel, _ := startVReplication(t, bls, "") 1451 defer cancel() 1452 type testcase struct { 1453 input string 1454 output string 1455 table string 1456 data [][]string 1457 } 1458 testcases := []testcase{{ 1459 input: "insert into vitess_ints values(-128, 255, -32768, 65535, -8388608, 16777215, -2147483648, 4294967295, -9223372036854775808, 18446744073709551615, 2012)", 1460 output: "insert into vitess_ints(tiny,tinyu,small,smallu,medium,mediumu,normal,normalu,big,bigu,y) values (-128,255,-32768,65535,-8388608,16777215,-2147483648,4294967295,-9223372036854775808,18446744073709551615,2012)", 1461 table: "vitess_ints", 1462 data: [][]string{ 1463 {"-128", "255", "-32768", "65535", "-8388608", "16777215", "-2147483648", "4294967295", "-9223372036854775808", "18446744073709551615", "2012"}, 1464 }, 1465 }, { 1466 input: "insert into vitess_fracts values(1, 1.99, 2.99, 3.99, 4.99)", 1467 output: "insert into vitess_fracts(id,deci,num,f,d) values (1,1.99,2.99,3.99E+00,4.99E+00)", 1468 table: "vitess_fracts", 1469 data: [][]string{ 1470 {"1", "1.99", "2.99", "3.99", "4.99"}, 1471 }, 1472 }, { 1473 input: "insert into vitess_strings values('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'a,b')", 1474 output: "insert into vitess_strings(vb,c,vc,b,tb,bl,ttx,tx,en,s) values ('a','b','c','d\\0\\0\\0\\0','e','f','g','h',1,'3')", 1475 table: "vitess_strings", 1476 data: [][]string{ 1477 {"a", "b", "c", "d\000\000\000\000", "e", "f", "g", "h", "a", "a,b"}, 1478 }, 1479 }, { 1480 input: "insert into vitess_misc values(1, '\x01', '2012-01-01', '2012-01-01 15:45:45', '15:45:45', point(1, 2))", 1481 output: "insert into vitess_misc(id,b,d,dt,t,g) values (1,b'00000001','2012-01-01','2012-01-01 15:45:45','15:45:45','\\0\\0\\0\\0\x01\x01\\0\\0\\0\\0\\0\\0\\0\\0\\0\xf0?\\0\\0\\0\\0\\0\\0\\0@')", 1482 table: "vitess_misc", 1483 data: [][]string{ 1484 {"1", "\x01", "2012-01-01", "2012-01-01 15:45:45", "15:45:45", "\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@"}, 1485 }, 1486 }, { 1487 input: "insert into vitess_null values(1, null)", 1488 output: "insert into vitess_null(id,val) values (1,null)", 1489 table: "vitess_null", 1490 data: [][]string{ 1491 {"1", ""}, 1492 }, 1493 }, { 1494 input: "insert into binary_pk values('a', 'aaa')", 1495 output: "insert into binary_pk(b,val) values ('a\\0\\0\\0','aaa')", 1496 table: "binary_pk", 1497 data: [][]string{ 1498 {"a\000\000\000", "aaa"}, 1499 }, 1500 }, { 1501 input: "insert into vitess_decimal values(1, 0, 1, null, 0, 1.1, 1)", 1502 output: "insert into vitess_decimal(id,d1,d2,d3,d4,d5,d6) values (1,0,1,null,.0,1.1,1.0)", 1503 table: "vitess_decimal", 1504 data: [][]string{ 1505 {"1", "0", "1", "", "0.0", "1.1", "1.0"}, 1506 }, 1507 }, { 1508 // Binary pk is a special case: https://github.com/vitessio/vitess/issues/3984 1509 input: "update binary_pk set val='bbb' where b='a\\0\\0\\0'", 1510 output: "update binary_pk set val='bbb' where b='a\\0\\0\\0'", 1511 table: "binary_pk", 1512 data: [][]string{ 1513 {"a\000\000\000", "bbb"}, 1514 }, 1515 }} 1516 if enableJSONColumnTesting { 1517 testcases = append(testcases, testcase{ 1518 input: "insert into vitess_json(val1,val2,val3,val4,val5) values (null,'{}','1629849600','{\"a\":[42,-1,3.1415,-128,127,-9223372036854775808,9223372036854775807,18446744073709551615]}', '{\"foo\":\"bar\"}')", 1519 output: "insert into vitess_json(id,val1,val2,val3,val4,val5) values (1," + 1520 "convert(null using utf8mb4)," + "convert('{}' using utf8mb4)," + "convert('1629849600' using utf8mb4)," + 1521 "convert('{\\\"a\\\":[42,-1,3.1415,-128,127,-9223372036854775808,9223372036854775807,18446744073709551615]}' using utf8mb4)," + "convert('{\\\"foo\\\":\\\"bar\\\"}' using utf8mb4))", 1522 table: "vitess_json", 1523 data: [][]string{ 1524 {"1", "", "{}", "1629849600", `{"a": [42, -1, 3.1415, -128, 127, -9223372036854775808, 9223372036854775807, 18446744073709551615]}`, `{"foo": "bar"}`}, 1525 }, 1526 }) 1527 testcases = append(testcases, testcase{ 1528 input: "update vitess_json set val4 = '{\"a\": [-9223372036854775808, -2147483648]}', val5 = convert(x'7b7d' using utf8mb4)", 1529 output: "update vitess_json set val1=convert(null using utf8mb4), val2=convert('{}' using utf8mb4), val3=convert('1629849600' using utf8mb4), val4=convert('{\\\"a\\\":[-9223372036854775808,-2147483648]}' using utf8mb4), val5=convert('{}' using utf8mb4) where id=1", 1530 table: "vitess_json", 1531 data: [][]string{ 1532 {"1", "", "{}", "1629849600", `{"a": [-9223372036854775808, -2147483648]}`, `{}`}, 1533 }, 1534 }) 1535 } 1536 1537 for _, tcases := range testcases { 1538 execStatements(t, []string{tcases.input}) 1539 want := qh.Expect( 1540 "begin", 1541 tcases.output, 1542 "/update _vt.vreplication set pos=", 1543 "commit", 1544 ) 1545 expectDBClientQueries(t, want) 1546 if tcases.table != "" { 1547 expectData(t, tcases.table, tcases.data) 1548 } 1549 } 1550 } 1551 1552 func TestPlayerDDL(t *testing.T) { 1553 defer deleteTablet(addTablet(100)) 1554 execStatements(t, []string{ 1555 "create table t1(id int, primary key(id))", 1556 fmt.Sprintf("create table %s.t1(id int, primary key(id))", vrepldb), 1557 }) 1558 defer execStatements(t, []string{ 1559 "drop table t1", 1560 fmt.Sprintf("drop table %s.t1", vrepldb), 1561 }) 1562 env.SchemaEngine.Reload(context.Background()) 1563 1564 filter := &binlogdatapb.Filter{ 1565 Rules: []*binlogdatapb.Rule{{ 1566 Match: "/.*", 1567 }}, 1568 } 1569 bls := &binlogdatapb.BinlogSource{ 1570 Keyspace: env.KeyspaceName, 1571 Shard: env.ShardName, 1572 Filter: filter, 1573 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1574 } 1575 cancel, _ := startVReplication(t, bls, "") 1576 // Issue a dummy change to ensure vreplication is initialized. Otherwise there 1577 // is a race between the DDLs and the schema loader of vstreamer. 1578 // Root cause seems to be with MySQL where t1 shows up in information_schema before 1579 // the actual table is created. 1580 execStatements(t, []string{"insert into t1 values(1)"}) 1581 expectDBClientQueries(t, qh.Expect( 1582 "begin", 1583 "insert into t1(id) values (1)", 1584 "/update _vt.vreplication set pos=", 1585 "commit", 1586 )) 1587 1588 execStatements(t, []string{"alter table t1 add column val varchar(128)"}) 1589 execStatements(t, []string{"alter table t1 drop column val"}) 1590 expectDBClientQueries(t, qh.Expect( 1591 "/update _vt.vreplication set pos=", 1592 "/update _vt.vreplication set pos=", 1593 )) 1594 cancel() 1595 bls = &binlogdatapb.BinlogSource{ 1596 Keyspace: env.KeyspaceName, 1597 Shard: env.ShardName, 1598 Filter: filter, 1599 OnDdl: binlogdatapb.OnDDLAction_STOP, 1600 } 1601 cancel, id := startVReplication(t, bls, "") 1602 pos0 := primaryPosition(t) //For debugging only 1603 execStatements(t, []string{"alter table t1 add column val varchar(128)"}) 1604 pos1 := primaryPosition(t) 1605 // The stop position must be the GTID of the first DDL 1606 expectDBClientQueries(t, qh.Expect( 1607 "begin", 1608 fmt.Sprintf("/update _vt.vreplication set pos='%s'", pos1), 1609 "/update _vt.vreplication set state='Stopped'", 1610 "commit", 1611 )) 1612 pos2b := primaryPosition(t) 1613 execStatements(t, []string{"alter table t1 drop column val"}) 1614 pos2 := primaryPosition(t) 1615 log.Errorf("Expected log:: TestPlayerDDL Positions are: before first alter %v, after first alter %v, before second alter %v, after second alter %v", 1616 pos0, pos1, pos2b, pos2) //For debugging only: to check what are the positions when test works and if/when it fails 1617 // Restart vreplication 1618 if _, err := playerEngine.Exec(fmt.Sprintf(`update _vt.vreplication set state = 'Running', message='' where id=%d`, id)); err != nil { 1619 t.Fatal(err) 1620 } 1621 // It should stop at the next DDL 1622 expectDBClientQueries(t, qh.Expect( 1623 "/update.*'Running'", 1624 // Second update is from vreplicator. 1625 "/update _vt.vreplication set message='Picked source tablet.*", 1626 "/update.*'Running'", 1627 "begin", 1628 fmt.Sprintf("/update.*'%s'", pos2), 1629 "/update _vt.vreplication set state='Stopped'", 1630 "commit", 1631 )) 1632 cancel() 1633 bls = &binlogdatapb.BinlogSource{ 1634 Keyspace: env.KeyspaceName, 1635 Shard: env.ShardName, 1636 Filter: filter, 1637 OnDdl: binlogdatapb.OnDDLAction_EXEC, 1638 } 1639 execStatements(t, []string{fmt.Sprintf("alter table %s.t1 add column val2 varchar(128)", vrepldb)}) 1640 cancel, _ = startVReplication(t, bls, "") 1641 execStatements(t, []string{"alter table t1 add column val1 varchar(128)"}) 1642 expectDBClientQueries(t, qh.Expect( 1643 "alter table t1 add column val1 varchar(128)", 1644 "/update _vt.vreplication set pos=", 1645 // The apply of the DDL on target generates an "other" event. 1646 "/update _vt.vreplication set pos=", 1647 )) 1648 execStatements(t, []string{"alter table t1 add column val2 varchar(128)"}) 1649 expectDBClientQueries(t, qh.Expect( 1650 "alter table t1 add column val2 varchar(128)", 1651 "/update _vt.vreplication set message='Duplicate", 1652 "/update _vt.vreplication set state='Error', message='Duplicate", 1653 )) 1654 cancel() 1655 1656 execStatements(t, []string{ 1657 "alter table t1 drop column val1", 1658 "alter table t1 drop column val2", 1659 fmt.Sprintf("alter table %s.t1 drop column val1", vrepldb), 1660 }) 1661 1662 bls = &binlogdatapb.BinlogSource{ 1663 Keyspace: env.KeyspaceName, 1664 Shard: env.ShardName, 1665 Filter: filter, 1666 OnDdl: binlogdatapb.OnDDLAction_EXEC_IGNORE, 1667 } 1668 execStatements(t, []string{fmt.Sprintf("create table %s.t2(id int, primary key(id))", vrepldb)}) 1669 cancel, _ = startVReplication(t, bls, "") 1670 execStatements(t, []string{"alter table t1 add column val1 varchar(128)"}) 1671 expectDBClientQueries(t, qh.Expect( 1672 "alter table t1 add column val1 varchar(128)", 1673 "/update _vt.vreplication set pos=", 1674 // The apply of the DDL on target generates an "other" event. 1675 "/update _vt.vreplication set pos=", 1676 )) 1677 execStatements(t, []string{"alter table t1 add column val2 varchar(128)"}) 1678 expectDBClientQueries(t, qh.Expect( 1679 "alter table t1 add column val2 varchar(128)", 1680 "/update _vt.vreplication set pos=", 1681 )) 1682 cancel() 1683 } 1684 1685 func TestGTIDCompress(t *testing.T) { 1686 ctx := context.Background() 1687 defer deleteTablet(addTablet(100)) 1688 err := env.Mysqld.ExecuteSuperQuery(ctx, "insert into _vt.vreplication (id, workflow, source, pos, max_tps, max_replication_lag, time_updated, transaction_timestamp, state,db_name) values (1, '', '', '', 0,0,0,0,'Stopped','')") 1689 require.NoError(t, err) 1690 1691 type testCase struct { 1692 name, gtid string 1693 compress bool 1694 } 1695 1696 testCases := []testCase{ 1697 {"cleartext1", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092", false}, 1698 {"cleartext2", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092,320a5e98-6965-11ea-b949-eeafd34ae6e4:1-3,81cbdbf8-6969-11ea-aeb1-a6143b021f67:1-524891956,c9a0f301-6965-11ea-ba9d-02c229065569:1-3,cb698dac-6969-11ea-ac38-16e5d0ac5c3a:1-524441991,e39fca4d-6960-11ea-b4c2-1e895fd49fa0:1-3", false}, 1699 {"compress1", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092", true}, 1700 {"compress2", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092,320a5e98-6965-11ea-b949-eeafd34ae6e4:1-3,81cbdbf8-6969-11ea-aeb1-a6143b021f67:1-524891956,c9a0f301-6965-11ea-ba9d-02c229065569:1-3,cb698dac-6969-11ea-ac38-16e5d0ac5c3a:1-524441991,e39fca4d-6960-11ea-b4c2-1e895fd49fa0:1-3", true}, 1701 {"nil-compress", "", true}, 1702 {"nil-clear", "", false}, 1703 } 1704 for _, tCase := range testCases { 1705 t.Run(tCase.name, func(t *testing.T) { 1706 strGTID := fmt.Sprintf("'%s'", tCase.gtid) 1707 if tCase.compress { 1708 strGTID = fmt.Sprintf("compress(%s)", strGTID) 1709 } 1710 err := env.Mysqld.ExecuteSuperQuery(ctx, fmt.Sprintf("update _vt.vreplication set pos=%s where id = 1", strGTID)) 1711 require.NoError(t, err) 1712 qr, err := env.Mysqld.FetchSuperQuery(ctx, "select pos from _vt.vreplication where id = 1") 1713 require.NoError(t, err) 1714 require.NotNil(t, qr) 1715 require.Equal(t, 1, len(qr.Rows)) 1716 gotGTID := qr.Rows[0][0].ToString() 1717 pos, err := mysql.DecodePosition(gotGTID) 1718 if tCase.compress { 1719 require.True(t, pos.IsZero()) 1720 pos, err = binlogplayer.DecodePosition(gotGTID) 1721 require.NoError(t, err) 1722 require.NotNil(t, pos) 1723 tpos, err := mysql.DecodePosition(tCase.gtid) 1724 require.NoError(t, err) 1725 require.Equal(t, tpos.String(), pos.String()) 1726 } else { 1727 require.NoError(t, err) 1728 require.NotNil(t, pos) 1729 require.Equal(t, tCase.gtid, gotGTID) 1730 } 1731 }) 1732 } 1733 } 1734 1735 func TestPlayerStopPos(t *testing.T) { 1736 defer deleteTablet(addTablet(100)) 1737 vreplicationStoreCompressedGTID = true 1738 defer func() { 1739 vreplicationStoreCompressedGTID = false 1740 }() 1741 execStatements(t, []string{ 1742 "create table yes(id int, val varbinary(128), primary key(id))", 1743 fmt.Sprintf("create table %s.yes(id int, val varbinary(128), primary key(id))", vrepldb), 1744 "create table no(id int, val varbinary(128), primary key(id))", 1745 }) 1746 defer execStatements(t, []string{ 1747 "drop table yes", 1748 fmt.Sprintf("drop table %s.yes", vrepldb), 1749 "drop table no", 1750 }) 1751 env.SchemaEngine.Reload(context.Background()) 1752 1753 filter := &binlogdatapb.Filter{ 1754 Rules: []*binlogdatapb.Rule{{ 1755 Match: "/yes", 1756 }}, 1757 } 1758 bls := &binlogdatapb.BinlogSource{ 1759 Keyspace: env.KeyspaceName, 1760 Shard: env.ShardName, 1761 Filter: filter, 1762 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1763 } 1764 startPos := primaryPosition(t) 1765 query := binlogplayer.CreateVReplicationState("test", bls, startPos, binlogplayer.BlpStopped, vrepldb, 0, 0) 1766 qr, err := playerEngine.Exec(query) 1767 if err != nil { 1768 t.Fatal(err) 1769 } 1770 id := uint32(qr.InsertID) 1771 for q := range globalDBQueries { 1772 if strings.HasPrefix(q, "insert into _vt.vreplication") { 1773 break 1774 } 1775 } 1776 1777 // Test normal stop. 1778 execStatements(t, []string{ 1779 "insert into yes values(1, 'aaa')", 1780 }) 1781 stopPos := primaryPosition(t) 1782 query = binlogplayer.StartVReplicationUntil(id, stopPos) 1783 if _, err := playerEngine.Exec(query); err != nil { 1784 t.Fatal(err) 1785 } 1786 expectDBClientQueries(t, qh.Expect( 1787 "/update.*'Running'", 1788 // Second update is from vreplicator. 1789 "/update _vt.vreplication set message='Picked source tablet.*", 1790 "/update.*'Running'", 1791 "begin", 1792 "insert into yes(id,val) values (1,'aaa')", 1793 fmt.Sprintf("/update.*compress.*'%s'", stopPos), 1794 "/update.*'Stopped'", 1795 "commit", 1796 )) 1797 1798 // Test stopping at empty transaction. 1799 execStatements(t, []string{ 1800 "insert into no values(2, 'aaa')", 1801 "insert into no values(3, 'aaa')", 1802 }) 1803 stopPos = primaryPosition(t) 1804 execStatements(t, []string{ 1805 "insert into no values(4, 'aaa')", 1806 }) 1807 query = binlogplayer.StartVReplicationUntil(id, stopPos) 1808 if _, err := playerEngine.Exec(query); err != nil { 1809 t.Fatal(err) 1810 } 1811 expectDBClientQueries(t, qh.Expect( 1812 "/update.*'Running'", 1813 // Second update is from vreplicator. 1814 "/update _vt.vreplication set message='Picked source tablet.*", 1815 "/update.*'Running'", 1816 "begin", 1817 // Since 'no' generates empty transactions that are skipped by 1818 // vplayer, a commit is done only for the stop position event. 1819 fmt.Sprintf("/update.*'%s'", stopPos), 1820 "/update.*'Stopped'", 1821 "commit", 1822 )) 1823 1824 // Test stopping when position is already reached. 1825 query = binlogplayer.StartVReplicationUntil(id, stopPos) 1826 if _, err := playerEngine.Exec(query); err != nil { 1827 t.Fatal(err) 1828 } 1829 expectDBClientQueries(t, qh.Expect( 1830 "/update.*'Running'", 1831 // Second update is from vreplicator. 1832 "/update _vt.vreplication set message='Picked source tablet.*", 1833 "/update.*'Running'", 1834 "/update.*'Stopped'.*already reached", 1835 )) 1836 } 1837 1838 func TestPlayerStopAtOther(t *testing.T) { 1839 t.Skip("This test was written to verify a bug fix, but is extremely flaky. Only a manual test is possible") 1840 1841 defer deleteTablet(addTablet(100)) 1842 1843 execStatements(t, []string{ 1844 "create table t1(id int, val varbinary(128), primary key(id))", 1845 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 1846 }) 1847 defer execStatements(t, []string{ 1848 "drop table t1", 1849 fmt.Sprintf("drop table %s.t1", vrepldb), 1850 }) 1851 env.SchemaEngine.Reload(context.Background()) 1852 1853 // Insert a source row. 1854 execStatements(t, []string{ 1855 "insert into t1 values(1, 'aaa')", 1856 }) 1857 startPos := primaryPosition(t) 1858 filter := &binlogdatapb.Filter{ 1859 Rules: []*binlogdatapb.Rule{{ 1860 Match: "/.*", 1861 }}, 1862 } 1863 bls := &binlogdatapb.BinlogSource{ 1864 Keyspace: env.KeyspaceName, 1865 Shard: env.ShardName, 1866 Filter: filter, 1867 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1868 } 1869 query := binlogplayer.CreateVReplicationState("test", bls, startPos, binlogplayer.BlpStopped, vrepldb, 0, 0) 1870 qr, err := playerEngine.Exec(query) 1871 if err != nil { 1872 t.Fatal(err) 1873 } 1874 id := uint32(qr.InsertID) 1875 for q := range globalDBQueries { 1876 if strings.HasPrefix(q, "insert into _vt.vreplication") { 1877 break 1878 } 1879 } 1880 defer func() { 1881 if _, err := playerEngine.Exec(fmt.Sprintf("delete from _vt.vreplication where id = %d", id)); err != nil { 1882 t.Fatal(err) 1883 } 1884 expectDeleteQueries(t) 1885 }() 1886 1887 vconn := &realDBClient{nolog: true} 1888 if err := vconn.Connect(); err != nil { 1889 t.Error(err) 1890 } 1891 defer vconn.Close() 1892 1893 // Insert the same row on the target and lock it. 1894 if _, err := vconn.ExecuteFetch("insert into t1 values(1, 'aaa')", 1); err != nil { 1895 t.Error(err) 1896 } 1897 if _, err := vconn.ExecuteFetch("begin", 1); err != nil { 1898 t.Error(err) 1899 } 1900 if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil { 1901 t.Error(err) 1902 } 1903 1904 // Start a VReplication where the first transaction updates the locked row. 1905 // It will cause the apply to wait, which will cause the other two events 1906 // to accumulate. The stop position will be on the grant. 1907 // We're testing the behavior where an OTHER transaction is part of a batch, 1908 // we have to commit its stop position correctly. 1909 execStatements(t, []string{ 1910 "update t1 set val='ccc' where id=1", 1911 "insert into t1 values(2, 'ddd')", 1912 "grant select on *.* to 'vt_app'@'127.0.0.1'", 1913 }) 1914 stopPos := primaryPosition(t) 1915 query = binlogplayer.StartVReplicationUntil(id, stopPos) 1916 if _, err := playerEngine.Exec(query); err != nil { 1917 t.Fatal(err) 1918 } 1919 1920 // Wait for the begin. The update will be blocked. 1921 expectDBClientQueries(t, qh.Expect( 1922 "/update.*'Running'", 1923 // Second update is from vreplicator. 1924 "/update.*'Running'", 1925 "begin", 1926 )) 1927 1928 // Give time for the other two transactions to reach the relay log. 1929 time.Sleep(100 * time.Millisecond) 1930 _, _ = vconn.ExecuteFetch("rollback", 1) 1931 1932 // This is approximately the expected sequence of updates. 1933 expectDBClientQueries(t, qh.Expect( 1934 "update t1 set val='ccc' where id=1", 1935 "/update _vt.vreplication set pos=", 1936 "commit", 1937 "begin", 1938 "insert into t1(id,val) values (2,'ddd')", 1939 "/update _vt.vreplication set pos=", 1940 "commit", 1941 fmt.Sprintf("/update _vt.vreplication set pos='%s'", stopPos), 1942 "/update.*'Stopped'", 1943 )) 1944 } 1945 1946 func TestPlayerIdleUpdate(t *testing.T) { 1947 defer deleteTablet(addTablet(100)) 1948 1949 savedIdleTimeout := idleTimeout 1950 defer func() { idleTimeout = savedIdleTimeout }() 1951 idleTimeout = 100 * time.Millisecond 1952 1953 execStatements(t, []string{ 1954 "create table t1(id int, val varbinary(128), primary key(id))", 1955 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 1956 }) 1957 defer execStatements(t, []string{ 1958 "drop table t1", 1959 fmt.Sprintf("drop table %s.t1", vrepldb), 1960 }) 1961 env.SchemaEngine.Reload(context.Background()) 1962 1963 filter := &binlogdatapb.Filter{ 1964 Rules: []*binlogdatapb.Rule{{ 1965 Match: "/.*", 1966 }}, 1967 } 1968 bls := &binlogdatapb.BinlogSource{ 1969 Keyspace: env.KeyspaceName, 1970 Shard: env.ShardName, 1971 Filter: filter, 1972 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 1973 } 1974 cancel, _ := startVReplication(t, bls, "") 1975 defer cancel() 1976 1977 execStatements(t, []string{ 1978 "insert into t1 values(1, 'aaa')", 1979 }) 1980 start := time.Now() 1981 expectDBClientQueries(t, qh.Expect( 1982 "begin", 1983 "insert into t1(id,val) values (1,'aaa')", 1984 "/update _vt.vreplication set pos=", 1985 "commit", 1986 ), 1987 "/update _vt.vreplication set pos=", 1988 ) 1989 // The above write will generate a new binlog event, and 1990 // that event will loopback into player as an empty event. 1991 // But it must not get saved until idleTimeout has passed. 1992 // The exact positions are hard to verify because of this 1993 // loopback mechanism. 1994 expectDBClientQueries(t, qh.Expect( 1995 "/update _vt.vreplication set pos=", 1996 )) 1997 if duration := time.Since(start); duration < idleTimeout { 1998 t.Errorf("duration: %v, must be at least %v", duration, idleTimeout) 1999 } 2000 } 2001 2002 func TestPlayerSplitTransaction(t *testing.T) { 2003 defer deleteTablet(addTablet(100)) 2004 setFlag("vstream_packet_size", "10") 2005 defer setFlag("vstream_packet_size", "10000") 2006 2007 execStatements(t, []string{ 2008 "create table t1(id int, val varbinary(128), primary key(id))", 2009 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 2010 }) 2011 defer execStatements(t, []string{ 2012 "drop table t1", 2013 fmt.Sprintf("drop table %s.t1", vrepldb), 2014 }) 2015 env.SchemaEngine.Reload(context.Background()) 2016 2017 filter := &binlogdatapb.Filter{ 2018 Rules: []*binlogdatapb.Rule{{ 2019 Match: "/.*", 2020 }}, 2021 } 2022 bls := &binlogdatapb.BinlogSource{ 2023 Keyspace: env.KeyspaceName, 2024 Shard: env.ShardName, 2025 Filter: filter, 2026 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2027 } 2028 cancel, _ := startVReplication(t, bls, "") 2029 defer cancel() 2030 2031 execStatements(t, []string{ 2032 "begin", 2033 "insert into t1 values(1, '123456')", 2034 "insert into t1 values(2, '789012')", 2035 "commit", 2036 }) 2037 // Because the packet size is 10, this is received as two events, 2038 // but still combined as one transaction. 2039 expectDBClientQueries(t, qh.Expect( 2040 "begin", 2041 "insert into t1(id,val) values (1,'123456')", 2042 "insert into t1(id,val) values (2,'789012')", 2043 "/update _vt.vreplication set pos=", 2044 "commit", 2045 )) 2046 } 2047 2048 func TestPlayerLockErrors(t *testing.T) { 2049 defer deleteTablet(addTablet(100)) 2050 2051 execStatements(t, []string{ 2052 "create table t1(id int, val varbinary(128), primary key(id))", 2053 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 2054 }) 2055 defer execStatements(t, []string{ 2056 "drop table t1", 2057 fmt.Sprintf("drop table %s.t1", vrepldb), 2058 }) 2059 env.SchemaEngine.Reload(context.Background()) 2060 2061 filter := &binlogdatapb.Filter{ 2062 Rules: []*binlogdatapb.Rule{{ 2063 Match: "/.*", 2064 }}, 2065 } 2066 bls := &binlogdatapb.BinlogSource{ 2067 Keyspace: env.KeyspaceName, 2068 Shard: env.ShardName, 2069 Filter: filter, 2070 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2071 } 2072 cancel, _ := startVReplication(t, bls, "") 2073 defer cancel() 2074 2075 execStatements(t, []string{ 2076 "begin", 2077 "insert into t1 values(1, 'aaa')", 2078 "insert into t1 values(2, 'bbb')", 2079 "commit", 2080 }) 2081 expectDBClientQueries(t, qh.Expect( 2082 "begin", 2083 "insert into t1(id,val) values (1,'aaa')", 2084 "insert into t1(id,val) values (2,'bbb')", 2085 "/update _vt.vreplication set pos=", 2086 "commit", 2087 )) 2088 2089 vconn := &realDBClient{nolog: true} 2090 if err := vconn.Connect(); err != nil { 2091 t.Error(err) 2092 } 2093 defer vconn.Close() 2094 2095 // Start a transaction and lock the second row. 2096 if _, err := vconn.ExecuteFetch("begin", 1); err != nil { 2097 t.Error(err) 2098 } 2099 if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=2", 1); err != nil { 2100 t.Error(err) 2101 } 2102 2103 execStatements(t, []string{ 2104 "begin", 2105 "update t1 set val='ccc' where id=1", 2106 "update t1 set val='ccc' where id=2", 2107 "commit", 2108 }) 2109 // The innodb lock wait timeout is set to 1s. 2110 expectDBClientQueries(t, qh.Expect( 2111 "begin", 2112 "update t1 set val='ccc' where id=1", 2113 "update t1 set val='ccc' where id=2", 2114 "rollback", 2115 )) 2116 2117 // Release the lock, and watch the retry go through. 2118 _, _ = vconn.ExecuteFetch("rollback", 1) 2119 expectDBClientQueries(t, qh.Expect( 2120 "begin", 2121 "update t1 set val='ccc' where id=1", 2122 "update t1 set val='ccc' where id=2", 2123 "/update _vt.vreplication set pos=", 2124 "commit", 2125 )) 2126 } 2127 2128 func TestPlayerCancelOnLock(t *testing.T) { 2129 defer deleteTablet(addTablet(100)) 2130 2131 execStatements(t, []string{ 2132 "create table t1(id int, val varbinary(128), primary key(id))", 2133 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 2134 }) 2135 defer execStatements(t, []string{ 2136 "drop table t1", 2137 fmt.Sprintf("drop table %s.t1", vrepldb), 2138 }) 2139 env.SchemaEngine.Reload(context.Background()) 2140 2141 filter := &binlogdatapb.Filter{ 2142 Rules: []*binlogdatapb.Rule{{ 2143 Match: "/.*", 2144 }}, 2145 } 2146 bls := &binlogdatapb.BinlogSource{ 2147 Keyspace: env.KeyspaceName, 2148 Shard: env.ShardName, 2149 Filter: filter, 2150 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2151 } 2152 cancel, _ := startVReplication(t, bls, "") 2153 defer cancel() 2154 2155 execStatements(t, []string{ 2156 "begin", 2157 "insert into t1 values(1, 'aaa')", 2158 "commit", 2159 }) 2160 expectDBClientQueries(t, qh.Expect( 2161 "begin", 2162 "insert into t1(id,val) values (1,'aaa')", 2163 "/update _vt.vreplication set pos=", 2164 "commit", 2165 )) 2166 2167 vconn := &realDBClient{nolog: true} 2168 if err := vconn.Connect(); err != nil { 2169 t.Error(err) 2170 } 2171 defer vconn.Close() 2172 2173 // Start a transaction and lock the row. 2174 if _, err := vconn.ExecuteFetch("begin", 1); err != nil { 2175 t.Error(err) 2176 } 2177 if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil { 2178 t.Error(err) 2179 } 2180 2181 execStatements(t, []string{ 2182 "begin", 2183 "update t1 set val='ccc' where id=1", 2184 "commit", 2185 }) 2186 // The innodb lock wait timeout is set to 1s. 2187 expectDBClientQueries(t, qh.Expect( 2188 "begin", 2189 "update t1 set val='ccc' where id=1", 2190 "rollback", 2191 )) 2192 2193 // VReplication should not get stuck if you cancel now. 2194 done := make(chan bool) 2195 go func() { 2196 cancel() 2197 close(done) 2198 }() 2199 select { 2200 case <-done: 2201 case <-time.After(5 * time.Second): 2202 t.Error("cancel is hung") 2203 } 2204 } 2205 2206 func TestPlayerBatching(t *testing.T) { 2207 defer deleteTablet(addTablet(100)) 2208 2209 execStatements(t, []string{ 2210 "create table t1(id int, val varbinary(128), primary key(id))", 2211 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 2212 }) 2213 defer execStatements(t, []string{ 2214 "drop table t1", 2215 fmt.Sprintf("drop table %s.t1", vrepldb), 2216 }) 2217 env.SchemaEngine.Reload(context.Background()) 2218 2219 filter := &binlogdatapb.Filter{ 2220 Rules: []*binlogdatapb.Rule{{ 2221 Match: "/.*", 2222 }}, 2223 } 2224 bls := &binlogdatapb.BinlogSource{ 2225 Keyspace: env.KeyspaceName, 2226 Shard: env.ShardName, 2227 Filter: filter, 2228 OnDdl: binlogdatapb.OnDDLAction_EXEC, 2229 } 2230 cancel, _ := startVReplication(t, bls, "") 2231 defer cancel() 2232 2233 execStatements(t, []string{ 2234 "insert into t1 values(1, 'aaa')", 2235 }) 2236 expectDBClientQueries(t, qh.Expect( 2237 "begin", 2238 "insert into t1(id,val) values (1,'aaa')", 2239 "/update _vt.vreplication set pos=", 2240 "commit", 2241 )) 2242 2243 vconn := &realDBClient{nolog: true} 2244 if err := vconn.Connect(); err != nil { 2245 t.Error(err) 2246 } 2247 defer vconn.Close() 2248 2249 // Start a transaction and lock the row. 2250 if _, err := vconn.ExecuteFetch("begin", 1); err != nil { 2251 t.Error(err) 2252 } 2253 if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil { 2254 t.Error(err) 2255 } 2256 2257 // create one transaction 2258 execStatements(t, []string{ 2259 "update t1 set val='ccc' where id=1", 2260 }) 2261 // Wait for the begin. The update will be blocked. 2262 expectDBClientQueries(t, qh.Expect( 2263 "begin", 2264 )) 2265 2266 // Create two more transactions. They will go and wait in the relayLog. 2267 execStatements(t, []string{ 2268 "insert into t1 values(2, 'aaa')", 2269 "insert into t1 values(3, 'aaa')", 2270 "alter table t1 add column val2 varbinary(128)", 2271 "alter table t1 drop column val2", 2272 }) 2273 2274 // Release the lock. 2275 _, _ = vconn.ExecuteFetch("rollback", 1) 2276 // First transaction will complete. The other two 2277 // transactions must be batched into one. But the 2278 // DDLs should be on their own. 2279 expectDBClientQueries(t, qh.Expect( 2280 "update t1 set val='ccc' where id=1", 2281 "/update _vt.vreplication set pos=", 2282 "commit", 2283 "begin", 2284 "insert into t1(id,val) values (2,'aaa')", 2285 "insert into t1(id,val) values (3,'aaa')", 2286 "/update _vt.vreplication set pos=", 2287 "commit", 2288 "alter table t1 add column val2 varbinary(128)", 2289 "/update _vt.vreplication set pos=", 2290 "alter table t1 drop column val2", 2291 "/update _vt.vreplication set pos=", 2292 // The apply of the DDLs on target generates two "other" event. 2293 "/update _vt.vreplication set pos=", 2294 "/update _vt.vreplication set pos=", 2295 )) 2296 } 2297 2298 func TestPlayerRelayLogMaxSize(t *testing.T) { 2299 defer deleteTablet(addTablet(100)) 2300 2301 for i := 0; i < 2; i++ { 2302 // First iteration checks max size, second checks max items 2303 func() { 2304 switch i { 2305 case 0: 2306 savedSize := relayLogMaxSize 2307 defer func() { relayLogMaxSize = savedSize }() 2308 relayLogMaxSize = 10 2309 case 1: 2310 savedLen := relayLogMaxItems 2311 defer func() { relayLogMaxItems = savedLen }() 2312 relayLogMaxItems = 2 2313 } 2314 2315 execStatements(t, []string{ 2316 "create table t1(id int, val varbinary(128), primary key(id))", 2317 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 2318 }) 2319 defer execStatements(t, []string{ 2320 "drop table t1", 2321 fmt.Sprintf("drop table %s.t1", vrepldb), 2322 }) 2323 env.SchemaEngine.Reload(context.Background()) 2324 2325 filter := &binlogdatapb.Filter{ 2326 Rules: []*binlogdatapb.Rule{{ 2327 Match: "/.*", 2328 }}, 2329 } 2330 bls := &binlogdatapb.BinlogSource{ 2331 Keyspace: env.KeyspaceName, 2332 Shard: env.ShardName, 2333 Filter: filter, 2334 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2335 } 2336 cancel, _ := startVReplication(t, bls, "") 2337 defer cancel() 2338 2339 execStatements(t, []string{ 2340 "insert into t1 values(1, '123456')", 2341 }) 2342 expectDBClientQueries(t, qh.Expect( 2343 "begin", 2344 "insert into t1(id,val) values (1,'123456')", 2345 "/update _vt.vreplication set pos=", 2346 "commit", 2347 )) 2348 2349 vconn := &realDBClient{nolog: true} 2350 if err := vconn.Connect(); err != nil { 2351 t.Error(err) 2352 } 2353 defer vconn.Close() 2354 2355 // Start a transaction and lock the row. 2356 if _, err := vconn.ExecuteFetch("begin", 1); err != nil { 2357 t.Error(err) 2358 } 2359 if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil { 2360 t.Error(err) 2361 } 2362 2363 // create one transaction 2364 execStatements(t, []string{ 2365 "update t1 set val='ccc' where id=1", 2366 }) 2367 // Wait for the begin. The update will be blocked. 2368 expectDBClientQueries(t, qh.Expect( 2369 "begin", 2370 )) 2371 2372 // Create two more transactions. They will go and wait in the relayLog. 2373 execStatements(t, []string{ 2374 "insert into t1 values(2, '789012')", 2375 "insert into t1 values(3, '345678')", 2376 "insert into t1 values(4, '901234')", 2377 }) 2378 2379 // Release the lock. 2380 _, _ = vconn.ExecuteFetch("rollback", 1) 2381 // First transaction will complete. The other two 2382 // transactions must be batched into one. The last transaction 2383 // will wait to be sent to the relay until the player fetches 2384 // them. 2385 expectDBClientQueries(t, qh.Expect( 2386 "update t1 set val='ccc' where id=1", 2387 "/update _vt.vreplication set pos=", 2388 "commit", 2389 "begin", 2390 "insert into t1(id,val) values (2,'789012')", 2391 "insert into t1(id,val) values (3,'345678')", 2392 "/update _vt.vreplication set pos=", 2393 "commit", 2394 "begin", 2395 "insert into t1(id,val) values (4,'901234')", 2396 "/update _vt.vreplication set pos=", 2397 "commit", 2398 )) 2399 }() 2400 } 2401 } 2402 2403 func TestRestartOnVStreamEnd(t *testing.T) { 2404 defer deleteTablet(addTablet(100)) 2405 2406 savedDelay := retryDelay 2407 defer func() { retryDelay = savedDelay }() 2408 retryDelay = 1 * time.Millisecond 2409 2410 execStatements(t, []string{ 2411 "create table t1(id int, val varbinary(128), primary key(id))", 2412 fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), 2413 }) 2414 defer execStatements(t, []string{ 2415 "drop table t1", 2416 fmt.Sprintf("drop table %s.t1", vrepldb), 2417 }) 2418 env.SchemaEngine.Reload(context.Background()) 2419 2420 filter := &binlogdatapb.Filter{ 2421 Rules: []*binlogdatapb.Rule{{ 2422 Match: "/.*", 2423 }}, 2424 } 2425 bls := &binlogdatapb.BinlogSource{ 2426 Keyspace: env.KeyspaceName, 2427 Shard: env.ShardName, 2428 Filter: filter, 2429 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2430 } 2431 cancel, _ := startVReplication(t, bls, "") 2432 defer cancel() 2433 2434 execStatements(t, []string{ 2435 "insert into t1 values(1, 'aaa')", 2436 }) 2437 expectDBClientQueries(t, qh.Expect( 2438 "begin", 2439 "insert into t1(id,val) values (1,'aaa')", 2440 "/update _vt.vreplication set pos=", 2441 "commit", 2442 )) 2443 2444 streamerEngine.Close() 2445 expectDBClientQueries(t, qh.Expect( 2446 "/update _vt.vreplication set message='vstream ended'", 2447 )) 2448 streamerEngine.Open() 2449 execStatements(t, []string{ 2450 "insert into t1 values(2, 'aaa')", 2451 }) 2452 expectDBClientQueries(t, qh.Expect( 2453 "/update _vt.vreplication set message='Picked source tablet.*", 2454 "/update _vt.vreplication set state='Running'", 2455 "begin", 2456 "insert into t1(id,val) values (2,'aaa')", 2457 "/update _vt.vreplication set pos=", 2458 "commit", 2459 )) 2460 } 2461 2462 func TestTimestamp(t *testing.T) { 2463 defer deleteTablet(addTablet(100)) 2464 2465 execStatements(t, []string{ 2466 "create table t1(id int, ts timestamp, dt datetime)", 2467 fmt.Sprintf("create table %s.t1(id int, ts timestamp, dt datetime)", vrepldb), 2468 }) 2469 defer execStatements(t, []string{ 2470 "drop table t1", 2471 fmt.Sprintf("drop table %s.t1", vrepldb), 2472 }) 2473 env.SchemaEngine.Reload(context.Background()) 2474 2475 filter := &binlogdatapb.Filter{ 2476 Rules: []*binlogdatapb.Rule{{ 2477 Match: "/.*", 2478 }}, 2479 } 2480 2481 bls := &binlogdatapb.BinlogSource{ 2482 Keyspace: env.KeyspaceName, 2483 Shard: env.ShardName, 2484 Filter: filter, 2485 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2486 } 2487 cancel, _ := startVReplication(t, bls, "") 2488 defer cancel() 2489 2490 qr, err := env.Mysqld.FetchSuperQuery(context.Background(), "select now()") 2491 if err != nil { 2492 t.Fatal(err) 2493 } 2494 want := qr.Rows[0][0].ToString() 2495 t.Logf("want: %s", want) 2496 2497 execStatements(t, []string{ 2498 fmt.Sprintf("insert into t1 values(1, '%s', '%s')", want, want), 2499 }) 2500 expectDBClientQueries(t, qh.Expect( 2501 "begin", 2502 // The insert value for ts will be in UTC. 2503 // We'll check the row instead. 2504 "/insert into t1", 2505 "/update _vt.vreplication set pos=", 2506 "commit", 2507 )) 2508 2509 expectData(t, "t1", [][]string{{"1", want, want}}) 2510 } 2511 2512 func shouldRunJSONTests(t *testing.T, name string) bool { 2513 skipTest := true 2514 flavors := []string{"mysql80", "mysql57"} 2515 for _, flavor := range flavors { 2516 if strings.EqualFold(env.Flavor, flavor) { 2517 skipTest = false 2518 break 2519 } 2520 } 2521 if skipTest { 2522 t.Logf("not running %s on %s", name, env.Flavor) 2523 return false 2524 } 2525 return true 2526 } 2527 2528 // TestPlayerJSONDocs validates more complex and 'large' json docs. It only validates that the data on target matches that on source. 2529 // TestPlayerTypes, above, also verifies the sql queries applied on the target. 2530 func TestPlayerJSONDocs(t *testing.T) { 2531 if !shouldRunJSONTests(t, "TestPlayerJSONDocs") { 2532 return 2533 } 2534 defer deleteTablet(addTablet(100)) 2535 2536 execStatements(t, []string{ 2537 "create table vitess_json(id int auto_increment, val json, primary key(id))", 2538 fmt.Sprintf("create table %s.vitess_json(id int, val json, primary key(id))", vrepldb), 2539 }) 2540 defer execStatements(t, []string{ 2541 "drop table vitess_json", 2542 fmt.Sprintf("drop table %s.vitess_json", vrepldb), 2543 }) 2544 2545 env.SchemaEngine.Reload(context.Background()) 2546 2547 filter := &binlogdatapb.Filter{ 2548 Rules: []*binlogdatapb.Rule{{ 2549 Match: "/.*", 2550 }}, 2551 } 2552 bls := &binlogdatapb.BinlogSource{ 2553 Keyspace: env.KeyspaceName, 2554 Shard: env.ShardName, 2555 Filter: filter, 2556 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2557 } 2558 cancel, _ := startVReplication(t, bls, "") 2559 defer cancel() 2560 type testcase struct { 2561 name string 2562 input string 2563 data [][]string 2564 } 2565 var testcases []testcase 2566 id := 0 2567 var addTestCase = func(name, val string) { 2568 id++ 2569 testcases = append(testcases, testcase{ 2570 name: name, 2571 input: fmt.Sprintf("insert into vitess_json(val) values (%s)", encodeString(val)), 2572 data: [][]string{ 2573 {strconv.Itoa(id), val}, 2574 }, 2575 }) 2576 } 2577 addTestCase("singleDoc", jsonSingleDoc) 2578 addTestCase("multipleDocs", jsonMultipleDocs) 2579 longString := strings.Repeat("aa", math.MaxInt16) 2580 2581 largeObject := fmt.Sprintf(singleLargeObjectTemplate, longString) 2582 addTestCase("singleLargeObject", largeObject) 2583 2584 largeArray := fmt.Sprintf(`[1, 1234567890, "a", true, %s]`, largeObject) 2585 _ = largeArray 2586 addTestCase("singleLargeArray", largeArray) 2587 2588 // the json doc is repeated multiple times to hit the 64K threshold: 140 is got by trial and error 2589 addTestCase("largeArrayDoc", repeatJSON(jsonSingleDoc, 140, largeJSONArrayCollection)) 2590 addTestCase("largeObjectDoc", repeatJSON(jsonSingleDoc, 140, largeJSONObjectCollection)) 2591 id = 0 2592 for _, tcase := range testcases { 2593 t.Run(tcase.name, func(t *testing.T) { 2594 id++ 2595 execStatements(t, []string{tcase.input}) 2596 want := qh.Expect( 2597 "begin", 2598 "/insert into vitess_json", 2599 "/update _vt.vreplication set pos=", 2600 "commit", 2601 ) 2602 expectDBClientQueries(t, want) 2603 expectJSON(t, "vitess_json", tcase.data, id, env.Mysqld.FetchSuperQuery) 2604 }) 2605 } 2606 } 2607 2608 // TestPlayerJSONTwoColumns tests for two json columns in a table 2609 func TestPlayerJSONTwoColumns(t *testing.T) { 2610 if !shouldRunJSONTests(t, "TestPlayerJSONTwoColumns") { 2611 return 2612 } 2613 defer deleteTablet(addTablet(100)) 2614 execStatements(t, []string{ 2615 "create table vitess_json2(id int auto_increment, val json, val2 json, primary key(id))", 2616 fmt.Sprintf("create table %s.vitess_json2(id int, val json, val2 json, primary key(id))", vrepldb), 2617 }) 2618 defer execStatements(t, []string{ 2619 "drop table vitess_json2", 2620 fmt.Sprintf("drop table %s.vitess_json2", vrepldb), 2621 }) 2622 2623 env.SchemaEngine.Reload(context.Background()) 2624 2625 filter := &binlogdatapb.Filter{ 2626 Rules: []*binlogdatapb.Rule{{ 2627 Match: "/.*", 2628 }}, 2629 } 2630 bls := &binlogdatapb.BinlogSource{ 2631 Keyspace: env.KeyspaceName, 2632 Shard: env.ShardName, 2633 Filter: filter, 2634 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2635 } 2636 cancel, _ := startVReplication(t, bls, "") 2637 defer cancel() 2638 type testcase struct { 2639 name string 2640 input string 2641 data [][]string 2642 } 2643 var testcases []testcase 2644 id := 0 2645 var addTestCase = func(name, val, val2 string) { 2646 id++ 2647 testcases = append(testcases, testcase{ 2648 name: name, 2649 input: fmt.Sprintf("insert into vitess_json2(val, val2) values (%s, %s)", encodeString(val), encodeString(val2)), 2650 data: [][]string{ 2651 {strconv.Itoa(id), val, val2}, 2652 }, 2653 }) 2654 } 2655 longString := strings.Repeat("aa", math.MaxInt16) 2656 largeObject := fmt.Sprintf(singleLargeObjectTemplate, longString) 2657 addTestCase("twoCols", jsonSingleDoc, largeObject) 2658 id = 0 2659 for _, tcase := range testcases { 2660 t.Run(tcase.name, func(t *testing.T) { 2661 id++ 2662 execStatements(t, []string{tcase.input}) 2663 want := qh.Expect( 2664 "begin", 2665 "/insert into vitess_json2", 2666 "/update _vt.vreplication set pos=", 2667 "commit", 2668 ) 2669 expectDBClientQueries(t, want) 2670 expectJSON(t, "vitess_json2", tcase.data, id, env.Mysqld.FetchSuperQuery) 2671 }) 2672 } 2673 2674 } 2675 2676 func TestVReplicationLogs(t *testing.T) { 2677 defer deleteTablet(addTablet(100)) 2678 dbClient := playerEngine.dbClientFactoryDba() 2679 err := dbClient.Connect() 2680 require.NoError(t, err) 2681 defer dbClient.Close() 2682 vdbc := newVDBClient(dbClient, binlogplayer.NewStats()) 2683 query := "select vrepl_id, state, message, count from _vt.vreplication_log order by id desc limit 1" 2684 2685 expected := []string{ 2686 "[[INT32(1) VARBINARY(\"Running\") TEXT(\"message1\") INT64(1)]]", 2687 "[[INT32(1) VARBINARY(\"Running\") TEXT(\"message1\") INT64(2)]]", 2688 } 2689 2690 for _, want := range expected { 2691 t.Run("", func(t *testing.T) { 2692 err = insertLog(vdbc, LogMessage, 1, "Running", "message1") 2693 require.NoError(t, err) 2694 qr, err := env.Mysqld.FetchSuperQuery(context.Background(), query) 2695 require.NoError(t, err) 2696 require.Equal(t, want, fmt.Sprintf("%v", qr.Rows)) 2697 }) 2698 2699 } 2700 } 2701 2702 func TestGeneratedColumns(t *testing.T) { 2703 flavor := strings.ToLower(env.Flavor) 2704 // Disable tests on percona (which identifies as mysql56) and mariadb platforms in CI since they 2705 // generated columns support was added in 5.7 and mariadb added mysql compatible generated columns in 10.2 2706 if !strings.Contains(flavor, "mysql57") && !strings.Contains(flavor, "mysql80") { 2707 return 2708 } 2709 defer deleteTablet(addTablet(100)) 2710 2711 execStatements(t, []string{ 2712 "create table t1(id int, val varbinary(6), val2 varbinary(6) as (concat(id, val)), val3 varbinary(6) as (concat(val, id)), id2 int, primary key(id))", 2713 fmt.Sprintf("create table %s.t1(id int, val varbinary(6), val2 varbinary(6) as (concat(id, val)), val3 varbinary(6), id2 int, primary key(id))", vrepldb), 2714 "create table t2(id int, val varbinary(128), val2 varbinary(128) as (concat(id, val)) stored, val3 varbinary(128) as (concat(val, id)), id2 int, primary key(id))", 2715 fmt.Sprintf("create table %s.t2(id int, val3 varbinary(128), val varbinary(128), id2 int, primary key(id))", vrepldb), 2716 }) 2717 defer execStatements(t, []string{ 2718 "drop table t1", 2719 fmt.Sprintf("drop table %s.t1", vrepldb), 2720 "drop table t2", 2721 fmt.Sprintf("drop table %s.t2", vrepldb), 2722 }) 2723 env.SchemaEngine.Reload(context.Background()) 2724 2725 filter := &binlogdatapb.Filter{ 2726 Rules: []*binlogdatapb.Rule{{ 2727 Match: "t1", 2728 Filter: "select * from t1", 2729 }, { 2730 Match: "t2", 2731 Filter: "select id, val3, val, id2 from t2", 2732 }}, 2733 } 2734 bls := &binlogdatapb.BinlogSource{ 2735 Keyspace: env.KeyspaceName, 2736 Shard: env.ShardName, 2737 Filter: filter, 2738 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2739 } 2740 cancel, _ := startVReplication(t, bls, "") 2741 defer cancel() 2742 2743 testcases := []struct { 2744 input string 2745 output string 2746 table string 2747 data [][]string 2748 }{{ 2749 input: "insert into t1(id, val, id2) values (1, 'aaa', 10)", 2750 output: "insert into t1(id,val,val3,id2) values (1,'aaa','aaa1',10)", 2751 table: "t1", 2752 data: [][]string{ 2753 {"1", "aaa", "1aaa", "aaa1", "10"}, 2754 }, 2755 }, { 2756 input: "update t1 set val = 'bbb', id2 = 11 where id = 1", 2757 output: "update t1 set val='bbb', val3='bbb1', id2=11 where id=1", 2758 table: "t1", 2759 data: [][]string{ 2760 {"1", "bbb", "1bbb", "bbb1", "11"}, 2761 }, 2762 }, { 2763 input: "insert into t2(id, val, id2) values (1, 'aaa', 10)", 2764 output: "insert into t2(id,val3,val,id2) values (1,'aaa1','aaa',10)", 2765 table: "t2", 2766 data: [][]string{ 2767 {"1", "aaa1", "aaa", "10"}, 2768 }, 2769 }, { 2770 input: "update t2 set val = 'bbb', id2 = 11 where id = 1", 2771 output: "update t2 set val3='bbb1', val='bbb', id2=11 where id=1", 2772 table: "t2", 2773 data: [][]string{ 2774 {"1", "bbb1", "bbb", "11"}, 2775 }, 2776 }} 2777 2778 for _, tcases := range testcases { 2779 execStatements(t, []string{tcases.input}) 2780 output := qh.Expect(tcases.output) 2781 expectNontxQueries(t, output) 2782 if tcases.table != "" { 2783 expectData(t, tcases.table, tcases.data) 2784 } 2785 } 2786 } 2787 2788 func TestPlayerInvalidDates(t *testing.T) { 2789 defer deleteTablet(addTablet(100)) 2790 2791 execStatements(t, []string{ 2792 "create table src1(id int, dt date, primary key(id))", 2793 fmt.Sprintf("create table %s.dst1(id int, dt date, primary key(id))", vrepldb), 2794 }) 2795 defer execStatements(t, []string{ 2796 "drop table src1", 2797 fmt.Sprintf("drop table %s.dst1", vrepldb), 2798 }) 2799 pos := primaryPosition(t) 2800 execStatements(t, []string{"set sql_mode='';insert into src1 values(1, '0000-00-00');set sql_mode='STRICT_TRANS_TABLES';"}) 2801 env.SchemaEngine.Reload(context.Background()) 2802 2803 // default mysql flavor allows invalid dates: so disallow explicitly for this test 2804 if err := env.Mysqld.ExecuteSuperQuery(context.Background(), "SET @@global.sql_mode=REPLACE(REPLACE(@@session.sql_mode, 'NO_ZERO_DATE', ''), 'NO_ZERO_IN_DATE', '')"); err != nil { 2805 fmt.Fprintf(os.Stderr, "%v", err) 2806 } 2807 defer func() { 2808 if err := env.Mysqld.ExecuteSuperQuery(context.Background(), "SET @@global.sql_mode=REPLACE(@@global.sql_mode, ',NO_ZERO_DATE,NO_ZERO_IN_DATE','')"); err != nil { 2809 fmt.Fprintf(os.Stderr, "%v", err) 2810 } 2811 }() 2812 2813 filter := &binlogdatapb.Filter{ 2814 Rules: []*binlogdatapb.Rule{{ 2815 Match: "dst1", 2816 Filter: "select * from src1", 2817 }}, 2818 } 2819 bls := &binlogdatapb.BinlogSource{ 2820 Keyspace: env.KeyspaceName, 2821 Shard: env.ShardName, 2822 Filter: filter, 2823 OnDdl: binlogdatapb.OnDDLAction_IGNORE, 2824 } 2825 cancel, _ := startVReplication(t, bls, pos) 2826 defer cancel() 2827 testcases := []struct { 2828 input string 2829 output string 2830 table string 2831 data [][]string 2832 }{{ 2833 input: "select 1 from dual", 2834 output: "insert into dst1(id,dt) values (1,'0000-00-00')", 2835 table: "dst1", 2836 data: [][]string{ 2837 {"1", "0000-00-00"}, 2838 }, 2839 }, { 2840 input: "insert into src1 values (2, '2020-01-01')", 2841 output: "insert into dst1(id,dt) values (2,'2020-01-01')", 2842 table: "dst1", 2843 data: [][]string{ 2844 {"1", "0000-00-00"}, 2845 {"2", "2020-01-01"}, 2846 }, 2847 }} 2848 2849 for _, tcases := range testcases { 2850 execStatements(t, []string{tcases.input}) 2851 output := qh.Expect(tcases.output) 2852 expectNontxQueries(t, output) 2853 2854 if tcases.table != "" { 2855 // without the sleep there is a flakiness where row inserted by vreplication is not visible to vdbclient 2856 time.Sleep(100 * time.Millisecond) 2857 expectData(t, tcases.table, tcases.data) 2858 } 2859 } 2860 } 2861 func expectJSON(t *testing.T, table string, values [][]string, id int, exec func(ctx context.Context, query string) (*sqltypes.Result, error)) { 2862 t.Helper() 2863 2864 var query string 2865 if len(strings.Split(table, ".")) == 1 { 2866 query = fmt.Sprintf("select * from %s.%s where id=%d", vrepldb, table, id) 2867 } else { 2868 query = fmt.Sprintf("select * from %s where id=%d", table, id) 2869 } 2870 qr, err := exec(context.Background(), query) 2871 if err != nil { 2872 t.Error(err) 2873 return 2874 } 2875 if len(values) != len(qr.Rows) { 2876 t.Fatalf("row counts don't match: %d, want %d", len(qr.Rows), len(values)) 2877 } 2878 for i, row := range values { 2879 if len(row) != len(qr.Rows[i]) { 2880 t.Fatalf("Too few columns, \nrow: %d, \nresult: %d:%v, \nwant: %d:%v", i, len(qr.Rows[i]), qr.Rows[i], len(row), row) 2881 } 2882 if qr.Rows[i][0].ToString() != row[0] { 2883 t.Fatalf("Id mismatch: want %s, got %s", qr.Rows[i][0].ToString(), row[0]) 2884 } 2885 got, err := ajson.Unmarshal([]byte(qr.Rows[i][1].ToString())) 2886 require.NoError(t, err) 2887 want, err := ajson.Unmarshal([]byte(row[1])) 2888 require.NoError(t, err) 2889 match, err := got.Eq(want) 2890 require.NoError(t, err) 2891 require.True(t, match) 2892 } 2893 } 2894 2895 func startVReplication(t *testing.T, bls *binlogdatapb.BinlogSource, pos string) (cancelFunc func(), id int) { 2896 t.Helper() 2897 2898 if pos == "" { 2899 pos = primaryPosition(t) 2900 } 2901 query := binlogplayer.CreateVReplication("test", bls, pos, 9223372036854775807, 9223372036854775807, 0, vrepldb, 0, 0, false) 2902 qr, err := playerEngine.Exec(query) 2903 if err != nil { 2904 t.Fatal(err) 2905 } 2906 expectDBClientQueries(t, qh.Expect( 2907 "/insert into _vt.vreplication", 2908 "/update _vt.vreplication set message='Picked source tablet.*", 2909 "/update _vt.vreplication set state='Running'", 2910 )) 2911 var once sync.Once 2912 return func() { 2913 t.Helper() 2914 once.Do(func() { 2915 query := fmt.Sprintf("delete from _vt.vreplication where id = %d", qr.InsertID) 2916 if _, err := playerEngine.Exec(query); err != nil { 2917 t.Fatal(err) 2918 } 2919 expectDeleteQueries(t) 2920 }) 2921 }, int(qr.InsertID) 2922 }