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  }