vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/vstreamer/uvstreamer_flaky_test.go (about)

     1  /*
     2  Copyright 2020 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  /*
    18  TestVStreamCopyCompleteFlow tests a complete happy VStream Copy flow: copy/catchup/fastforward/replicate
    19  Three tables t1, t2, t3 are copied. Initially 10 (numInitialRows) rows are inserted into each.
    20  To avoid races in testing we send additional events when *uvstreamerTestMode* is set to true. These are used in
    21  conjunction with callbacks to do additional crud at precise points of the flow to test the different paths
    22  We intercept the vstreamer send callback to look for specific events and invoke these test callbacks.
    23  Fast forward requires tables to be locked briefly to get a snapshot: the test uses this knowledge to hold a lock
    24  on the table in order to insert rows for fastforward to find.
    25  
    26  The flow is as follows:
    27  	t1: copy phase, 10 rows.
    28  		The lastpk event is intercepted
    29  		A row is inserted into t1 to be found in catchup
    30  		A row is inserted into t2 which will be an empty transaction during t1 catchup but will be found in t2 copy
    31  	t1/t2: catchup phase finds inserted row in t1
    32  	t2: copy phase to start. Test event is sent, intercepted, we lock t2 to block t2's copy, and a row is inserted into t1 and then unlock
    33  	t2: fastforward finds t1 event
    34  	t2: copy starts
    35  	t2: copy complete
    36  	t3: copy phase to start. Test event is sent, intercepted, we lock t3 to block t3's copy, and two rows are, one each into t1 and t2 and then unlock
    37  	t3: fastforward finds t1 and t2 events
    38  	t3: copy starts
    39  	t3: copy complete, all tables copied, Copy Complete test event sent, insert 3 rows, one each into t1/t2/t3
    40  	replicate: finds the 3 inserts, context is cancelled after last expected row callback and its commit, vstream context is cancelled
    41  */
    42  
    43  package vstreamer
    44  
    45  import (
    46  	"context"
    47  	"fmt"
    48  	"regexp"
    49  	"strings"
    50  	"sync"
    51  	"testing"
    52  	"time"
    53  
    54  	"vitess.io/vitess/go/vt/dbconfigs"
    55  
    56  	"vitess.io/vitess/go/sqltypes"
    57  	"vitess.io/vitess/go/vt/proto/query"
    58  
    59  	"github.com/stretchr/testify/require"
    60  
    61  	"vitess.io/vitess/go/vt/log"
    62  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    63  )
    64  
    65  const (
    66  	createTableQuery = "create table %s(id%d1 int, id%d2 int, primary key(id%d1))"
    67  	bulkInsertQuery  = "insert into %s (id%d1, id%d2) values "
    68  	insertQuery      = "insert into %s (id%d1, id%d2) values (%d, %d)"
    69  	numInitialRows   = 10
    70  )
    71  
    72  type state struct {
    73  	tables []string
    74  }
    75  
    76  var testState = &state{}
    77  
    78  var positions map[string]string
    79  var allEvents []*binlogdatapb.VEvent
    80  var muAllEvents sync.Mutex
    81  var callbacks map[string]func()
    82  
    83  func TestVStreamCopyFilterValidations(t *testing.T) {
    84  	if testing.Short() {
    85  		t.Skip()
    86  	}
    87  	ctx, cancel := context.WithCancel(context.Background())
    88  	defer cancel()
    89  
    90  	defer execStatements(t, []string{
    91  		"drop table t1",
    92  		"drop table t2a",
    93  		"drop table t2b",
    94  	})
    95  
    96  	execStatements(t, []string{
    97  		"create table t1(id11 int, id12 int, primary key(id11))",
    98  		"create table t2a(id21 int, id22 int, primary key(id21))",
    99  		"create table t2b(id21 int, id22 int, primary key(id21))",
   100  	})
   101  	engine.se.Reload(context.Background())
   102  
   103  	var getUVStreamer = func(filter *binlogdatapb.Filter, tablePKs []*binlogdatapb.TableLastPK) *uvstreamer {
   104  		uvs := &uvstreamer{
   105  			ctx:        ctx,
   106  			cancel:     cancel,
   107  			vse:        nil,
   108  			send:       nil,
   109  			cp:         dbconfigs.Connector{},
   110  			se:         engine.se,
   111  			startPos:   "",
   112  			filter:     filter,
   113  			vschema:    nil,
   114  			config:     nil,
   115  			inTablePKs: tablePKs,
   116  		}
   117  		return uvs
   118  	}
   119  	var testFilter = func(rules []*binlogdatapb.Rule, tablePKs []*binlogdatapb.TableLastPK, expected []string, expectedError string) {
   120  		uvs := getUVStreamer(&binlogdatapb.Filter{Rules: rules}, tablePKs)
   121  		if expectedError == "" {
   122  			require.NoError(t, uvs.init())
   123  		} else {
   124  			require.Error(t, uvs.init(), expectedError)
   125  			return
   126  		}
   127  		require.Equal(t, len(expected), len(uvs.plans))
   128  		for _, tableName := range expected {
   129  			require.True(t, uvs.plans[tableName].tablePK.TableName == tableName)
   130  			if tablePKs == nil {
   131  				require.Nil(t, uvs.plans[tableName].tablePK.Lastpk)
   132  			}
   133  		}
   134  		for _, pk := range tablePKs {
   135  			require.Equal(t, uvs.plans[pk.TableName].tablePK, pk)
   136  		}
   137  	}
   138  
   139  	type TestCase struct {
   140  		rules         []*binlogdatapb.Rule
   141  		tablePKs      []*binlogdatapb.TableLastPK
   142  		expected      []string
   143  		expectedError string
   144  	}
   145  
   146  	var testCases []*TestCase
   147  
   148  	testCases = append(testCases, &TestCase{[]*binlogdatapb.Rule{{Match: "t1"}}, nil, []string{"t1"}, ""})
   149  	testCases = append(testCases, &TestCase{[]*binlogdatapb.Rule{{Match: "t2a"}, {Match: "t1"}}, nil, []string{"t1", "t2a"}, ""})
   150  	testCases = append(testCases, &TestCase{[]*binlogdatapb.Rule{{Match: "/.*"}}, nil, []string{"t1", "t2a", "t2b"}, ""})
   151  	testCases = append(testCases, &TestCase{[]*binlogdatapb.Rule{{Match: "/t2.*"}}, nil, []string{"t2a", "t2b"}, ""})
   152  
   153  	tablePKs := []*binlogdatapb.TableLastPK{{
   154  		TableName: "t1",
   155  		Lastpk:    getQRFromLastPK([]*query.Field{{Name: "id11", Type: query.Type_INT32}}, []sqltypes.Value{sqltypes.NewInt32(10)}),
   156  	}}
   157  	testCases = append(testCases, &TestCase{[]*binlogdatapb.Rule{{Match: "t1"}}, tablePKs, []string{"t1"}, ""})
   158  
   159  	testCases = append(testCases, &TestCase{[]*binlogdatapb.Rule{{Match: "/.*"}, {Match: "xyz"}}, nil, []string{""}, "table xyz is not present in the database"})
   160  	testCases = append(testCases, &TestCase{[]*binlogdatapb.Rule{{Match: "/x.*"}}, nil, []string{""}, "stream needs a position or a table to copy"})
   161  
   162  	for _, tc := range testCases {
   163  		log.Infof("Running %v", tc.rules)
   164  		testFilter(tc.rules, tc.tablePKs, tc.expected, tc.expectedError)
   165  	}
   166  }
   167  
   168  func TestVStreamCopyCompleteFlow(t *testing.T) {
   169  	if testing.Short() {
   170  		t.Skip()
   171  	}
   172  	ctx, cancel := context.WithCancel(context.Background())
   173  	defer cancel()
   174  	engine.se.Reload(context.Background())
   175  
   176  	defer execStatements(t, []string{
   177  		"drop table t1",
   178  		"drop table t2",
   179  		"drop table t3",
   180  	})
   181  
   182  	uvstreamerTestMode = true
   183  	defer func() { uvstreamerTestMode = false }()
   184  	initialize(t)
   185  
   186  	if err := engine.se.Reload(context.Background()); err != nil {
   187  		t.Fatal("Error reloading schema")
   188  	}
   189  
   190  	var rules []*binlogdatapb.Rule
   191  	var tablePKs []*binlogdatapb.TableLastPK
   192  	for i, table := range testState.tables {
   193  		rules = append(rules, getRule(table))
   194  
   195  		// for table t2, let tablepk be nil, so that we don't send events for the insert in initTables()
   196  		if table == "t2" {
   197  			continue
   198  		}
   199  
   200  		tablePKs = append(tablePKs, getTablePK(table, i+1))
   201  	}
   202  	filter := &binlogdatapb.Filter{
   203  		Rules: rules,
   204  	}
   205  
   206  	// Test event called after t1 copy is complete
   207  	callbacks["OTHER.*Copy Start t2"] = func() {
   208  		conn, err := env.Mysqld.GetDbaConnection(ctx)
   209  		require.NoError(t, err)
   210  		defer conn.Close()
   211  
   212  		log.Info("Inserting row for fast forward to find, locking t2")
   213  		conn.ExecuteFetch("lock tables t2 write", 1, false)
   214  		insertRow(t, "t1", 1, numInitialRows+2)
   215  		log.Infof("Position after second insert into t1: %s", primaryPosition(t))
   216  		conn.ExecuteFetch("unlock tables", 1, false)
   217  		log.Info("Inserted row for fast forward to find, unlocked tables")
   218  
   219  	}
   220  
   221  	callbacks["OTHER.*Copy Start t3"] = func() {
   222  		conn, err := env.Mysqld.GetDbaConnection(ctx)
   223  		require.NoError(t, err)
   224  		defer conn.Close()
   225  
   226  		log.Info("Inserting row for fast forward to find, locking t3")
   227  		conn.ExecuteFetch("lock tables t3 write", 1, false)
   228  		insertRow(t, "t1", 1, numInitialRows+3)
   229  		insertRow(t, "t2", 2, numInitialRows+2)
   230  		log.Infof("Position after third insert into t1: %s", primaryPosition(t))
   231  		conn.ExecuteFetch("unlock tables", 1, false)
   232  		log.Info("Inserted rows for fast forward to find, unlocked tables")
   233  
   234  	}
   235  
   236  	callbacks["COPY_COMPLETED"] = func() {
   237  		log.Info("Copy done, inserting events to stream")
   238  		insertRow(t, "t1", 1, numInitialRows+4)
   239  		insertRow(t, "t2", 2, numInitialRows+3)
   240  		// savepoints should not be sent in the event stream
   241  		execStatement(t, `
   242  begin;
   243  insert into t3 (id31, id32) values (12, 360);
   244  savepoint a;
   245  insert into t3 (id31, id32) values (13, 390);
   246  rollback work to savepoint a;
   247  savepoint b;
   248  insert into t3 (id31, id32) values (13, 390);
   249  release savepoint b;
   250  commit;"
   251  `)
   252  	}
   253  
   254  	numCopyEvents := 3 /*t1,t2,t3*/ * (numInitialRows + 1 /*FieldEvent*/ + 1 /*LastPKEvent*/ + 1 /*TestEvent: Copy Start*/ + 2 /*begin,commit*/ + 3 /* LastPK Completed*/)
   255  	numCopyEvents += 2                                    /* GTID + Event after all copy is done */
   256  	numCatchupEvents := 3 * 5                             /* 2 t1, 1 t2 : BEGIN+FIELD+ROW+GTID+COMMIT */
   257  	numFastForwardEvents := 5                             /*t1:FIELD+ROW*/
   258  	numMisc := 1                                          /* t2 insert during t1 catchup that comes in t2 copy */
   259  	numReplicateEvents := 2*5 /* insert into t1/t2 */ + 6 /* begin/field/2 inserts/gtid/commit */
   260  	numExpectedEvents := numCopyEvents + numCatchupEvents + numFastForwardEvents + numMisc + numReplicateEvents
   261  
   262  	var lastRowEventSeen bool
   263  
   264  	callbacks["ROW.*t3.*13390"] = func() {
   265  		log.Infof("Saw last row event")
   266  		lastRowEventSeen = true
   267  	}
   268  
   269  	callbacks["COMMIT"] = func() {
   270  		log.Infof("Got commit, lastRowSeen is %t", lastRowEventSeen)
   271  		if lastRowEventSeen {
   272  			log.Infof("Found last row event, canceling context")
   273  			cancel()
   274  		}
   275  	}
   276  	resetMetrics(t)
   277  	startVStreamCopy(ctx, t, filter, tablePKs)
   278  
   279  	select {
   280  	case <-time.After(5 * time.Second):
   281  		printAllEvents("Timed out")
   282  		t.Fatal("Timed out waiting for events")
   283  	case <-ctx.Done():
   284  		log.Infof("Received context.Done, ending test")
   285  	}
   286  	muAllEvents.Lock()
   287  	defer muAllEvents.Unlock()
   288  	if len(allEvents) != numExpectedEvents {
   289  		printAllEvents(fmt.Sprintf("Received %d events, expected %d", len(allEvents), numExpectedEvents))
   290  		t.Fatalf("Received %d events, expected %d", len(allEvents), numExpectedEvents)
   291  	} else {
   292  		log.Infof("Successfully received %d events", numExpectedEvents)
   293  	}
   294  	validateReceivedEvents(t)
   295  	validateMetrics(t)
   296  }
   297  
   298  func validateReceivedEvents(t *testing.T) {
   299  	for i, ev := range allEvents {
   300  		ev.Timestamp = 0
   301  		if ev.Type == binlogdatapb.VEventType_FIELD {
   302  			for j := range ev.FieldEvent.Fields {
   303  				ev.FieldEvent.Fields[j].Flags = 0
   304  				ev.FieldEvent.Keyspace = ""
   305  				ev.FieldEvent.Shard = ""
   306  			}
   307  		}
   308  		if ev.Type == binlogdatapb.VEventType_ROW {
   309  			ev.RowEvent.Keyspace = ""
   310  			ev.RowEvent.Shard = ""
   311  		}
   312  		got := ev.String()
   313  		want := env.RemoveAnyDeprecatedDisplayWidths(expectedEvents[i])
   314  		if !strings.HasPrefix(got, want) {
   315  			printAllEvents("Events not received in the right order")
   316  			t.Fatalf("Event %d did not match, want %s, got %s", i, want, got)
   317  		}
   318  	}
   319  }
   320  
   321  func resetMetrics(t *testing.T) {
   322  	engine.vstreamerEventsStreamed.Reset()
   323  	engine.resultStreamerNumRows.Reset()
   324  	engine.rowStreamerNumRows.Reset()
   325  	engine.vstreamerPhaseTimings.Reset()
   326  	engine.rowStreamerWaits.Reset()
   327  }
   328  
   329  func validateMetrics(t *testing.T) {
   330  	require.Equal(t, engine.vstreamerEventsStreamed.Get(), int64(len(allEvents)))
   331  	require.Equal(t, engine.resultStreamerNumRows.Get(), int64(0))
   332  	require.Equal(t, engine.rowStreamerNumRows.Get(), int64(31))
   333  	require.Equal(t, engine.vstreamerPhaseTimings.Counts()["VStreamerTest.copy"], int64(3))
   334  	require.Equal(t, engine.vstreamerPhaseTimings.Counts()["VStreamerTest.catchup"], int64(2))
   335  	require.Equal(t, engine.vstreamerPhaseTimings.Counts()["VStreamerTest.fastforward"], int64(2))
   336  	require.Equal(t, engine.rowStreamerWaits.Counts()["VStreamerTest.waitForMySQL"], int64(0))
   337  }
   338  
   339  func insertMultipleRows(t *testing.T, table string, idx int, numRows int) {
   340  	query1 := fmt.Sprintf(bulkInsertQuery, table, idx, idx)
   341  	s := ""
   342  	for i := 1; i <= numRows; i++ {
   343  		if s != "" {
   344  			s += ","
   345  		}
   346  		s += fmt.Sprintf("(%d,%d)", i, i*idx*10)
   347  	}
   348  	query1 += s
   349  	execStatement(t, query1)
   350  }
   351  
   352  func initTables(t *testing.T, tables []string) {
   353  	var idx int
   354  	positions["start"] = primaryPosition(t)
   355  	for i, table := range tables {
   356  		idx = i + 1
   357  		execStatement(t, fmt.Sprintf(createTableQuery, table, idx, idx, idx))
   358  	}
   359  	for i, table := range tables {
   360  		tableName := table
   361  		idx = i + 1
   362  		insertMultipleRows(t, table, idx, numInitialRows)
   363  		positions[fmt.Sprintf("%sBulkInsert", table)] = primaryPosition(t)
   364  
   365  		callbacks[fmt.Sprintf("LASTPK.*%s.*%d", table, numInitialRows)] = func() {
   366  			ctx := context.Background()
   367  			if tableName == "t1" {
   368  				idx := 1
   369  				id := numInitialRows + 1
   370  				table := "t1"
   371  				query1 := fmt.Sprintf(insertQuery, table, idx, idx, id, id*idx*10)
   372  				idx = 2
   373  				table = "t2"
   374  				query2 := fmt.Sprintf(insertQuery, table, idx, idx, id, id*idx*10)
   375  
   376  				queries := []string{
   377  					"begin",
   378  					query1,
   379  					query2,
   380  					"commit",
   381  				}
   382  				env.Mysqld.ExecuteSuperQueryList(ctx, queries)
   383  				log.Infof("Position after first insert into t1 and t2: %s", primaryPosition(t))
   384  			}
   385  		}
   386  	}
   387  	positions["afterInitialInsert"] = primaryPosition(t)
   388  }
   389  
   390  func initialize(t *testing.T) {
   391  	callbacks = make(map[string]func())
   392  	testState.tables = []string{"t1", "t2", "t3"}
   393  	positions = make(map[string]string)
   394  	initTables(t, testState.tables)
   395  	callbacks["gtid.*"+positions["afterInitialInsert"]] = func() {
   396  		log.Infof("Callback: afterInitialInsert")
   397  	}
   398  }
   399  
   400  func getRule(table string) *binlogdatapb.Rule {
   401  	return &binlogdatapb.Rule{
   402  		Match:  table,
   403  		Filter: fmt.Sprintf("select * from %s", table),
   404  	}
   405  }
   406  
   407  func getTablePK(table string, idx int) *binlogdatapb.TableLastPK {
   408  	fields := []*query.Field{{Name: fmt.Sprintf("id%d1", idx), Type: query.Type_INT32}}
   409  
   410  	lastPK := []sqltypes.Value{sqltypes.NewInt32(0)}
   411  	return &binlogdatapb.TableLastPK{
   412  		TableName: table,
   413  		Lastpk:    getQRFromLastPK(fields, lastPK),
   414  	}
   415  }
   416  
   417  func insertRow(t *testing.T, table string, idx int, id int) {
   418  	execStatement(t, fmt.Sprintf(insertQuery, table, idx, idx, id, id*idx*10))
   419  }
   420  
   421  func printAllEvents(msg string) {
   422  	log.Errorf("%s: Received %d events", msg, len(allEvents))
   423  	for i, ev := range allEvents {
   424  		log.Errorf("%d:\t%s", i, ev)
   425  	}
   426  }
   427  
   428  func getEventCallback(event *binlogdatapb.VEvent) func() {
   429  	s := fmt.Sprintf("%v", event)
   430  	for key, cb := range callbacks {
   431  		match := regexp.MustCompile(".*" + key + ".*")
   432  		if key == s || match.MatchString(s) {
   433  			return cb
   434  		}
   435  	}
   436  	return nil
   437  }
   438  
   439  func startVStreamCopy(ctx context.Context, t *testing.T, filter *binlogdatapb.Filter, tablePKs []*binlogdatapb.TableLastPK) {
   440  	pos := ""
   441  	go func() {
   442  		err := engine.Stream(ctx, pos, tablePKs, filter, func(evs []*binlogdatapb.VEvent) error {
   443  			//t.Logf("Received events: %v", evs)
   444  			muAllEvents.Lock()
   445  			defer muAllEvents.Unlock()
   446  			for _, ev := range evs {
   447  				if ev.Type == binlogdatapb.VEventType_HEARTBEAT {
   448  					continue
   449  				}
   450  				if ev.Throttled {
   451  					continue
   452  				}
   453  				cb := getEventCallback(ev)
   454  				if cb != nil {
   455  					cb()
   456  				}
   457  				allEvents = append(allEvents, ev)
   458  			}
   459  			return nil
   460  		})
   461  		require.Nil(t, err)
   462  	}()
   463  }
   464  
   465  var expectedEvents = []string{
   466  	"type:OTHER gtid:\"Copy Start t1\"",
   467  	"type:BEGIN",
   468  	"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   469  	"type:GTID",
   470  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"110\"}}}",
   471  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"220\"}}}",
   472  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"330\"}}}",
   473  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"440\"}}}",
   474  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"550\"}}}",
   475  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"660\"}}}",
   476  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"770\"}}}",
   477  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"880\"}}}",
   478  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:2 values:\"990\"}}}",
   479  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:2 lengths:3 values:\"10100\"}}}",
   480  	"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t1\" lastpk:{rows:{lengths:2 values:\"10\"}}}}",
   481  	"type:COMMIT",
   482  	"type:BEGIN",
   483  	"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t1\"} completed:true}",
   484  	"type:COMMIT",
   485  	"type:BEGIN",
   486  	"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   487  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:2 lengths:3 values:\"11110\"}}}",
   488  	"type:GTID",
   489  	"type:COMMIT", //insert for t2 done along with t1 does not generate an event since t2 is not yet copied
   490  	"type:OTHER gtid:\"Copy Start t2\"",
   491  	"type:BEGIN",
   492  	"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   493  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:2 lengths:3 values:\"12120\"}}}",
   494  	"type:GTID",
   495  	"type:COMMIT",
   496  	"type:BEGIN",
   497  	"type:FIELD field_event:{table_name:\"t2\" fields:{name:\"id21\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id21\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id22\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id22\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   498  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:2 values:\"120\"}}}",
   499  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:2 values:\"240\"}}}",
   500  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:2 values:\"360\"}}}",
   501  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:2 values:\"480\"}}}",
   502  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:3 values:\"5100\"}}}",
   503  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:3 values:\"6120\"}}}",
   504  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:3 values:\"7140\"}}}",
   505  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:3 values:\"8160\"}}}",
   506  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:1 lengths:3 values:\"9180\"}}}",
   507  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:2 lengths:3 values:\"10200\"}}}",
   508  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:2 lengths:3 values:\"11220\"}}}",
   509  	"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t2\" lastpk:{rows:{lengths:2 values:\"11\"}}}}",
   510  	"type:COMMIT",
   511  	"type:BEGIN",
   512  	"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t2\"} completed:true}",
   513  	"type:COMMIT",
   514  	"type:OTHER gtid:\"Copy Start t3\"",
   515  	"type:BEGIN",
   516  	"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   517  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:2 lengths:3 values:\"13130\"}}}",
   518  	"type:GTID",
   519  	"type:COMMIT",
   520  	"type:BEGIN",
   521  	"type:FIELD field_event:{table_name:\"t2\" fields:{name:\"id21\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id21\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id22\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id22\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   522  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:2 lengths:3 values:\"12240\"}}}",
   523  	"type:GTID",
   524  	"type:COMMIT",
   525  	"type:BEGIN",
   526  	"type:FIELD field_event:{table_name:\"t3\" fields:{name:\"id31\" type:INT32 table:\"t3\" org_table:\"t3\" database:\"vttest\" org_name:\"id31\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id32\" type:INT32 table:\"t3\" org_table:\"t3\" database:\"vttest\" org_name:\"id32\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   527  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:2 values:\"130\"}}}",
   528  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:2 values:\"260\"}}}",
   529  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:2 values:\"390\"}}}",
   530  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:3 values:\"4120\"}}}",
   531  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:3 values:\"5150\"}}}",
   532  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:3 values:\"6180\"}}}",
   533  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:3 values:\"7210\"}}}",
   534  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:3 values:\"8240\"}}}",
   535  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:1 lengths:3 values:\"9270\"}}}",
   536  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:2 lengths:3 values:\"10300\"}}}",
   537  	"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t3\" lastpk:{rows:{lengths:2 values:\"10\"}}}}",
   538  	"type:COMMIT",
   539  	"type:BEGIN",
   540  	"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t3\"} completed:true}",
   541  	"type:COMMIT",
   542  	"type:COPY_COMPLETED",
   543  	"type:BEGIN",
   544  	"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   545  	"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:2 lengths:3 values:\"14140\"}}}",
   546  	"type:GTID",
   547  	"type:COMMIT",
   548  	"type:BEGIN",
   549  	"type:FIELD field_event:{table_name:\"t2\" fields:{name:\"id21\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id21\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id22\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id22\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   550  	"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:2 lengths:3 values:\"13260\"}}}",
   551  	"type:GTID",
   552  	"type:COMMIT",
   553  	"type:BEGIN",
   554  	"type:FIELD field_event:{table_name:\"t3\" fields:{name:\"id31\" type:INT32 table:\"t3\" org_table:\"t3\" database:\"vttest\" org_name:\"id31\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id32\" type:INT32 table:\"t3\" org_table:\"t3\" database:\"vttest\" org_name:\"id32\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   555  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:2 lengths:3 values:\"12360\"}}}",
   556  	"type:ROW row_event:{table_name:\"t3\" row_changes:{after:{lengths:2 lengths:3 values:\"13390\"}}}",
   557  	"type:GTID",
   558  	"type:COMMIT",
   559  }