github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/owner/owner_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 owner
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"time"
    20  
    21  	"github.com/pingcap/check"
    22  	"github.com/pingcap/ticdc/cdc/model"
    23  	"github.com/pingcap/ticdc/pkg/config"
    24  	cdcContext "github.com/pingcap/ticdc/pkg/context"
    25  	cerror "github.com/pingcap/ticdc/pkg/errors"
    26  	"github.com/pingcap/ticdc/pkg/etcd"
    27  	"github.com/pingcap/ticdc/pkg/orchestrator"
    28  	"github.com/pingcap/ticdc/pkg/util/testleak"
    29  	"github.com/pingcap/tidb/store/tikv/oracle"
    30  )
    31  
    32  var _ = check.Suite(&ownerSuite{})
    33  
    34  type ownerSuite struct {
    35  }
    36  
    37  type mockGcManager struct {
    38  	GcManager
    39  }
    40  
    41  func (m *mockGcManager) checkStaleCheckpointTs(ctx cdcContext.Context, checkpointTs model.Ts) error {
    42  	return cerror.ErrGCTTLExceeded.GenWithStackByArgs()
    43  }
    44  
    45  var _ GcManager = &mockGcManager{}
    46  
    47  func createOwner4Test(ctx cdcContext.Context, c *check.C) (*Owner, *model.GlobalReactorState, *orchestrator.ReactorStateTester) {
    48  	ctx.GlobalVars().PDClient = &mockPDClient{updateServiceGCSafePointFunc: func(ctx context.Context, serviceID string, ttl int64, safePoint uint64) (uint64, error) {
    49  		return safePoint, nil
    50  	}}
    51  	cf := NewOwner4Test(func(ctx cdcContext.Context, startTs uint64) (DDLPuller, error) {
    52  		return &mockDDLPuller{resolvedTs: startTs - 1}, nil
    53  	}, func(ctx cdcContext.Context) (AsyncSink, error) {
    54  		return &mockAsyncSink{}, nil
    55  	})
    56  	state := model.NewGlobalState().(*model.GlobalReactorState)
    57  	tester := orchestrator.NewReactorStateTester(c, state, nil)
    58  
    59  	// set captures
    60  	cdcKey := etcd.CDCKey{
    61  		Tp:        etcd.CDCKeyTypeCapture,
    62  		CaptureID: ctx.GlobalVars().CaptureInfo.ID,
    63  	}
    64  	captureBytes, err := ctx.GlobalVars().CaptureInfo.Marshal()
    65  	c.Assert(err, check.IsNil)
    66  	tester.MustUpdate(cdcKey.String(), captureBytes)
    67  	return cf, state, tester
    68  }
    69  
    70  func (s *ownerSuite) TestCreateRemoveChangefeed(c *check.C) {
    71  	defer testleak.AfterTest(c)()
    72  	ctx := cdcContext.NewBackendContext4Test(false)
    73  	owner, state, tester := createOwner4Test(ctx, c)
    74  	changefeedID := "test-changefeed"
    75  	changefeedInfo := &model.ChangeFeedInfo{
    76  		StartTs: oracle.GoTimeToTS(time.Now()),
    77  		Config:  config.GetDefaultReplicaConfig(),
    78  	}
    79  	changefeedStr, err := changefeedInfo.Marshal()
    80  	c.Assert(err, check.IsNil)
    81  	cdcKey := etcd.CDCKey{
    82  		Tp:           etcd.CDCKeyTypeChangefeedInfo,
    83  		ChangefeedID: changefeedID,
    84  	}
    85  	tester.MustUpdate(cdcKey.String(), []byte(changefeedStr))
    86  	_, err = owner.Tick(ctx, state)
    87  	tester.MustApplyPatches()
    88  	c.Assert(err, check.IsNil)
    89  	c.Assert(owner.changefeeds, check.HasKey, changefeedID)
    90  
    91  	// delete changefeed info key to remove changefeed
    92  	tester.MustUpdate(cdcKey.String(), nil)
    93  	// this tick to clean the leak info fo the removed changefeed
    94  	_, err = owner.Tick(ctx, state)
    95  	c.Assert(err, check.IsNil)
    96  	// this tick to remove the changefeed state in memory
    97  	tester.MustApplyPatches()
    98  	_, err = owner.Tick(ctx, state)
    99  	c.Assert(err, check.IsNil)
   100  	tester.MustApplyPatches()
   101  	c.Assert(err, check.IsNil)
   102  	c.Assert(owner.changefeeds, check.Not(check.HasKey), changefeedID)
   103  	c.Assert(state.Changefeeds, check.Not(check.HasKey), changefeedID)
   104  
   105  	tester.MustUpdate(cdcKey.String(), []byte(changefeedStr))
   106  	_, err = owner.Tick(ctx, state)
   107  	tester.MustApplyPatches()
   108  	c.Assert(err, check.IsNil)
   109  	c.Assert(owner.changefeeds, check.HasKey, changefeedID)
   110  
   111  	removeJob := model.AdminJob{
   112  		CfID:  changefeedID,
   113  		Type:  model.AdminRemove,
   114  		Opts:  &model.AdminJobOption{ForceRemove: true},
   115  		Error: nil,
   116  	}
   117  
   118  	// this will make changefeed always meet ErrGCTTLExceeded
   119  	mockedGcManager := &mockGcManager{GcManager: owner.gcManager}
   120  	owner.gcManager = mockedGcManager
   121  
   122  	// this tick create remove changefeed patches
   123  	owner.EnqueueJob(removeJob)
   124  	_, err = owner.Tick(ctx, state)
   125  	c.Assert(err, check.IsNil)
   126  
   127  	// apply patches and update owner's in memory changefeed states
   128  	tester.MustApplyPatches()
   129  	_, err = owner.Tick(ctx, state)
   130  	c.Assert(err, check.IsNil)
   131  	c.Assert(owner.changefeeds, check.Not(check.HasKey), changefeedID)
   132  }
   133  
   134  func (s *ownerSuite) TestStopChangefeed(c *check.C) {
   135  	defer testleak.AfterTest(c)()
   136  	ctx := cdcContext.NewBackendContext4Test(false)
   137  	owner, state, tester := createOwner4Test(ctx, c)
   138  	changefeedID := "test-changefeed"
   139  	changefeedInfo := &model.ChangeFeedInfo{
   140  		StartTs: oracle.GoTimeToTS(time.Now()),
   141  		Config:  config.GetDefaultReplicaConfig(),
   142  	}
   143  	changefeedStr, err := changefeedInfo.Marshal()
   144  	c.Assert(err, check.IsNil)
   145  	cdcKey := etcd.CDCKey{
   146  		Tp:           etcd.CDCKeyTypeChangefeedInfo,
   147  		ChangefeedID: changefeedID,
   148  	}
   149  	tester.MustUpdate(cdcKey.String(), []byte(changefeedStr))
   150  	_, err = owner.Tick(ctx, state)
   151  	tester.MustApplyPatches()
   152  	c.Assert(err, check.IsNil)
   153  	c.Assert(owner.changefeeds, check.HasKey, changefeedID)
   154  
   155  	// remove changefeed forcibly
   156  	owner.EnqueueJob(model.AdminJob{
   157  		CfID: changefeedID,
   158  		Type: model.AdminRemove,
   159  		Opts: &model.AdminJobOption{
   160  			ForceRemove: true,
   161  		},
   162  	})
   163  
   164  	// this tick to clean the leak info fo the removed changefeed
   165  	_, err = owner.Tick(ctx, state)
   166  	c.Assert(err, check.IsNil)
   167  	c.Assert(err, check.IsNil)
   168  	// this tick to remove the changefeed state in memory
   169  	tester.MustApplyPatches()
   170  	_, err = owner.Tick(ctx, state)
   171  	c.Assert(err, check.IsNil)
   172  	c.Assert(err, check.IsNil)
   173  	tester.MustApplyPatches()
   174  	c.Assert(err, check.IsNil)
   175  	c.Assert(owner.changefeeds, check.Not(check.HasKey), changefeedID)
   176  	c.Assert(state.Changefeeds, check.Not(check.HasKey), changefeedID)
   177  }
   178  
   179  func (s *ownerSuite) TestCheckClusterVersion(c *check.C) {
   180  	defer testleak.AfterTest(c)()
   181  	ctx := cdcContext.NewBackendContext4Test(false)
   182  	owner, state, tester := createOwner4Test(ctx, c)
   183  	tester.MustUpdate("/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", []byte(`{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300","version":"v6.0.0"}`))
   184  
   185  	changefeedID := "test-changefeed"
   186  	changefeedInfo := &model.ChangeFeedInfo{
   187  		StartTs: oracle.GoTimeToTS(time.Now()),
   188  		Config:  config.GetDefaultReplicaConfig(),
   189  	}
   190  	changefeedStr, err := changefeedInfo.Marshal()
   191  	c.Assert(err, check.IsNil)
   192  	cdcKey := etcd.CDCKey{
   193  		Tp:           etcd.CDCKeyTypeChangefeedInfo,
   194  		ChangefeedID: changefeedID,
   195  	}
   196  	tester.MustUpdate(cdcKey.String(), []byte(changefeedStr))
   197  
   198  	// check the tick is skipped and the changefeed will not be handled
   199  	_, err = owner.Tick(ctx, state)
   200  	tester.MustApplyPatches()
   201  	c.Assert(err, check.IsNil)
   202  	c.Assert(owner.changefeeds, check.Not(check.HasKey), changefeedID)
   203  
   204  	tester.MustUpdate("/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225",
   205  		[]byte(`{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300","version":"`+ctx.GlobalVars().CaptureInfo.Version+`"}`))
   206  
   207  	// check the tick is not skipped and the changefeed will be handled normally
   208  	_, err = owner.Tick(ctx, state)
   209  	tester.MustApplyPatches()
   210  	c.Assert(err, check.IsNil)
   211  	c.Assert(owner.changefeeds, check.HasKey, changefeedID)
   212  }
   213  
   214  func (s *ownerSuite) TestAdminJob(c *check.C) {
   215  	defer testleak.AfterTest(c)()
   216  	ctx := cdcContext.NewBackendContext4Test(false)
   217  	owner, _, _ := createOwner4Test(ctx, c)
   218  	owner.EnqueueJob(model.AdminJob{
   219  		CfID: "test-changefeed1",
   220  		Type: model.AdminResume,
   221  	})
   222  	owner.TriggerRebalance("test-changefeed2")
   223  	owner.ManualSchedule("test-changefeed3", "test-caputre1", 10)
   224  	var buf bytes.Buffer
   225  	owner.WriteDebugInfo(&buf)
   226  
   227  	// remove job.done, it's hard to check deep equals
   228  	jobs := owner.takeOwnerJobs()
   229  	for _, job := range jobs {
   230  		c.Assert(job.done, check.NotNil)
   231  		close(job.done)
   232  		job.done = nil
   233  	}
   234  	c.Assert(jobs, check.DeepEquals, []*ownerJob{
   235  		{
   236  			tp: ownerJobTypeAdminJob,
   237  			adminJob: &model.AdminJob{
   238  				CfID: "test-changefeed1",
   239  				Type: model.AdminResume,
   240  			},
   241  			changefeedID: "test-changefeed1",
   242  		}, {
   243  			tp:           ownerJobTypeRebalance,
   244  			changefeedID: "test-changefeed2",
   245  		}, {
   246  			tp:              ownerJobTypeManualSchedule,
   247  			changefeedID:    "test-changefeed3",
   248  			targetCaptureID: "test-caputre1",
   249  			tableID:         10,
   250  		}, {
   251  			tp:              ownerJobTypeDebugInfo,
   252  			debugInfoWriter: &buf,
   253  		},
   254  	})
   255  	c.Assert(owner.takeOwnerJobs(), check.HasLen, 0)
   256  }