vitess.io/vitess@v0.16.2/go/vt/binlog/binlogplayer/binlog_player_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 binlogplayer 18 19 import ( 20 "errors" 21 "testing" 22 "time" 23 24 querypb "vitess.io/vitess/go/vt/proto/query" 25 26 "context" 27 28 "vitess.io/vitess/go/mysql" 29 "vitess.io/vitess/go/sqltypes" 30 "vitess.io/vitess/go/vt/throttler" 31 32 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 33 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 34 ) 35 36 var ( 37 testSettingsResponse = &sqltypes.Result{ 38 Fields: []*querypb.Field{ 39 {Name: "pos", Type: sqltypes.VarBinary}, 40 {Name: "stop_pos", Type: sqltypes.VarBinary}, 41 {Name: "max_tps", Type: sqltypes.Int64}, 42 {Name: "max_replication_lag", Type: sqltypes.Int64}, 43 {Name: "state", Type: sqltypes.VarBinary}, 44 {Name: "workflow_type", Type: sqltypes.Int64}, 45 {Name: "workflow", Type: sqltypes.VarChar}, 46 {Name: "workflow_sub_type", Type: sqltypes.Int64}, 47 {Name: "defer_secondary_keys", Type: sqltypes.Int64}, 48 }, 49 RowsAffected: 1, 50 InsertID: 0, 51 Rows: [][]sqltypes.Value{ 52 { 53 sqltypes.NewVarBinary("MariaDB/0-1-1083"), // pos 54 sqltypes.NULL, // stop_pos 55 sqltypes.NewInt64(9223372036854775807), // max_tps 56 sqltypes.NewInt64(9223372036854775807), // max_replication_lag 57 sqltypes.NewVarBinary("Running"), // state 58 sqltypes.NewInt64(1), // workflow_type 59 sqltypes.NewVarChar("wf"), // workflow 60 sqltypes.NewInt64(0), // workflow_sub_type 61 sqltypes.NewInt64(0), // defer_secondary_keys 62 }, 63 }, 64 } 65 testDMLResponse = &sqltypes.Result{RowsAffected: 1} 66 testPos = "MariaDB/0-1-1083" 67 ) 68 69 func TestNewBinlogPlayerKeyRange(t *testing.T) { 70 dbClient := NewMockDBClient(t) 71 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 72 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", testSettingsResponse, nil) 73 dbClient.ExpectRequest("begin", nil, nil) 74 dbClient.ExpectRequest("insert into t values(1)", testDMLResponse, nil) 75 dbClient.ExpectRequestRE("update _vt.vreplication set pos='MariaDB/0-1-1235', time_updated=.*", testDMLResponse, nil) 76 dbClient.ExpectRequest("commit", nil, nil) 77 78 fbc := newFakeBinlogClient() 79 wantTablet := &topodatapb.Tablet{ 80 Alias: &topodatapb.TabletAlias{ 81 Cell: "cell", 82 Uid: 1, 83 }, 84 Keyspace: "ks", 85 Shard: "0", 86 } 87 wantKeyRange := &topodatapb.KeyRange{End: []byte{0x80}} 88 89 blp := NewBinlogPlayerKeyRange(dbClient, wantTablet, wantKeyRange, 1, NewStats()) 90 errfunc := applyEvents(blp) 91 92 dbClient.Wait() 93 expectFBCRequest(t, fbc, wantTablet, testPos, nil, &topodatapb.KeyRange{End: []byte{0x80}}) 94 95 if err := errfunc(); err != nil { 96 t.Error(err) 97 } 98 } 99 100 func TestNewBinlogPlayerTables(t *testing.T) { 101 dbClient := NewMockDBClient(t) 102 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 103 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", testSettingsResponse, nil) 104 dbClient.ExpectRequest("begin", nil, nil) 105 dbClient.ExpectRequest("insert into t values(1)", testDMLResponse, nil) 106 dbClient.ExpectRequestRE("update _vt.vreplication set pos='MariaDB/0-1-1235', time_updated=.*", testDMLResponse, nil) 107 dbClient.ExpectRequest("commit", nil, nil) 108 109 fbc := newFakeBinlogClient() 110 wantTablet := &topodatapb.Tablet{ 111 Alias: &topodatapb.TabletAlias{ 112 Cell: "cell", 113 Uid: 1, 114 }, 115 Keyspace: "ks", 116 Shard: "0", 117 } 118 wantTables := []string{"a", "b"} 119 120 blp := NewBinlogPlayerTables(dbClient, wantTablet, wantTables, 1, NewStats()) 121 errfunc := applyEvents(blp) 122 123 dbClient.Wait() 124 expectFBCRequest(t, fbc, wantTablet, testPos, wantTables, nil) 125 126 if err := errfunc(); err != nil { 127 t.Error(err) 128 } 129 } 130 131 // TestApplyEventsFail ensures the error is recorded in the vreplication table if there's a failure. 132 func TestApplyEventsFail(t *testing.T) { 133 dbClient := NewMockDBClient(t) 134 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 135 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", testSettingsResponse, nil) 136 dbClient.ExpectRequest("begin", nil, errors.New("err")) 137 dbClient.ExpectRequest("update _vt.vreplication set state='Error', message='error in processing binlog event failed query BEGIN, err: err' where id=1", testDMLResponse, nil) 138 139 _ = newFakeBinlogClient() 140 141 blp := NewBinlogPlayerTables(dbClient, nil, []string{"a"}, 1, NewStats()) 142 errfunc := applyEvents(blp) 143 144 dbClient.Wait() 145 146 want := "error in processing binlog event failed query BEGIN, err: err" 147 if err := errfunc(); err == nil || err.Error() != want { 148 t.Errorf("ApplyBinlogEvents err: %v, want %v", err, want) 149 } 150 } 151 152 var settingsFields []*querypb.Field = []*querypb.Field{ 153 {Name: "pos", Type: sqltypes.VarBinary}, 154 {Name: "stop_pos", Type: sqltypes.VarBinary}, 155 {Name: "max_tps", Type: sqltypes.Int64}, 156 {Name: "max_replication_lag", Type: sqltypes.Int64}, 157 {Name: "state", Type: sqltypes.VarBinary}, 158 {Name: "workflow_type", Type: sqltypes.Int64}, 159 {Name: "workflow", Type: sqltypes.VarChar}, 160 {Name: "workflow_sub_type", Type: sqltypes.Int64}, 161 {Name: "defer_secondary_keys", Type: sqltypes.Int64}, 162 } 163 164 // TestStopPosEqual ensures player stops if stopPos==pos. 165 func TestStopPosEqual(t *testing.T) { 166 dbClient := NewMockDBClient(t) 167 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 168 posEqual := &sqltypes.Result{ 169 Fields: settingsFields, 170 RowsAffected: 1, 171 InsertID: 0, 172 Rows: [][]sqltypes.Value{ 173 { 174 sqltypes.NewVarBinary("MariaDB/0-1-1083"), // pos 175 sqltypes.NewVarBinary("MariaDB/0-1-1083"), // stop_pos 176 sqltypes.NewInt64(9223372036854775807), // max_tps 177 sqltypes.NewInt64(9223372036854775807), // max_replication_lag 178 sqltypes.NewVarBinary("Running"), // state 179 sqltypes.NewInt64(1), // workflow_type 180 sqltypes.NewVarChar("wf"), // workflow 181 sqltypes.NewInt64(1), // workflow_sub_type 182 sqltypes.NewInt64(1), // defer_secondary_keys 183 }, 184 }, 185 } 186 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", posEqual, nil) 187 dbClient.ExpectRequest(`update _vt.vreplication set state='Stopped', message='not starting BinlogPlayer, we\'re already at the desired position 0-1-1083' where id=1`, testDMLResponse, nil) 188 189 _ = newFakeBinlogClient() 190 191 blp := NewBinlogPlayerTables(dbClient, nil, []string{"a"}, 1, NewStats()) 192 errfunc := applyEvents(blp) 193 194 dbClient.Wait() 195 196 if err := errfunc(); err != nil { 197 t.Error(err) 198 } 199 } 200 201 // TestStopPosLess ensures player stops if stopPos<pos. 202 func TestStopPosLess(t *testing.T) { 203 dbClient := NewMockDBClient(t) 204 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 205 posEqual := &sqltypes.Result{ 206 Fields: settingsFields, 207 RowsAffected: 1, 208 InsertID: 0, 209 Rows: [][]sqltypes.Value{ 210 { 211 sqltypes.NewVarBinary("MariaDB/0-1-1083"), // pos 212 sqltypes.NewVarBinary("MariaDB/0-1-1082"), // stop_pos 213 sqltypes.NewInt64(9223372036854775807), // max_tps 214 sqltypes.NewInt64(9223372036854775807), // max_replication_lag 215 sqltypes.NewVarBinary("Running"), // state 216 sqltypes.NewInt64(1), // workflow_type 217 sqltypes.NewVarChar("wf"), // workflow 218 sqltypes.NewInt64(1), // workflow_sub_type 219 sqltypes.NewInt64(1), // defer_secondary_keys 220 }, 221 }, 222 } 223 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", posEqual, nil) 224 dbClient.ExpectRequest(`update _vt.vreplication set state='Stopped', message='starting point 0-1-1083 greater than stopping point 0-1-1082' where id=1`, testDMLResponse, nil) 225 226 _ = newFakeBinlogClient() 227 228 blp := NewBinlogPlayerTables(dbClient, nil, []string{"a"}, 1, NewStats()) 229 errfunc := applyEvents(blp) 230 231 dbClient.Wait() 232 233 if err := errfunc(); err != nil { 234 t.Error(err) 235 } 236 } 237 238 // TestStopPosGreater ensures player stops if stopPos>pos. 239 func TestStopPosGreater(t *testing.T) { 240 dbClient := NewMockDBClient(t) 241 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 242 posEqual := &sqltypes.Result{ 243 Fields: settingsFields, 244 RowsAffected: 1, 245 InsertID: 0, 246 Rows: [][]sqltypes.Value{ 247 { 248 sqltypes.NewVarBinary("MariaDB/0-1-1083"), // pos 249 sqltypes.NewVarBinary("MariaDB/0-1-1085"), // stop_pos 250 sqltypes.NewInt64(9223372036854775807), // max_tps 251 sqltypes.NewInt64(9223372036854775807), // max_replication_lag 252 sqltypes.NewVarBinary("Running"), // state 253 sqltypes.NewInt64(1), // workflow_type 254 sqltypes.NewVarChar("wf"), // workflow 255 sqltypes.NewInt64(1), // workflow_sub_type 256 sqltypes.NewInt64(1), // defer_secondary_keys 257 }, 258 }, 259 } 260 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", posEqual, nil) 261 dbClient.ExpectRequest("begin", nil, nil) 262 dbClient.ExpectRequest("insert into t values(1)", testDMLResponse, nil) 263 dbClient.ExpectRequestRE("update _vt.vreplication set pos='MariaDB/0-1-1235', time_updated=.*", testDMLResponse, nil) 264 dbClient.ExpectRequest("commit", nil, nil) 265 dbClient.ExpectRequest(`update _vt.vreplication set state='Stopped', message='Reached stopping position, done playing logs' where id=1`, testDMLResponse, nil) 266 267 _ = newFakeBinlogClient() 268 269 blp := NewBinlogPlayerTables(dbClient, nil, []string{"a"}, 1, NewStats()) 270 errfunc := applyEvents(blp) 271 272 dbClient.Wait() 273 274 if err := errfunc(); err != nil { 275 t.Error(err) 276 } 277 } 278 279 // TestContextCancel ensures player does not record error or stop if context is canceled. 280 func TestContextCancel(t *testing.T) { 281 dbClient := NewMockDBClient(t) 282 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 283 posEqual := &sqltypes.Result{ 284 Fields: settingsFields, 285 RowsAffected: 1, 286 InsertID: 0, 287 Rows: [][]sqltypes.Value{ 288 { 289 sqltypes.NewVarBinary("MariaDB/0-1-1083"), // pos 290 sqltypes.NewVarBinary("MariaDB/0-1-1085"), // stop_pos 291 sqltypes.NewInt64(9223372036854775807), // max_tps 292 sqltypes.NewInt64(9223372036854775807), // max_replication_lag 293 sqltypes.NewVarBinary("Running"), // state 294 sqltypes.NewInt64(1), // workflow_type 295 sqltypes.NewVarChar("wf"), // workflow 296 sqltypes.NewInt64(1), // workflow_sub_type 297 sqltypes.NewInt64(1), // defer_secondary_keys 298 }, 299 }, 300 } 301 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", posEqual, nil) 302 dbClient.ExpectRequest("begin", nil, nil) 303 dbClient.ExpectRequest("insert into t values(1)", testDMLResponse, nil) 304 dbClient.ExpectRequestRE("update _vt.vreplication set pos='MariaDB/0-1-1235', time_updated=.*", testDMLResponse, nil) 305 dbClient.ExpectRequest("commit", nil, nil) 306 dbClient.ExpectRequest(`update _vt.vreplication set state='Stopped', message='Reached stopping position, done playing logs' where id=1`, testDMLResponse, nil) 307 308 _ = newFakeBinlogClient() 309 310 blp := NewBinlogPlayerTables(dbClient, nil, []string{"a"}, 1, NewStats()) 311 errfunc := applyEvents(blp) 312 313 dbClient.Wait() 314 315 // Wait for Apply to return, 316 // and call dbClient.Wait to ensure 317 // no new statements were issued. 318 if err := errfunc(); err != nil { 319 t.Error(err) 320 } 321 322 dbClient.Wait() 323 } 324 325 func TestRetryOnDeadlock(t *testing.T) { 326 dbClient := NewMockDBClient(t) 327 dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) 328 dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", testSettingsResponse, nil) 329 deadlocked := &mysql.SQLError{Num: 1213, Message: "deadlocked"} 330 dbClient.ExpectRequest("begin", nil, nil) 331 dbClient.ExpectRequest("insert into t values(1)", nil, deadlocked) 332 dbClient.ExpectRequest("rollback", nil, nil) 333 dbClient.ExpectRequest("begin", nil, nil) 334 dbClient.ExpectRequest("insert into t values(1)", testDMLResponse, nil) 335 dbClient.ExpectRequestRE("update _vt.vreplication set pos='MariaDB/0-1-1235', time_updated=.*", testDMLResponse, nil) 336 dbClient.ExpectRequest("commit", nil, nil) 337 338 blp := NewBinlogPlayerTables(dbClient, nil, []string{"a"}, 1, NewStats()) 339 blp.deadlockRetry = 10 * time.Millisecond 340 errfunc := applyEvents(blp) 341 342 dbClient.Wait() 343 344 if err := errfunc(); err != nil { 345 t.Error(err) 346 } 347 } 348 349 // applyEvents starts a goroutine to apply events, and returns an error function. 350 // The error func must be invoked before exiting the test to ensure that apply 351 // has finished. Otherwise, it may cause race with other tests. 352 func applyEvents(blp *BinlogPlayer) func() error { 353 errChan := make(chan error) 354 ctx, cancel := context.WithCancel(context.Background()) 355 356 go func() { 357 errChan <- blp.ApplyBinlogEvents(ctx) 358 }() 359 360 return func() error { 361 cancel() 362 return <-errChan 363 } 364 } 365 366 func TestCreateVReplicationKeyRange(t *testing.T) { 367 want := "insert into _vt.vreplication " + 368 "(workflow, source, pos, max_tps, max_replication_lag, time_updated, transaction_timestamp, state, db_name, workflow_type, workflow_sub_type, defer_secondary_keys) " + 369 `values ('Resharding', 'keyspace:\"ks\" shard:\"0\" key_range:{end:\"\\x80\"}', 'MariaDB/0-1-1083', 9223372036854775807, 9223372036854775807, 481823, 0, 'Running', 'db', 0, 0, false)` 370 371 bls := binlogdatapb.BinlogSource{ 372 Keyspace: "ks", 373 Shard: "0", 374 KeyRange: &topodatapb.KeyRange{ 375 End: []byte{0x80}, 376 }, 377 } 378 379 got := CreateVReplication("Resharding", &bls, "MariaDB/0-1-1083", throttler.MaxRateModuleDisabled, throttler.ReplicationLagModuleDisabled, 481823, "db", 0, 0, false) 380 if got != want { 381 t.Errorf("CreateVReplication() =\n%v, want\n%v", got, want) 382 } 383 } 384 385 func TestCreateVReplicationTables(t *testing.T) { 386 want := "insert into _vt.vreplication " + 387 "(workflow, source, pos, max_tps, max_replication_lag, time_updated, transaction_timestamp, state, db_name, workflow_type, workflow_sub_type, defer_secondary_keys) " + 388 `values ('Resharding', 'keyspace:\"ks\" shard:\"0\" tables:\"a\" tables:\"b\"', 'MariaDB/0-1-1083', 9223372036854775807, 9223372036854775807, 481823, 0, 'Running', 'db', 0, 0, false)` 389 390 bls := binlogdatapb.BinlogSource{ 391 Keyspace: "ks", 392 Shard: "0", 393 Tables: []string{"a", "b"}, 394 } 395 396 got := CreateVReplication("Resharding", &bls, "MariaDB/0-1-1083", throttler.MaxRateModuleDisabled, throttler.ReplicationLagModuleDisabled, 481823, "db", 0, 0, false) 397 if got != want { 398 t.Errorf("CreateVReplication() =\n%v, want\n%v", got, want) 399 } 400 } 401 402 func TestUpdateVReplicationPos(t *testing.T) { 403 gtid := mysql.MustParseGTID("MariaDB", "0-1-8283") 404 want := "update _vt.vreplication " + 405 "set pos='MariaDB/0-1-8283', time_updated=88822, rows_copied=0, message='' " + 406 "where id=78522" 407 408 got := GenerateUpdatePos(78522, mysql.Position{GTIDSet: gtid.GTIDSet()}, 88822, 0, 0, false) 409 if got != want { 410 t.Errorf("updateVReplicationPos() = %#v, want %#v", got, want) 411 } 412 } 413 414 func TestUpdateVReplicationTimestamp(t *testing.T) { 415 gtid := mysql.MustParseGTID("MariaDB", "0-2-582") 416 want := "update _vt.vreplication " + 417 "set pos='MariaDB/0-2-582', time_updated=88822, transaction_timestamp=481828, rows_copied=0, message='' " + 418 "where id=78522" 419 420 got := GenerateUpdatePos(78522, mysql.Position{GTIDSet: gtid.GTIDSet()}, 88822, 481828, 0, false) 421 if got != want { 422 t.Errorf("updateVReplicationPos() = %#v, want %#v", got, want) 423 } 424 } 425 426 func TestReadVReplicationPos(t *testing.T) { 427 want := "select pos from _vt.vreplication where id=482821" 428 got := ReadVReplicationPos(482821) 429 if got != want { 430 t.Errorf("ReadVReplicationPos(482821) = %#v, want %#v", got, want) 431 } 432 } 433 434 func TestReadVReplicationStatus(t *testing.T) { 435 want := "select pos, state, message from _vt.vreplication where id=482821" 436 got := ReadVReplicationStatus(482821) 437 if got != want { 438 t.Errorf("ReadVReplicationStatus(482821) = %#v, want %#v", got, want) 439 } 440 }