github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/executor/worker/task_committer_test.go (about) 1 // Copyright 2022 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 worker 15 16 import ( 17 "testing" 18 "time" 19 20 "github.com/pingcap/tiflow/engine/executor/worker/internal" 21 "github.com/pingcap/tiflow/engine/framework/taskutil" 22 "github.com/pingcap/tiflow/engine/pkg/clock" 23 "github.com/pingcap/tiflow/pkg/errors" 24 "github.com/stretchr/testify/mock" 25 "github.com/stretchr/testify/require" 26 ) 27 28 type mockWrappedTaskAdder struct { 29 mock.Mock 30 } 31 32 func (a *mockWrappedTaskAdder) addWrappedTask(task *internal.RunnableContainer) error { 33 args := a.Called(task) 34 return args.Error(0) 35 } 36 37 type taskCommitterTestSuite struct { 38 Committer *TaskCommitter 39 Clock *clock.Mock 40 Runner *mockWrappedTaskAdder 41 } 42 43 func newTaskCommitterTestSuite() *taskCommitterTestSuite { 44 clk := clock.NewMock() 45 clk.Set(time.Now()) 46 runner := &mockWrappedTaskAdder{} 47 committer := newTaskCommitterWithClock(runner, preDispatchTTLForTest, clk) 48 return &taskCommitterTestSuite{ 49 Committer: committer, 50 Clock: clk, 51 Runner: runner, 52 } 53 } 54 55 func (s *taskCommitterTestSuite) Close() { 56 s.Committer.Close() 57 } 58 59 const preDispatchTTLForTest = 10 * time.Second 60 61 func TestTaskCommitterSuccessCase(t *testing.T) { 62 suite := newTaskCommitterTestSuite() 63 64 submitTime := time.Now() 65 suite.Clock.Set(submitTime) 66 task := newDummyWorker("task-1") 67 ok := suite.Committer.PreDispatchTask("request-1", taskutil.WrapWorker(task)) 68 require.True(t, ok) 69 70 suite.Runner.On("addWrappedTask", 71 mock.MatchedBy(func(arg *internal.RunnableContainer) bool { 72 return arg.ID() == "task-1" && arg.Info().SubmitTime == clock.ToMono(submitTime) 73 })).Return(nil) 74 ok, err := suite.Committer.ConfirmDispatchTask("request-1", "task-1") 75 require.True(t, ok) 76 require.NoError(t, err) 77 suite.Runner.AssertExpectations(t) 78 79 suite.Close() 80 } 81 82 func TestTaskCommitterNoConfirmUntilTTL(t *testing.T) { 83 suite := newTaskCommitterTestSuite() 84 85 task := newDummyWorker("task-1") 86 ok := suite.Committer.PreDispatchTask("request-1", taskutil.WrapWorker(task)) 87 require.True(t, ok) 88 89 oldCount := suite.Committer.cleanUpCount() 90 suite.Clock.Add(preDispatchTTLForTest) 91 require.Eventually(t, func() bool { 92 suite.Clock.Add(time.Second) 93 return suite.Committer.cleanUpCount() > oldCount 94 }, 1*time.Second, 10*time.Millisecond) 95 96 ok, err := suite.Committer.ConfirmDispatchTask("request-1", "task-1") 97 require.False(t, ok) 98 require.NoError(t, err) 99 100 suite.Close() 101 } 102 103 func TestTaskCommitterSameTaskIDOverwrites(t *testing.T) { 104 suite := newTaskCommitterTestSuite() 105 106 task := newDummyWorker("task-1") 107 submitTime1 := time.Now() 108 suite.Clock.Set(submitTime1) 109 ok := suite.Committer.PreDispatchTask("request-1", taskutil.WrapWorker(task)) 110 require.True(t, ok) 111 112 anotherTask := newDummyWorker("task-1") 113 submitTime2 := submitTime1.Add(time.Second) 114 suite.Clock.Set(submitTime2) 115 ok = suite.Committer.PreDispatchTask("request-2", taskutil.WrapWorker(anotherTask)) 116 require.True(t, ok) 117 118 ok, err := suite.Committer.ConfirmDispatchTask("request-1", "task-1") 119 require.False(t, ok) 120 require.NoError(t, err) 121 122 suite.Runner.On("addWrappedTask", 123 mock.MatchedBy(func(arg *internal.RunnableContainer) bool { 124 return arg.ID() == "task-1" && arg.Info().SubmitTime == clock.ToMono(submitTime2) 125 })).Return(nil) 126 ok, err = suite.Committer.ConfirmDispatchTask("request-2", "task-1") 127 require.True(t, ok) 128 require.NoError(t, err) 129 suite.Runner.AssertExpectations(t) 130 131 suite.Close() 132 } 133 134 func TestTaskCommitterDuplicatePreDispatch(t *testing.T) { 135 suite := newTaskCommitterTestSuite() 136 137 task := newDummyWorker("task-1") 138 ok := suite.Committer.PreDispatchTask("request-1", taskutil.WrapWorker(task)) 139 require.True(t, ok) 140 141 ok = suite.Committer.PreDispatchTask("request-1", taskutil.WrapWorker(task)) 142 require.False(t, ok) 143 144 suite.Close() 145 } 146 147 func TestTaskCommitterFailToSubmit(t *testing.T) { 148 suite := newTaskCommitterTestSuite() 149 150 submitTime := time.Now() 151 suite.Clock.Set(submitTime) 152 task := newDummyWorker("task-1") 153 ok := suite.Committer.PreDispatchTask("request-1", taskutil.WrapWorker(task)) 154 require.True(t, ok) 155 156 suite.Runner.On("addWrappedTask", mock.Anything). 157 Return(errors.ErrRuntimeIncomingQueueFull.GenWithStackByArgs()) 158 ok, err := suite.Committer.ConfirmDispatchTask("request-1", "task-1") 159 require.False(t, ok) 160 require.Error(t, err) 161 require.Regexp(t, ".*ErrRuntimeIncomingQueueFull.*", err) 162 suite.Runner.AssertExpectations(t) 163 164 suite.Close() 165 }