github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/owner/feed_state_manager_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  	"github.com/pingcap/check"
    18  	"github.com/pingcap/ticdc/cdc/model"
    19  	"github.com/pingcap/ticdc/pkg/config"
    20  	cdcContext "github.com/pingcap/ticdc/pkg/context"
    21  	"github.com/pingcap/ticdc/pkg/orchestrator"
    22  	"github.com/pingcap/ticdc/pkg/util/testleak"
    23  )
    24  
    25  var _ = check.Suite(&feedStateManagerSuite{})
    26  
    27  type feedStateManagerSuite struct {
    28  }
    29  
    30  func (s *feedStateManagerSuite) TestHandleJob(c *check.C) {
    31  	defer testleak.AfterTest(c)()
    32  	ctx := cdcContext.NewBackendContext4Test(true)
    33  	manager := new(feedStateManager)
    34  	state := model.NewChangefeedReactorState(ctx.ChangefeedVars().ID)
    35  	tester := orchestrator.NewReactorStateTester(c, state, nil)
    36  	state.PatchInfo(func(info *model.ChangeFeedInfo) (*model.ChangeFeedInfo, bool, error) {
    37  		c.Assert(info, check.IsNil)
    38  		return &model.ChangeFeedInfo{SinkURI: "123", Config: &config.ReplicaConfig{}}, true, nil
    39  	})
    40  	state.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
    41  		c.Assert(status, check.IsNil)
    42  		return &model.ChangeFeedStatus{}, true, nil
    43  	})
    44  	tester.MustApplyPatches()
    45  	manager.Tick(state)
    46  	tester.MustApplyPatches()
    47  	c.Assert(manager.ShouldRunning(), check.IsTrue)
    48  
    49  	// an admin job which of changefeed is not match
    50  	manager.PushAdminJob(&model.AdminJob{
    51  		CfID: "fake-changefeed-id",
    52  		Type: model.AdminStop,
    53  	})
    54  	manager.Tick(state)
    55  	tester.MustApplyPatches()
    56  	c.Assert(manager.ShouldRunning(), check.IsTrue)
    57  
    58  	// a running can not be resume
    59  	manager.PushAdminJob(&model.AdminJob{
    60  		CfID: ctx.ChangefeedVars().ID,
    61  		Type: model.AdminResume,
    62  	})
    63  	manager.Tick(state)
    64  	tester.MustApplyPatches()
    65  	c.Assert(manager.ShouldRunning(), check.IsTrue)
    66  
    67  	// stop a changefeed
    68  	manager.PushAdminJob(&model.AdminJob{
    69  		CfID: ctx.ChangefeedVars().ID,
    70  		Type: model.AdminStop,
    71  	})
    72  	manager.Tick(state)
    73  	tester.MustApplyPatches()
    74  	c.Assert(manager.ShouldRunning(), check.IsFalse)
    75  	c.Assert(state.Info.State, check.Equals, model.StateStopped)
    76  	c.Assert(state.Info.AdminJobType, check.Equals, model.AdminStop)
    77  	c.Assert(state.Status.AdminJobType, check.Equals, model.AdminStop)
    78  
    79  	// resume a changefeed
    80  	manager.PushAdminJob(&model.AdminJob{
    81  		CfID: ctx.ChangefeedVars().ID,
    82  		Type: model.AdminResume,
    83  	})
    84  	manager.Tick(state)
    85  	tester.MustApplyPatches()
    86  	c.Assert(manager.ShouldRunning(), check.IsTrue)
    87  	c.Assert(state.Info.State, check.Equals, model.StateNormal)
    88  	c.Assert(state.Info.AdminJobType, check.Equals, model.AdminNone)
    89  	c.Assert(state.Status.AdminJobType, check.Equals, model.AdminNone)
    90  
    91  	// remove a changefeed
    92  	manager.PushAdminJob(&model.AdminJob{
    93  		CfID: ctx.ChangefeedVars().ID,
    94  		Type: model.AdminRemove,
    95  	})
    96  	manager.Tick(state)
    97  	tester.MustApplyPatches()
    98  	c.Assert(manager.ShouldRunning(), check.IsFalse)
    99  	c.Assert(state.Exist(), check.IsFalse)
   100  }
   101  
   102  func (s *feedStateManagerSuite) TestMarkFinished(c *check.C) {
   103  	defer testleak.AfterTest(c)()
   104  	ctx := cdcContext.NewBackendContext4Test(true)
   105  	manager := new(feedStateManager)
   106  	state := model.NewChangefeedReactorState(ctx.ChangefeedVars().ID)
   107  	tester := orchestrator.NewReactorStateTester(c, state, nil)
   108  	state.PatchInfo(func(info *model.ChangeFeedInfo) (*model.ChangeFeedInfo, bool, error) {
   109  		c.Assert(info, check.IsNil)
   110  		return &model.ChangeFeedInfo{SinkURI: "123", Config: &config.ReplicaConfig{}}, true, nil
   111  	})
   112  	state.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
   113  		c.Assert(status, check.IsNil)
   114  		return &model.ChangeFeedStatus{}, true, nil
   115  	})
   116  	tester.MustApplyPatches()
   117  	manager.Tick(state)
   118  	tester.MustApplyPatches()
   119  	c.Assert(manager.ShouldRunning(), check.IsTrue)
   120  
   121  	manager.MarkFinished()
   122  	manager.Tick(state)
   123  	tester.MustApplyPatches()
   124  	c.Assert(manager.ShouldRunning(), check.IsFalse)
   125  	c.Assert(state.Info.State, check.Equals, model.StateFinished)
   126  	c.Assert(state.Info.AdminJobType, check.Equals, model.AdminFinish)
   127  	c.Assert(state.Status.AdminJobType, check.Equals, model.AdminFinish)
   128  }
   129  
   130  func (s *feedStateManagerSuite) TestCleanUpInfos(c *check.C) {
   131  	defer testleak.AfterTest(c)()
   132  	ctx := cdcContext.NewBackendContext4Test(true)
   133  	manager := new(feedStateManager)
   134  	state := model.NewChangefeedReactorState(ctx.ChangefeedVars().ID)
   135  	tester := orchestrator.NewReactorStateTester(c, state, nil)
   136  	state.PatchInfo(func(info *model.ChangeFeedInfo) (*model.ChangeFeedInfo, bool, error) {
   137  		c.Assert(info, check.IsNil)
   138  		return &model.ChangeFeedInfo{SinkURI: "123", Config: &config.ReplicaConfig{}}, true, nil
   139  	})
   140  	state.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
   141  		c.Assert(status, check.IsNil)
   142  		return &model.ChangeFeedStatus{}, true, nil
   143  	})
   144  	state.PatchTaskStatus(ctx.GlobalVars().CaptureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   145  		return &model.TaskStatus{}, true, nil
   146  	})
   147  	state.PatchTaskPosition(ctx.GlobalVars().CaptureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) {
   148  		return &model.TaskPosition{}, true, nil
   149  	})
   150  	state.PatchTaskWorkload(ctx.GlobalVars().CaptureInfo.ID, func(workload model.TaskWorkload) (model.TaskWorkload, bool, error) {
   151  		return model.TaskWorkload{}, true, nil
   152  	})
   153  	tester.MustApplyPatches()
   154  	c.Assert(state.TaskStatuses, check.HasKey, ctx.GlobalVars().CaptureInfo.ID)
   155  	c.Assert(state.TaskPositions, check.HasKey, ctx.GlobalVars().CaptureInfo.ID)
   156  	c.Assert(state.Workloads, check.HasKey, ctx.GlobalVars().CaptureInfo.ID)
   157  	manager.Tick(state)
   158  	tester.MustApplyPatches()
   159  	c.Assert(manager.ShouldRunning(), check.IsTrue)
   160  
   161  	manager.MarkFinished()
   162  	manager.Tick(state)
   163  	tester.MustApplyPatches()
   164  	c.Assert(manager.ShouldRunning(), check.IsFalse)
   165  	c.Assert(state.Info.State, check.Equals, model.StateFinished)
   166  	c.Assert(state.Info.AdminJobType, check.Equals, model.AdminFinish)
   167  	c.Assert(state.Status.AdminJobType, check.Equals, model.AdminFinish)
   168  	c.Assert(state.TaskStatuses, check.Not(check.HasKey), ctx.GlobalVars().CaptureInfo.ID)
   169  	c.Assert(state.TaskPositions, check.Not(check.HasKey), ctx.GlobalVars().CaptureInfo.ID)
   170  	c.Assert(state.Workloads, check.Not(check.HasKey), ctx.GlobalVars().CaptureInfo.ID)
   171  }
   172  
   173  func (s *feedStateManagerSuite) TestHandleError(c *check.C) {
   174  	defer testleak.AfterTest(c)()
   175  	ctx := cdcContext.NewBackendContext4Test(true)
   176  	manager := new(feedStateManager)
   177  	state := model.NewChangefeedReactorState(ctx.ChangefeedVars().ID)
   178  	tester := orchestrator.NewReactorStateTester(c, state, nil)
   179  	state.PatchInfo(func(info *model.ChangeFeedInfo) (*model.ChangeFeedInfo, bool, error) {
   180  		c.Assert(info, check.IsNil)
   181  		return &model.ChangeFeedInfo{SinkURI: "123", Config: &config.ReplicaConfig{}}, true, nil
   182  	})
   183  	state.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) {
   184  		c.Assert(status, check.IsNil)
   185  		return &model.ChangeFeedStatus{}, true, nil
   186  	})
   187  	state.PatchTaskStatus(ctx.GlobalVars().CaptureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) {
   188  		return &model.TaskStatus{}, true, nil
   189  	})
   190  	state.PatchTaskPosition(ctx.GlobalVars().CaptureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) {
   191  		return &model.TaskPosition{Error: &model.RunningError{
   192  			Addr:    ctx.GlobalVars().CaptureInfo.AdvertiseAddr,
   193  			Code:    "[CDC:ErrEtcdSessionDone]",
   194  			Message: "fake error for test",
   195  		}}, true, nil
   196  	})
   197  	state.PatchTaskWorkload(ctx.GlobalVars().CaptureInfo.ID, func(workload model.TaskWorkload) (model.TaskWorkload, bool, error) {
   198  		return model.TaskWorkload{}, true, nil
   199  	})
   200  	tester.MustApplyPatches()
   201  	manager.Tick(state)
   202  	tester.MustApplyPatches()
   203  	c.Assert(manager.ShouldRunning(), check.IsTrue)
   204  	// error reported by processor in task position should be cleaned
   205  	c.Assert(state.TaskPositions[ctx.GlobalVars().CaptureInfo.ID].Error, check.IsNil)
   206  
   207  	// throw error more than history threshold to turn feed state into error
   208  	for i := 0; i < model.ErrorHistoryThreshold; i++ {
   209  		state.PatchTaskPosition(ctx.GlobalVars().CaptureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) {
   210  			return &model.TaskPosition{Error: &model.RunningError{
   211  				Addr:    ctx.GlobalVars().CaptureInfo.AdvertiseAddr,
   212  				Code:    "[CDC:ErrEtcdSessionDone]",
   213  				Message: "fake error for test",
   214  			}}, true, nil
   215  		})
   216  		tester.MustApplyPatches()
   217  		manager.Tick(state)
   218  		tester.MustApplyPatches()
   219  	}
   220  	c.Assert(manager.ShouldRunning(), check.IsFalse)
   221  	c.Assert(state.Info.State, check.Equals, model.StateError)
   222  	c.Assert(state.Info.AdminJobType, check.Equals, model.AdminStop)
   223  	c.Assert(state.Status.AdminJobType, check.Equals, model.AdminStop)
   224  }
   225  
   226  func (s *feedStateManagerSuite) TestChangefeedStatusNotExist(c *check.C) {
   227  	defer testleak.AfterTest(c)()
   228  	ctx := cdcContext.NewBackendContext4Test(true)
   229  	manager := new(feedStateManager)
   230  	state := model.NewChangefeedReactorState(ctx.ChangefeedVars().ID)
   231  	tester := orchestrator.NewReactorStateTester(c, state, map[string]string{
   232  		"/tidb/cdc/capture/d563bfc0-f406-4f34-bc7d-6dc2e35a44e5": `{"id":"d563bfc0-f406-4f34-bc7d-6dc2e35a44e5","address":"172.16.6.147:8300","version":"v5.0.0-master-dirty"}`,
   233  		"/tidb/cdc/changefeed/info/" + ctx.ChangefeedVars().ID:   `{"sink-uri":"blackhole:///","opts":{},"create-time":"2021-06-05T00:44:15.065939487+08:00","start-ts":425381670108266496,"target-ts":0,"admin-job-type":1,"sort-engine":"unified","config":{"case-sensitive":true,"enable-old-value":true,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":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":"failed","history":[],"error":{"addr":"172.16.6.147:8300","code":"CDC:ErrSnapshotLostByGC","message":"[CDC:ErrSnapshotLostByGC]fail to create or maintain changefeed due to snapshot loss caused by GC. checkpoint-ts 425381670108266496 is earlier than GC safepoint at 0"},"sync-point-enabled":false,"sync-point-interval":600000000000,"creator-version":"v5.0.0-master-dirty"}`,
   234  		"/tidb/cdc/owner/156579d017f84a68":                       "d563bfc0-f406-4f34-bc7d-6dc2e35a44e5",
   235  	})
   236  	manager.Tick(state)
   237  	c.Assert(manager.ShouldRunning(), check.IsFalse)
   238  	tester.MustApplyPatches()
   239  
   240  	manager.PushAdminJob(&model.AdminJob{
   241  		CfID: ctx.ChangefeedVars().ID,
   242  		Type: model.AdminRemove,
   243  		Opts: &model.AdminJobOption{ForceRemove: true},
   244  	})
   245  	manager.Tick(state)
   246  	c.Assert(manager.ShouldRunning(), check.IsFalse)
   247  	tester.MustApplyPatches()
   248  	c.Assert(state.Info, check.IsNil)
   249  	c.Assert(state.Exist(), check.IsFalse)
   250  }