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

     1  // Copyright 2021 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 processor
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"testing"
    20  
    21  	"github.com/pingcap/check"
    22  	"github.com/pingcap/errors"
    23  	"github.com/pingcap/log"
    24  	"github.com/pingcap/ticdc/cdc/model"
    25  	tablepipeline "github.com/pingcap/ticdc/cdc/processor/pipeline"
    26  	cdcContext "github.com/pingcap/ticdc/pkg/context"
    27  	cerror "github.com/pingcap/ticdc/pkg/errors"
    28  	"github.com/pingcap/ticdc/pkg/orchestrator"
    29  	"github.com/pingcap/ticdc/pkg/util/testleak"
    30  )
    31  
    32  func Test(t *testing.T) { check.TestingT(t) }
    33  
    34  type processorSuite struct{}
    35  
    36  var _ = check.Suite(&processorSuite{})
    37  
    38  func initProcessor4Test(ctx cdcContext.Context, c *check.C) (*processor, *orchestrator.ReactorStateTester) {
    39  	p := newProcessor4Test(ctx, func(ctx cdcContext.Context, tableID model.TableID, replicaInfo *model.TableReplicaInfo) (tablepipeline.TablePipeline, error) {
    40  		return &mockTablePipeline{
    41  			tableID:      tableID,
    42  			name:         fmt.Sprintf("`test`.`table%d`", tableID),
    43  			status:       tablepipeline.TableStatusRunning,
    44  			resolvedTs:   replicaInfo.StartTs,
    45  			checkpointTs: replicaInfo.StartTs,
    46  		}, nil
    47  	})
    48  	p.changefeed = model.NewChangefeedReactorState(ctx.ChangefeedVars().ID)
    49  	return p, orchestrator.NewReactorStateTester(c, p.changefeed, map[string]string{
    50  		"/tidb/cdc/capture/" + ctx.GlobalVars().CaptureInfo.ID:                                     `{"id":"` + ctx.GlobalVars().CaptureInfo.ID + `","address":"127.0.0.1:8300"}`,
    51  		"/tidb/cdc/changefeed/info/" + ctx.ChangefeedVars().ID:                                     `{"sink-uri":"blackhole://","opts":{},"create-time":"2020-02-02T00:00:00.000000+00:00","start-ts":0,"target-ts":0,"admin-job-type":0,"sort-engine":"memory","sort-dir":".","config":{"case-sensitive":true,"enable-old-value":false,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":null,"ddl-allow-list":null},"mounter":{"worker-num":16},"sink":{"dispatchers":null,"protocol":"default"},"cyclic-replication":{"enable":false,"replica-id":0,"filter-replica-ids":null,"id-buckets":0,"sync-ddl":false},"scheduler":{"type":"table-number","polling-time":-1}},"state":"normal","history":null,"error":null,"sync-point-enabled":false,"sync-point-interval":600000000000}`,
    52  		"/tidb/cdc/job/" + ctx.ChangefeedVars().ID:                                                 `{"resolved-ts":0,"checkpoint-ts":0,"admin-job-type":0}`,
    53  		"/tidb/cdc/task/status/" + ctx.GlobalVars().CaptureInfo.ID + "/" + ctx.ChangefeedVars().ID: `{"tables":{},"operation":null,"admin-job-type":0}`,
    54  	})
    55  }
    56  
    57  type mockTablePipeline struct {
    58  	tableID      model.TableID
    59  	name         string
    60  	resolvedTs   model.Ts
    61  	checkpointTs model.Ts
    62  	barrierTs    model.Ts
    63  	stopTs       model.Ts
    64  	status       tablepipeline.TableStatus
    65  	canceled     bool
    66  }
    67  
    68  func (m *mockTablePipeline) ID() (tableID int64, markTableID int64) {
    69  	return m.tableID, 0
    70  }
    71  
    72  func (m *mockTablePipeline) Name() string {
    73  	return m.name
    74  }
    75  
    76  func (m *mockTablePipeline) ResolvedTs() model.Ts {
    77  	return m.resolvedTs
    78  }
    79  
    80  func (m *mockTablePipeline) CheckpointTs() model.Ts {
    81  	return m.checkpointTs
    82  }
    83  
    84  func (m *mockTablePipeline) UpdateBarrierTs(ts model.Ts) {
    85  	m.barrierTs = ts
    86  }
    87  
    88  func (m *mockTablePipeline) AsyncStop(targetTs model.Ts) bool {
    89  	m.stopTs = targetTs
    90  	return true
    91  }
    92  
    93  func (m *mockTablePipeline) Workload() model.WorkloadInfo {
    94  	return model.WorkloadInfo{Workload: 1}
    95  }
    96  
    97  func (m *mockTablePipeline) Status() tablepipeline.TableStatus {
    98  	return m.status
    99  }
   100  
   101  func (m *mockTablePipeline) Cancel() {
   102  	if m.canceled {
   103  		log.Panic("cancel a canceled table pipeline")
   104  	}
   105  	m.canceled = true
   106  }
   107  
   108  func (m *mockTablePipeline) Wait() {
   109  	// do nothing
   110  }
   111  
   112  func (s *processorSuite) TestCheckTablesNum(c *check.C) {
   113  	defer testleak.AfterTest(c)()
   114  	ctx := cdcContext.NewBackendContext4Test(true)
   115  	p, tester := initProcessor4Test(ctx, c)
   116  	var err error
   117  	_, err = p.Tick(ctx, p.changefeed)
   118  	c.Assert(err, check.IsNil)
   119  	tester.MustApplyPatches()
   120  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals,
   121  		&model.TaskPosition{
   122  			CheckPointTs: 0,
   123  			ResolvedTs:   0,
   124  			Count:        0,
   125  			Error:        nil,
   126  		})
   127  
   128  	p, tester = initProcessor4Test(ctx, c)
   129  	p.changefeed.Info.StartTs = 66
   130  	p.changefeed.Status.CheckpointTs = 88
   131  	_, err = p.Tick(ctx, p.changefeed)
   132  	c.Assert(err, check.IsNil)
   133  	tester.MustApplyPatches()
   134  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals,
   135  		&model.TaskPosition{
   136  			CheckPointTs: 88,
   137  			ResolvedTs:   88,
   138  			Count:        0,
   139  			Error:        nil,
   140  		})
   141  }
   142  
   143  func (s *processorSuite) TestHandleTableOperation4SingleTable(c *check.C) {
   144  	defer testleak.AfterTest(c)()
   145  	ctx := cdcContext.NewBackendContext4Test(true)
   146  	p, tester := initProcessor4Test(ctx, c)
   147  	var err error
   148  	// init tick
   149  	_, err = p.Tick(ctx, p.changefeed)
   150  	c.Assert(err, check.IsNil)
   151  	tester.MustApplyPatches()
   152  	p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
   153  		status.CheckpointTs = 90
   154  		status.ResolvedTs = 100
   155  		return status, true, nil
   156  	})
   157  	p.changefeed.PatchTaskPosition(p.captureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) {
   158  		position.ResolvedTs = 100
   159  		return position, true, nil
   160  	})
   161  	tester.MustApplyPatches()
   162  
   163  	// no operation
   164  	_, err = p.Tick(ctx, p.changefeed)
   165  	c.Assert(err, check.IsNil)
   166  	tester.MustApplyPatches()
   167  
   168  	// add table, in processing
   169  	// in current implementation of owner, the startTs and BoundaryTs of add table operation should be always equaled.
   170  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   171  		status.AddTable(66, &model.TableReplicaInfo{StartTs: 60}, 60)
   172  		return status, true, nil
   173  	})
   174  	tester.MustApplyPatches()
   175  	_, err = p.Tick(ctx, p.changefeed)
   176  	c.Assert(err, check.IsNil)
   177  	tester.MustApplyPatches()
   178  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   179  		Tables: map[int64]*model.TableReplicaInfo{
   180  			66: {StartTs: 60},
   181  		},
   182  		Operation: map[int64]*model.TableOperation{
   183  			66: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed},
   184  		},
   185  	})
   186  
   187  	// add table, not finished
   188  	_, err = p.Tick(ctx, p.changefeed)
   189  	c.Assert(err, check.IsNil)
   190  	tester.MustApplyPatches()
   191  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   192  		Tables: map[int64]*model.TableReplicaInfo{
   193  			66: {StartTs: 60},
   194  		},
   195  		Operation: map[int64]*model.TableOperation{
   196  			66: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed},
   197  		},
   198  	})
   199  
   200  	// add table, push the resolvedTs
   201  	table66 := p.tables[66].(*mockTablePipeline)
   202  	table66.resolvedTs = 101
   203  	_, err = p.Tick(ctx, p.changefeed)
   204  	c.Assert(err, check.IsNil)
   205  	tester.MustApplyPatches()
   206  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   207  		Tables: map[int64]*model.TableReplicaInfo{
   208  			66: {StartTs: 60},
   209  		},
   210  		Operation: map[int64]*model.TableOperation{
   211  			66: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed},
   212  		},
   213  	})
   214  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].ResolvedTs, check.Equals, uint64(101))
   215  
   216  	// finish the operation
   217  	_, err = p.Tick(ctx, p.changefeed)
   218  	c.Assert(err, check.IsNil)
   219  	tester.MustApplyPatches()
   220  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   221  		Tables: map[int64]*model.TableReplicaInfo{
   222  			66: {StartTs: 60},
   223  		},
   224  		Operation: map[int64]*model.TableOperation{
   225  			66: {Delete: false, BoundaryTs: 60, Done: true, Status: model.OperFinished},
   226  		},
   227  	})
   228  
   229  	// clear finished operations
   230  	cleanUpFinishedOpOperation(p.changefeed, p.captureInfo.ID, tester)
   231  
   232  	// remove table, in processing
   233  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   234  		status.RemoveTable(66, 120, false)
   235  		return status, true, nil
   236  	})
   237  	tester.MustApplyPatches()
   238  	_, err = p.Tick(ctx, p.changefeed)
   239  	c.Assert(err, check.IsNil)
   240  	tester.MustApplyPatches()
   241  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   242  		Tables: map[int64]*model.TableReplicaInfo{},
   243  		Operation: map[int64]*model.TableOperation{
   244  			66: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed},
   245  		},
   246  	})
   247  	c.Assert(table66.stopTs, check.Equals, uint64(120))
   248  
   249  	// remove table, not finished
   250  	_, err = p.Tick(ctx, p.changefeed)
   251  	c.Assert(err, check.IsNil)
   252  	tester.MustApplyPatches()
   253  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   254  		Tables: map[int64]*model.TableReplicaInfo{},
   255  		Operation: map[int64]*model.TableOperation{
   256  			66: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed},
   257  		},
   258  	})
   259  
   260  	// remove table, finished
   261  	table66.status = tablepipeline.TableStatusStopped
   262  	table66.checkpointTs = 121
   263  	_, err = p.Tick(ctx, p.changefeed)
   264  	c.Assert(err, check.IsNil)
   265  	tester.MustApplyPatches()
   266  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   267  		Tables: map[int64]*model.TableReplicaInfo{},
   268  		Operation: map[int64]*model.TableOperation{
   269  			66: {Delete: true, BoundaryTs: 121, Done: true, Status: model.OperFinished},
   270  		},
   271  	})
   272  	c.Assert(table66.canceled, check.IsTrue)
   273  	c.Assert(p.tables[66], check.IsNil)
   274  }
   275  
   276  func (s *processorSuite) TestHandleTableOperation4MultiTable(c *check.C) {
   277  	defer testleak.AfterTest(c)()
   278  	ctx := cdcContext.NewBackendContext4Test(true)
   279  	p, tester := initProcessor4Test(ctx, c)
   280  	var err error
   281  	// init tick
   282  	_, err = p.Tick(ctx, p.changefeed)
   283  	c.Assert(err, check.IsNil)
   284  	tester.MustApplyPatches()
   285  	p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
   286  		status.CheckpointTs = 20
   287  		status.ResolvedTs = 20
   288  		return status, true, nil
   289  	})
   290  	p.changefeed.PatchTaskPosition(p.captureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) {
   291  		position.ResolvedTs = 100
   292  		position.CheckPointTs = 90
   293  		return position, true, nil
   294  	})
   295  	tester.MustApplyPatches()
   296  
   297  	// no operation
   298  	_, err = p.Tick(ctx, p.changefeed)
   299  	c.Assert(err, check.IsNil)
   300  	tester.MustApplyPatches()
   301  
   302  	// add table, in processing
   303  	// in current implementation of owner, the startTs and BoundaryTs of add table operation should be always equaled.
   304  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   305  		status.AddTable(1, &model.TableReplicaInfo{StartTs: 60}, 60)
   306  		status.AddTable(2, &model.TableReplicaInfo{StartTs: 50}, 50)
   307  		status.AddTable(3, &model.TableReplicaInfo{StartTs: 40}, 40)
   308  		status.Tables[4] = &model.TableReplicaInfo{StartTs: 30}
   309  		return status, true, nil
   310  	})
   311  	tester.MustApplyPatches()
   312  	_, err = p.Tick(ctx, p.changefeed)
   313  	c.Assert(err, check.IsNil)
   314  	tester.MustApplyPatches()
   315  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   316  		Tables: map[int64]*model.TableReplicaInfo{
   317  			1: {StartTs: 60},
   318  			2: {StartTs: 50},
   319  			3: {StartTs: 40},
   320  			4: {StartTs: 30},
   321  		},
   322  		Operation: map[int64]*model.TableOperation{
   323  			1: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed},
   324  			2: {Delete: false, BoundaryTs: 50, Done: false, Status: model.OperProcessed},
   325  			3: {Delete: false, BoundaryTs: 40, Done: false, Status: model.OperProcessed},
   326  		},
   327  	})
   328  	c.Assert(p.tables, check.HasLen, 4)
   329  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].CheckPointTs, check.Equals, uint64(30))
   330  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].ResolvedTs, check.Equals, uint64(30))
   331  
   332  	// add table, push the resolvedTs, finished add table
   333  	table1 := p.tables[1].(*mockTablePipeline)
   334  	table2 := p.tables[2].(*mockTablePipeline)
   335  	table3 := p.tables[3].(*mockTablePipeline)
   336  	table4 := p.tables[4].(*mockTablePipeline)
   337  	table1.resolvedTs = 101
   338  	table2.resolvedTs = 101
   339  	table3.resolvedTs = 102
   340  	table4.resolvedTs = 103
   341  	// removed table 3
   342  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   343  		status.RemoveTable(3, 60, false)
   344  		return status, true, nil
   345  	})
   346  	tester.MustApplyPatches()
   347  	_, err = p.Tick(ctx, p.changefeed)
   348  	c.Assert(err, check.IsNil)
   349  	tester.MustApplyPatches()
   350  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   351  		Tables: map[int64]*model.TableReplicaInfo{
   352  			1: {StartTs: 60},
   353  			2: {StartTs: 50},
   354  			4: {StartTs: 30},
   355  		},
   356  		Operation: map[int64]*model.TableOperation{
   357  			1: {Delete: false, BoundaryTs: 60, Done: true, Status: model.OperFinished},
   358  			2: {Delete: false, BoundaryTs: 50, Done: true, Status: model.OperFinished},
   359  			3: {Delete: true, BoundaryTs: 60, Done: false, Status: model.OperProcessed},
   360  		},
   361  	})
   362  	c.Assert(p.tables, check.HasLen, 4)
   363  	c.Assert(table3.canceled, check.IsFalse)
   364  	c.Assert(table3.stopTs, check.Equals, uint64(60))
   365  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].ResolvedTs, check.Equals, uint64(101))
   366  
   367  	// finish remove operations
   368  	table3.status = tablepipeline.TableStatusStopped
   369  	table3.checkpointTs = 65
   370  	_, err = p.Tick(ctx, p.changefeed)
   371  	c.Assert(err, check.IsNil)
   372  	tester.MustApplyPatches()
   373  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   374  		Tables: map[int64]*model.TableReplicaInfo{
   375  			1: {StartTs: 60},
   376  			2: {StartTs: 50},
   377  			4: {StartTs: 30},
   378  		},
   379  		Operation: map[int64]*model.TableOperation{
   380  			1: {Delete: false, BoundaryTs: 60, Done: true, Status: model.OperFinished},
   381  			2: {Delete: false, BoundaryTs: 50, Done: true, Status: model.OperFinished},
   382  			3: {Delete: true, BoundaryTs: 65, Done: true, Status: model.OperFinished},
   383  		},
   384  	})
   385  	c.Assert(p.tables, check.HasLen, 3)
   386  	c.Assert(table3.canceled, check.IsTrue)
   387  
   388  	// clear finished operations
   389  	cleanUpFinishedOpOperation(p.changefeed, p.captureInfo.ID, tester)
   390  
   391  	// remove table, in processing
   392  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   393  		status.RemoveTable(1, 120, false)
   394  		status.RemoveTable(4, 120, false)
   395  		delete(status.Tables, 2)
   396  		return status, true, nil
   397  	})
   398  	tester.MustApplyPatches()
   399  	_, err = p.Tick(ctx, p.changefeed)
   400  	c.Assert(err, check.IsNil)
   401  	tester.MustApplyPatches()
   402  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   403  		Tables: map[int64]*model.TableReplicaInfo{},
   404  		Operation: map[int64]*model.TableOperation{
   405  			1: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed},
   406  			4: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed},
   407  		},
   408  	})
   409  	c.Assert(table1.stopTs, check.Equals, uint64(120))
   410  	c.Assert(table4.stopTs, check.Equals, uint64(120))
   411  	c.Assert(table2.canceled, check.IsTrue)
   412  	c.Assert(p.tables, check.HasLen, 2)
   413  
   414  	// remove table, not finished
   415  	_, err = p.Tick(ctx, p.changefeed)
   416  	c.Assert(err, check.IsNil)
   417  	tester.MustApplyPatches()
   418  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   419  		Tables: map[int64]*model.TableReplicaInfo{},
   420  		Operation: map[int64]*model.TableOperation{
   421  			1: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed},
   422  			4: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed},
   423  		},
   424  	})
   425  
   426  	// remove table, finished
   427  	table1.status = tablepipeline.TableStatusStopped
   428  	table1.checkpointTs = 121
   429  	table4.status = tablepipeline.TableStatusStopped
   430  	table4.checkpointTs = 122
   431  	_, err = p.Tick(ctx, p.changefeed)
   432  	c.Assert(err, check.IsNil)
   433  	tester.MustApplyPatches()
   434  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   435  		Tables: map[int64]*model.TableReplicaInfo{},
   436  		Operation: map[int64]*model.TableOperation{
   437  			1: {Delete: true, BoundaryTs: 121, Done: true, Status: model.OperFinished},
   438  			4: {Delete: true, BoundaryTs: 122, Done: true, Status: model.OperFinished},
   439  		},
   440  	})
   441  	c.Assert(table1.canceled, check.IsTrue)
   442  	c.Assert(table4.canceled, check.IsTrue)
   443  	c.Assert(p.tables, check.HasLen, 0)
   444  }
   445  
   446  func (s *processorSuite) TestInitTable(c *check.C) {
   447  	defer testleak.AfterTest(c)()
   448  	ctx := cdcContext.NewBackendContext4Test(true)
   449  	p, tester := initProcessor4Test(ctx, c)
   450  	var err error
   451  	// init tick
   452  	_, err = p.Tick(ctx, p.changefeed)
   453  	c.Assert(err, check.IsNil)
   454  	tester.MustApplyPatches()
   455  
   456  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   457  		status.Tables[1] = &model.TableReplicaInfo{StartTs: 20}
   458  		status.Tables[2] = &model.TableReplicaInfo{StartTs: 30}
   459  		return status, true, nil
   460  	})
   461  	tester.MustApplyPatches()
   462  	_, err = p.Tick(ctx, p.changefeed)
   463  	c.Assert(err, check.IsNil)
   464  	tester.MustApplyPatches()
   465  	c.Assert(p.tables[1], check.Not(check.IsNil))
   466  	c.Assert(p.tables[2], check.Not(check.IsNil))
   467  }
   468  
   469  func (s *processorSuite) TestProcessorError(c *check.C) {
   470  	defer testleak.AfterTest(c)()
   471  	ctx := cdcContext.NewBackendContext4Test(true)
   472  	p, tester := initProcessor4Test(ctx, c)
   473  	var err error
   474  	// init tick
   475  	_, err = p.Tick(ctx, p.changefeed)
   476  	c.Assert(err, check.IsNil)
   477  	tester.MustApplyPatches()
   478  
   479  	// send a abnormal error
   480  	p.sendError(cerror.ErrSinkURIInvalid)
   481  	_, err = p.Tick(ctx, p.changefeed)
   482  	tester.MustApplyPatches()
   483  	c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue)
   484  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{
   485  		Error: &model.RunningError{
   486  			Addr:    "127.0.0.1:0000",
   487  			Code:    "CDC:ErrSinkURIInvalid",
   488  			Message: "[CDC:ErrSinkURIInvalid]sink uri invalid",
   489  		},
   490  	})
   491  
   492  	p, tester = initProcessor4Test(ctx, c)
   493  	// init tick
   494  	_, err = p.Tick(ctx, p.changefeed)
   495  	c.Assert(err, check.IsNil)
   496  	tester.MustApplyPatches()
   497  
   498  	// send a normal error
   499  	p.sendError(context.Canceled)
   500  	_, err = p.Tick(ctx, p.changefeed)
   501  	tester.MustApplyPatches()
   502  	c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue)
   503  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{
   504  		Error: nil,
   505  	})
   506  }
   507  
   508  func (s *processorSuite) TestProcessorExit(c *check.C) {
   509  	defer testleak.AfterTest(c)()
   510  	ctx := cdcContext.NewBackendContext4Test(true)
   511  	p, tester := initProcessor4Test(ctx, c)
   512  	var err error
   513  	// init tick
   514  	_, err = p.Tick(ctx, p.changefeed)
   515  	c.Assert(err, check.IsNil)
   516  	tester.MustApplyPatches()
   517  
   518  	// stop the changefeed
   519  	p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
   520  		status.AdminJobType = model.AdminStop
   521  		return status, true, nil
   522  	})
   523  	p.changefeed.PatchTaskStatus(ctx.GlobalVars().CaptureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   524  		status.AdminJobType = model.AdminStop
   525  		return status, true, nil
   526  	})
   527  	tester.MustApplyPatches()
   528  	_, err = p.Tick(ctx, p.changefeed)
   529  	c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue)
   530  	tester.MustApplyPatches()
   531  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{
   532  		Error: nil,
   533  	})
   534  }
   535  
   536  func (s *processorSuite) TestProcessorClose(c *check.C) {
   537  	defer testleak.AfterTest(c)()
   538  	ctx := cdcContext.NewBackendContext4Test(true)
   539  	p, tester := initProcessor4Test(ctx, c)
   540  	var err error
   541  	// init tick
   542  	_, err = p.Tick(ctx, p.changefeed)
   543  	c.Assert(err, check.IsNil)
   544  	tester.MustApplyPatches()
   545  
   546  	// add tables
   547  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   548  		status.Tables[1] = &model.TableReplicaInfo{StartTs: 20}
   549  		status.Tables[2] = &model.TableReplicaInfo{StartTs: 30}
   550  		return status, true, nil
   551  	})
   552  	tester.MustApplyPatches()
   553  	_, err = p.Tick(ctx, p.changefeed)
   554  	c.Assert(err, check.IsNil)
   555  	tester.MustApplyPatches()
   556  
   557  	// push the resolvedTs and checkpointTs
   558  	p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
   559  		status.ResolvedTs = 100
   560  		return status, true, nil
   561  	})
   562  	tester.MustApplyPatches()
   563  	p.tables[1].(*mockTablePipeline).resolvedTs = 110
   564  	p.tables[2].(*mockTablePipeline).resolvedTs = 90
   565  	p.tables[1].(*mockTablePipeline).checkpointTs = 90
   566  	p.tables[2].(*mockTablePipeline).checkpointTs = 95
   567  	_, err = p.Tick(ctx, p.changefeed)
   568  	c.Assert(err, check.IsNil)
   569  	tester.MustApplyPatches()
   570  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{
   571  		CheckPointTs: 90,
   572  		ResolvedTs:   90,
   573  		Error:        nil,
   574  	})
   575  	c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{
   576  		Tables: map[int64]*model.TableReplicaInfo{1: {StartTs: 20}, 2: {StartTs: 30}},
   577  	})
   578  	c.Assert(p.changefeed.Workloads[p.captureInfo.ID], check.DeepEquals, model.TaskWorkload{1: {Workload: 1}, 2: {Workload: 1}})
   579  
   580  	c.Assert(p.Close(), check.IsNil)
   581  	tester.MustApplyPatches()
   582  	c.Assert(p.tables[1].(*mockTablePipeline).canceled, check.IsTrue)
   583  	c.Assert(p.tables[2].(*mockTablePipeline).canceled, check.IsTrue)
   584  
   585  	p, tester = initProcessor4Test(ctx, c)
   586  	// init tick
   587  	_, err = p.Tick(ctx, p.changefeed)
   588  	c.Assert(err, check.IsNil)
   589  	tester.MustApplyPatches()
   590  
   591  	// add tables
   592  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   593  		status.Tables[1] = &model.TableReplicaInfo{StartTs: 20}
   594  		status.Tables[2] = &model.TableReplicaInfo{StartTs: 30}
   595  		return status, true, nil
   596  	})
   597  	tester.MustApplyPatches()
   598  	_, err = p.Tick(ctx, p.changefeed)
   599  	c.Assert(err, check.IsNil)
   600  	tester.MustApplyPatches()
   601  
   602  	// send error
   603  	p.sendError(cerror.ErrSinkURIInvalid)
   604  	_, err = p.Tick(ctx, p.changefeed)
   605  	c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue)
   606  	tester.MustApplyPatches()
   607  
   608  	c.Assert(p.Close(), check.IsNil)
   609  	tester.MustApplyPatches()
   610  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].Error, check.DeepEquals, &model.RunningError{
   611  		Addr:    "127.0.0.1:0000",
   612  		Code:    "CDC:ErrSinkURIInvalid",
   613  		Message: "[CDC:ErrSinkURIInvalid]sink uri invalid",
   614  	})
   615  	c.Assert(p.tables[1].(*mockTablePipeline).canceled, check.IsTrue)
   616  	c.Assert(p.tables[2].(*mockTablePipeline).canceled, check.IsTrue)
   617  }
   618  
   619  func (s *processorSuite) TestPositionDeleted(c *check.C) {
   620  	defer testleak.AfterTest(c)()
   621  	ctx := cdcContext.NewBackendContext4Test(true)
   622  	p, tester := initProcessor4Test(ctx, c)
   623  	p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   624  		status.Tables[1] = &model.TableReplicaInfo{StartTs: 30}
   625  		status.Tables[2] = &model.TableReplicaInfo{StartTs: 40}
   626  		return status, true, nil
   627  	})
   628  	var err error
   629  	// init tick
   630  	_, err = p.Tick(ctx, p.changefeed)
   631  	c.Assert(err, check.IsNil)
   632  	tester.MustApplyPatches()
   633  
   634  	// cal position
   635  	_, err = p.Tick(ctx, p.changefeed)
   636  	c.Assert(err, check.IsNil)
   637  	tester.MustApplyPatches()
   638  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{
   639  		CheckPointTs: 30,
   640  		ResolvedTs:   30,
   641  	})
   642  
   643  	// some other delete the task position
   644  	p.changefeed.PatchTaskPosition(p.captureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) {
   645  		return nil, true, nil
   646  	})
   647  	tester.MustApplyPatches()
   648  	// position created again
   649  	_, err = p.Tick(ctx, p.changefeed)
   650  	c.Assert(err, check.IsNil)
   651  	tester.MustApplyPatches()
   652  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{
   653  		CheckPointTs: 0,
   654  		ResolvedTs:   0,
   655  	})
   656  
   657  	// cal position
   658  	_, err = p.Tick(ctx, p.changefeed)
   659  	c.Assert(err, check.IsNil)
   660  	tester.MustApplyPatches()
   661  	c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{
   662  		CheckPointTs: 30,
   663  		ResolvedTs:   30,
   664  	})
   665  }
   666  
   667  func cleanUpFinishedOpOperation(state *model.ChangefeedReactorState, captureID model.CaptureID, tester *orchestrator.ReactorStateTester) {
   668  	state.PatchTaskStatus(captureID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   669  		if status == nil || status.Operation == nil {
   670  			return status, false, nil
   671  		}
   672  		for tableID, opt := range status.Operation {
   673  			if opt.Done && opt.Status == model.OperFinished {
   674  				delete(status.Operation, tableID)
   675  			}
   676  		}
   677  		return status, true, nil
   678  	})
   679  	tester.MustApplyPatches()
   680  }