github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/processor/pipeline/sink_test.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package pipeline
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/pingcap/check"
    23  	"github.com/pingcap/ticdc/cdc/model"
    24  	"github.com/pingcap/ticdc/pkg/config"
    25  	cdcContext "github.com/pingcap/ticdc/pkg/context"
    26  	cerrors "github.com/pingcap/ticdc/pkg/errors"
    27  	"github.com/pingcap/ticdc/pkg/pipeline"
    28  	"github.com/pingcap/ticdc/pkg/util/testleak"
    29  	"github.com/pingcap/tidb/store/tikv/oracle"
    30  )
    31  
    32  func TestSuite(t *testing.T) {
    33  	check.TestingT(t)
    34  }
    35  
    36  type mockSink struct {
    37  	received []struct {
    38  		resolvedTs model.Ts
    39  		row        *model.RowChangedEvent
    40  	}
    41  }
    42  
    43  // mockFlowController is created because a real tableFlowController cannot be used
    44  // we are testing sinkNode by itself.
    45  type mockFlowController struct{}
    46  
    47  func (c *mockFlowController) Consume(commitTs uint64, size uint64, blockCallBack func() error) error {
    48  	return nil
    49  }
    50  
    51  func (c *mockFlowController) Release(resolvedTs uint64) {
    52  }
    53  
    54  func (c *mockFlowController) Abort() {
    55  }
    56  
    57  func (c *mockFlowController) GetConsumption() uint64 {
    58  	return 0
    59  }
    60  
    61  func (s *mockSink) Initialize(ctx context.Context, tableInfo []*model.SimpleTableInfo) error {
    62  	return nil
    63  }
    64  
    65  func (s *mockSink) EmitRowChangedEvents(ctx context.Context, rows ...*model.RowChangedEvent) error {
    66  	for _, row := range rows {
    67  		s.received = append(s.received, struct {
    68  			resolvedTs model.Ts
    69  			row        *model.RowChangedEvent
    70  		}{row: row})
    71  	}
    72  	return nil
    73  }
    74  
    75  func (s *mockSink) EmitDDLEvent(ctx context.Context, ddl *model.DDLEvent) error {
    76  	panic("unreachable")
    77  }
    78  
    79  func (s *mockSink) FlushRowChangedEvents(ctx context.Context, resolvedTs uint64) (uint64, error) {
    80  	s.received = append(s.received, struct {
    81  		resolvedTs model.Ts
    82  		row        *model.RowChangedEvent
    83  	}{resolvedTs: resolvedTs})
    84  	return resolvedTs, nil
    85  }
    86  
    87  func (s *mockSink) EmitCheckpointTs(ctx context.Context, ts uint64) error {
    88  	panic("unreachable")
    89  }
    90  
    91  func (s *mockSink) Close(ctx context.Context) error {
    92  	return nil
    93  }
    94  
    95  func (s *mockSink) Barrier(ctx context.Context) error {
    96  	return nil
    97  }
    98  
    99  func (s *mockSink) Check(c *check.C, expected []struct {
   100  	resolvedTs model.Ts
   101  	row        *model.RowChangedEvent
   102  }) {
   103  	c.Assert(s.received, check.DeepEquals, expected)
   104  }
   105  
   106  func (s *mockSink) Reset() {
   107  	s.received = s.received[:0]
   108  }
   109  
   110  type mockCloseControlSink struct {
   111  	mockSink
   112  	closeCh chan interface{}
   113  }
   114  
   115  func (s *mockCloseControlSink) Close(ctx context.Context) error {
   116  	select {
   117  	case <-ctx.Done():
   118  		return ctx.Err()
   119  	case <-s.closeCh:
   120  		return nil
   121  	}
   122  }
   123  
   124  type outputSuite struct{}
   125  
   126  var _ = check.Suite(&outputSuite{})
   127  
   128  func (s *outputSuite) TestStatus(c *check.C) {
   129  	defer testleak.AfterTest(c)()
   130  	ctx := cdcContext.NewContext(context.Background(), &cdcContext.GlobalVars{})
   131  	ctx = cdcContext.WithChangefeedVars(ctx, &cdcContext.ChangefeedVars{
   132  		ID: "changefeed-id-test-status",
   133  		Info: &model.ChangeFeedInfo{
   134  			StartTs: oracle.GoTimeToTS(time.Now()),
   135  			Config:  config.GetDefaultReplicaConfig(),
   136  		},
   137  	})
   138  
   139  	// test stop at targetTs
   140  	node := newSinkNode(&mockSink{}, 0, 10, &mockFlowController{})
   141  	c.Assert(node.Init(pipeline.MockNodeContext4Test(ctx, pipeline.Message{}, nil)), check.IsNil)
   142  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   143  
   144  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx, pipeline.BarrierMessage(20), nil)), check.IsNil)
   145  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   146  
   147  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   148  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 1, RawKV: &model.RawKVEntry{OpType: model.OpTypePut}, Row: &model.RowChangedEvent{}}), nil)), check.IsNil)
   149  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   150  
   151  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   152  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 2, RawKV: &model.RawKVEntry{OpType: model.OpTypePut}, Row: &model.RowChangedEvent{}}), nil)), check.IsNil)
   153  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   154  
   155  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   156  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 2, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil)), check.IsNil)
   157  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   158  
   159  	err := node.Receive(pipeline.MockNodeContext4Test(ctx,
   160  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 15, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil))
   161  	c.Assert(cerrors.ErrTableProcessorStoppedSafely.Equal(err), check.IsTrue)
   162  	c.Assert(node.Status(), check.Equals, TableStatusStopped)
   163  	c.Assert(node.CheckpointTs(), check.Equals, uint64(10))
   164  
   165  	// test the stop at ts command
   166  	node = newSinkNode(&mockSink{}, 0, 10, &mockFlowController{})
   167  	c.Assert(node.Init(pipeline.MockNodeContext4Test(ctx, pipeline.Message{}, nil)), check.IsNil)
   168  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   169  
   170  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx, pipeline.BarrierMessage(20), nil)), check.IsNil)
   171  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   172  
   173  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   174  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 2, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil)), check.IsNil)
   175  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   176  
   177  	err = node.Receive(pipeline.MockNodeContext4Test(ctx,
   178  		pipeline.CommandMessage(&pipeline.Command{Tp: pipeline.CommandTypeStop}), nil))
   179  	c.Assert(cerrors.ErrTableProcessorStoppedSafely.Equal(err), check.IsTrue)
   180  	c.Assert(node.Status(), check.Equals, TableStatusStopped)
   181  
   182  	err = node.Receive(pipeline.MockNodeContext4Test(ctx,
   183  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 7, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil))
   184  	c.Assert(cerrors.ErrTableProcessorStoppedSafely.Equal(err), check.IsTrue)
   185  	c.Assert(node.Status(), check.Equals, TableStatusStopped)
   186  	c.Assert(node.CheckpointTs(), check.Equals, uint64(2))
   187  
   188  	// test the stop at ts command is after then resolvedTs and checkpointTs is greater than stop ts
   189  	node = newSinkNode(&mockSink{}, 0, 10, &mockFlowController{})
   190  	c.Assert(node.Init(pipeline.MockNodeContext4Test(ctx, pipeline.Message{}, nil)), check.IsNil)
   191  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   192  
   193  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx, pipeline.BarrierMessage(20), nil)), check.IsNil)
   194  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   195  
   196  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   197  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 7, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil)), check.IsNil)
   198  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   199  
   200  	err = node.Receive(pipeline.MockNodeContext4Test(ctx,
   201  		pipeline.CommandMessage(&pipeline.Command{Tp: pipeline.CommandTypeStop}), nil))
   202  	c.Assert(cerrors.ErrTableProcessorStoppedSafely.Equal(err), check.IsTrue)
   203  	c.Assert(node.Status(), check.Equals, TableStatusStopped)
   204  
   205  	err = node.Receive(pipeline.MockNodeContext4Test(ctx,
   206  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 7, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil))
   207  	c.Assert(cerrors.ErrTableProcessorStoppedSafely.Equal(err), check.IsTrue)
   208  	c.Assert(node.Status(), check.Equals, TableStatusStopped)
   209  	c.Assert(node.CheckpointTs(), check.Equals, uint64(7))
   210  }
   211  
   212  // TestStopStatus tests the table status of a pipeline is not set to stopped
   213  // until the underlying sink is closed
   214  func (s *outputSuite) TestStopStatus(c *check.C) {
   215  	defer testleak.AfterTest(c)()
   216  	ctx := cdcContext.NewContext(context.Background(), &cdcContext.GlobalVars{})
   217  	ctx = cdcContext.WithChangefeedVars(ctx, &cdcContext.ChangefeedVars{
   218  		ID: "changefeed-id-test-status",
   219  		Info: &model.ChangeFeedInfo{
   220  			StartTs: oracle.GoTimeToTS(time.Now()),
   221  			Config:  config.GetDefaultReplicaConfig(),
   222  		},
   223  	})
   224  
   225  	closeCh := make(chan interface{}, 1)
   226  	node := newSinkNode(&mockCloseControlSink{mockSink: mockSink{}, closeCh: closeCh}, 0, 100, &mockFlowController{})
   227  	c.Assert(node.Init(pipeline.MockNodeContext4Test(ctx, pipeline.Message{}, nil)), check.IsNil)
   228  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   229  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   230  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 2, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil)), check.IsNil)
   231  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   232  
   233  	var wg sync.WaitGroup
   234  	wg.Add(1)
   235  	go func() {
   236  		defer wg.Done()
   237  		// This will block until sink Close returns
   238  		err := node.Receive(pipeline.MockNodeContext4Test(ctx,
   239  			pipeline.CommandMessage(&pipeline.Command{Tp: pipeline.CommandTypeStop}), nil))
   240  		c.Assert(cerrors.ErrTableProcessorStoppedSafely.Equal(err), check.IsTrue)
   241  		c.Assert(node.Status(), check.Equals, TableStatusStopped)
   242  	}()
   243  	// wait to ensure stop message is sent to the sink node
   244  	time.Sleep(time.Millisecond * 50)
   245  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   246  	closeCh <- struct{}{}
   247  	wg.Wait()
   248  }
   249  
   250  func (s *outputSuite) TestManyTs(c *check.C) {
   251  	defer testleak.AfterTest(c)()
   252  	ctx := cdcContext.NewContext(context.Background(), &cdcContext.GlobalVars{})
   253  	ctx = cdcContext.WithChangefeedVars(ctx, &cdcContext.ChangefeedVars{
   254  		ID: "changefeed-id-test-many-ts",
   255  		Info: &model.ChangeFeedInfo{
   256  			StartTs: oracle.GoTimeToTS(time.Now()),
   257  			Config:  config.GetDefaultReplicaConfig(),
   258  		},
   259  	})
   260  	sink := &mockSink{}
   261  	node := newSinkNode(sink, 0, 10, &mockFlowController{})
   262  	c.Assert(node.Init(pipeline.MockNodeContext4Test(ctx, pipeline.Message{}, nil)), check.IsNil)
   263  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   264  
   265  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   266  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 1, RawKV: &model.RawKVEntry{OpType: model.OpTypePut}, Row: &model.RowChangedEvent{CommitTs: 1}}), nil)), check.IsNil)
   267  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   268  
   269  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   270  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 2, RawKV: &model.RawKVEntry{OpType: model.OpTypePut}, Row: &model.RowChangedEvent{CommitTs: 2}}), nil)), check.IsNil)
   271  	c.Assert(node.Status(), check.Equals, TableStatusInitializing)
   272  
   273  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   274  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 2, RawKV: &model.RawKVEntry{OpType: model.OpTypeResolved}, Row: &model.RowChangedEvent{}}), nil)), check.IsNil)
   275  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   276  
   277  	sink.Check(c, nil)
   278  
   279  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx, pipeline.BarrierMessage(1), nil)), check.IsNil)
   280  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   281  
   282  	sink.Check(c, []struct {
   283  		resolvedTs model.Ts
   284  		row        *model.RowChangedEvent
   285  	}{
   286  		{row: &model.RowChangedEvent{CommitTs: 1}},
   287  		{row: &model.RowChangedEvent{CommitTs: 2}},
   288  		{resolvedTs: 1},
   289  	})
   290  	sink.Reset()
   291  	c.Assert(node.ResolvedTs(), check.Equals, uint64(2))
   292  	c.Assert(node.CheckpointTs(), check.Equals, uint64(1))
   293  
   294  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx, pipeline.BarrierMessage(5), nil)), check.IsNil)
   295  	c.Assert(node.Status(), check.Equals, TableStatusRunning)
   296  	sink.Check(c, []struct {
   297  		resolvedTs model.Ts
   298  		row        *model.RowChangedEvent
   299  	}{
   300  		{resolvedTs: 2},
   301  	})
   302  	sink.Reset()
   303  	c.Assert(node.ResolvedTs(), check.Equals, uint64(2))
   304  	c.Assert(node.CheckpointTs(), check.Equals, uint64(2))
   305  }
   306  
   307  func (s *outputSuite) TestSplitUpdateEventWhenEnableOldValue(c *check.C) {
   308  	defer testleak.AfterTest(c)()
   309  	ctx := cdcContext.NewContext(context.Background(), &cdcContext.GlobalVars{})
   310  	ctx = cdcContext.WithChangefeedVars(ctx, &cdcContext.ChangefeedVars{
   311  		ID: "changefeed-id-test-split-update-event",
   312  		Info: &model.ChangeFeedInfo{
   313  			StartTs: oracle.GoTimeToTS(time.Now()),
   314  			Config:  config.GetDefaultReplicaConfig(),
   315  		},
   316  	})
   317  	sink := &mockSink{}
   318  	node := newSinkNode(sink, 0, 10, &mockFlowController{})
   319  	c.Assert(node.Init(pipeline.MockNodeContext4Test(ctx, pipeline.Message{}, nil)), check.IsNil)
   320  
   321  	// nil row.
   322  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   323  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 1, RawKV: &model.RawKVEntry{OpType: model.OpTypePut}}), nil)), check.IsNil)
   324  	c.Assert(node.eventBuffer, check.HasLen, 0)
   325  
   326  	columns := []*model.Column{
   327  		{
   328  			Name:  "col1",
   329  			Flag:  model.BinaryFlag,
   330  			Value: "col1-value-updated",
   331  		},
   332  		{
   333  			Name:  "col2",
   334  			Flag:  model.HandleKeyFlag,
   335  			Value: "col2-value",
   336  		},
   337  	}
   338  	preColumns := []*model.Column{
   339  		{
   340  			Name:  "col1",
   341  			Flag:  model.BinaryFlag,
   342  			Value: "col1-value",
   343  		},
   344  		{
   345  			Name:  "col2",
   346  			Flag:  model.HandleKeyFlag,
   347  			Value: "col2-value",
   348  		},
   349  	}
   350  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(
   351  		ctx,
   352  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{
   353  			CRTs:  1,
   354  			RawKV: &model.RawKVEntry{OpType: model.OpTypePut},
   355  			Row:   &model.RowChangedEvent{CommitTs: 1, Columns: columns, PreColumns: preColumns},
   356  		}), nil)),
   357  		check.IsNil,
   358  	)
   359  	c.Assert(node.eventBuffer, check.HasLen, 1)
   360  	c.Assert(node.eventBuffer[0].Row.Columns, check.HasLen, 2)
   361  	c.Assert(node.eventBuffer[0].Row.PreColumns, check.HasLen, 2)
   362  }
   363  
   364  func (s *outputSuite) TestSplitUpdateEventWhenDisableOldValue(c *check.C) {
   365  	defer testleak.AfterTest(c)()
   366  	ctx := cdcContext.NewContext(context.Background(), &cdcContext.GlobalVars{})
   367  	cfg := config.GetDefaultReplicaConfig()
   368  	cfg.EnableOldValue = false
   369  	ctx = cdcContext.WithChangefeedVars(ctx, &cdcContext.ChangefeedVars{
   370  		ID: "changefeed-id-test-split-update-event",
   371  		Info: &model.ChangeFeedInfo{
   372  			StartTs: oracle.GoTimeToTS(time.Now()),
   373  			Config:  cfg,
   374  		},
   375  	})
   376  	sink := &mockSink{}
   377  	node := newSinkNode(sink, 0, 10, &mockFlowController{})
   378  	c.Assert(node.Init(pipeline.MockNodeContext4Test(ctx, pipeline.Message{}, nil)), check.IsNil)
   379  
   380  	// nil row.
   381  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(ctx,
   382  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{CRTs: 1, RawKV: &model.RawKVEntry{OpType: model.OpTypePut}}), nil)), check.IsNil)
   383  	c.Assert(node.eventBuffer, check.HasLen, 0)
   384  
   385  	// No update to the handle key column.
   386  	columns := []*model.Column{
   387  		{
   388  			Name:  "col1",
   389  			Flag:  model.BinaryFlag,
   390  			Value: "col1-value-updated",
   391  		},
   392  		{
   393  			Name:  "col2",
   394  			Flag:  model.HandleKeyFlag,
   395  			Value: "col2-value",
   396  		},
   397  	}
   398  	preColumns := []*model.Column{
   399  		{
   400  			Name:  "col1",
   401  			Flag:  model.BinaryFlag,
   402  			Value: "col1-value",
   403  		},
   404  		{
   405  			Name:  "col2",
   406  			Flag:  model.HandleKeyFlag,
   407  			Value: "col2-value",
   408  		},
   409  	}
   410  
   411  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(
   412  		ctx,
   413  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{
   414  			CRTs:  1,
   415  			RawKV: &model.RawKVEntry{OpType: model.OpTypePut},
   416  			Row:   &model.RowChangedEvent{CommitTs: 1, Columns: columns, PreColumns: preColumns},
   417  		}), nil)),
   418  		check.IsNil,
   419  	)
   420  	c.Assert(node.eventBuffer, check.HasLen, 1)
   421  	c.Assert(node.eventBuffer[0].Row.Columns, check.HasLen, 2)
   422  	c.Assert(node.eventBuffer[0].Row.PreColumns, check.HasLen, 0)
   423  
   424  	// Cleanup.
   425  	node.eventBuffer = []*model.PolymorphicEvent{}
   426  	// Update to the handle key column.
   427  	columns = []*model.Column{
   428  		{
   429  			Name:  "col1",
   430  			Flag:  model.BinaryFlag,
   431  			Value: "col1-value-updated",
   432  		},
   433  		{
   434  			Name:  "col2",
   435  			Flag:  model.HandleKeyFlag,
   436  			Value: "col2-value-updated",
   437  		},
   438  	}
   439  	preColumns = []*model.Column{
   440  		{
   441  			Name:  "col1",
   442  			Flag:  model.BinaryFlag,
   443  			Value: "col1-value",
   444  		},
   445  		{
   446  			Name:  "col2",
   447  			Flag:  model.HandleKeyFlag,
   448  			Value: "col2-value",
   449  		},
   450  	}
   451  
   452  	c.Assert(node.Receive(pipeline.MockNodeContext4Test(
   453  		ctx,
   454  		pipeline.PolymorphicEventMessage(&model.PolymorphicEvent{
   455  			CRTs:  1,
   456  			RawKV: &model.RawKVEntry{OpType: model.OpTypePut},
   457  			Row:   &model.RowChangedEvent{CommitTs: 1, Columns: columns, PreColumns: preColumns},
   458  		}), nil)),
   459  		check.IsNil,
   460  	)
   461  	// Split an update event into a delete and an insert event.
   462  	c.Assert(node.eventBuffer, check.HasLen, 2)
   463  
   464  	deleteEventIndex := 0
   465  	c.Assert(node.eventBuffer[deleteEventIndex].Row.Columns, check.HasLen, 0)
   466  	c.Assert(node.eventBuffer[deleteEventIndex].Row.PreColumns, check.HasLen, 2)
   467  	nonHandleKeyColIndex := 0
   468  	handleKeyColIndex := 1
   469  	// NOTICE: When old value disabled, we only keep the handle key pre cols.
   470  	c.Assert(node.eventBuffer[deleteEventIndex].Row.PreColumns[nonHandleKeyColIndex], check.IsNil)
   471  	c.Assert(node.eventBuffer[deleteEventIndex].Row.PreColumns[handleKeyColIndex].Name, check.Equals, "col2")
   472  	c.Assert(node.eventBuffer[deleteEventIndex].Row.PreColumns[handleKeyColIndex].Flag.IsHandleKey(), check.IsTrue)
   473  
   474  	insertEventIndex := 1
   475  	c.Assert(node.eventBuffer[insertEventIndex].Row.Columns, check.HasLen, 2)
   476  	c.Assert(node.eventBuffer[insertEventIndex].Row.PreColumns, check.HasLen, 0)
   477  }