github.com/matrixorigin/matrixone@v1.2.0/pkg/taskservice/task_service_cron_test.go (about) 1 // Copyright 2022 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package taskservice 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 "github.com/matrixorigin/matrixone/pkg/common/runtime" 24 "github.com/matrixorigin/matrixone/pkg/common/stopper" 25 "github.com/matrixorigin/matrixone/pkg/pb/task" 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func TestScheduleCronTask(t *testing.T) { 31 runScheduleCronTaskTest(t, func(store *memTaskStorage, s *taskService, ctx context.Context) { 32 assert.NoError(t, s.CreateCronTask(ctx, newTestTaskMetadata("t1"), "*/1 * * * * *")) 33 34 s.StartScheduleCronTask() 35 defer s.StopScheduleCronTask() 36 37 waitHasTasks(t, store, time.Second*20, WithTaskParentTaskIDCond(EQ, "t1")) 38 }) 39 } 40 41 func TestRetryScheduleCronTask(t *testing.T) { 42 runScheduleCronTaskTest(t, func(store *memTaskStorage, s *taskService, ctx context.Context) { 43 n := 0 44 store.preUpdateCron = func() error { 45 if n == 0 { 46 n++ 47 return moerr.NewInfo(context.TODO(), "test error") 48 } 49 return nil 50 } 51 52 assert.NoError(t, s.CreateCronTask(ctx, newTestTaskMetadata("t1"), "*/1 * * * * *")) 53 54 s.StartScheduleCronTask() 55 defer s.StopScheduleCronTask() 56 57 waitHasTasks(t, store, time.Second*20, WithTaskParentTaskIDCond(EQ, "t1")) 58 }) 59 } 60 61 func TestScheduleCronTaskImmediately(t *testing.T) { 62 runScheduleCronTaskTest(t, func(store *memTaskStorage, s *taskService, ctx context.Context) { 63 task := newTestCronTask("t1", "*/1 * * * * *") 64 task.CreateAt = time.Now().UnixMilli() 65 task.NextTime = task.CreateAt 66 task.TriggerTimes = 0 67 task.UpdateAt = time.Now().UnixMilli() 68 69 mustAddTestCronTask(t, store, 1, task) 70 71 s.StartScheduleCronTask() 72 defer s.StopScheduleCronTask() 73 74 waitHasTasks(t, store, time.Second*20, WithTaskParentTaskIDCond(EQ, "t1")) 75 }) 76 } 77 78 func TestScheduleCronTaskLimitConcurrency(t *testing.T) { 79 runScheduleCronTaskTest(t, func(store *memTaskStorage, s *taskService, ctx context.Context) { 80 cronTask := newTestCronTask("t1", "* * * * ? *") 81 cronTask.CreateAt = time.Now().UnixMilli() 82 cronTask.NextTime = cronTask.CreateAt 83 cronTask.TriggerTimes = 0 84 cronTask.UpdateAt = time.Now().UnixMilli() 85 cronTask.Metadata.Options.Concurrency = 1 86 87 mustAddTestCronTask(t, store, 1, cronTask) 88 89 s.StartScheduleCronTask() 90 defer s.StopScheduleCronTask() 91 92 waitHasTasks(t, store, time.Second*20, 93 WithTaskParentTaskIDCond(EQ, "t1")) 94 assertTaskCountEqual(t, store, time.Second*5, 1, 95 WithTaskParentTaskIDCond(EQ, "t1"), 96 WithTaskStatusCond(task.TaskStatus_Running)) 97 }) 98 } 99 100 func TestRemovedCronTask(t *testing.T) { 101 runScheduleCronTaskTest(t, func(store *memTaskStorage, s *taskService, ctx context.Context) { 102 assert.NoError(t, s.CreateCronTask(ctx, newTestTaskMetadata("t1"), "*/1 * * * * *")) 103 104 s.StartScheduleCronTask() 105 defer s.StopScheduleCronTask() 106 time.Sleep(time.Second * 3) 107 s.crons.stopForTest() 108 waitJobsCount(t, 1, s, time.Second*10) 109 110 store.Lock() 111 store.cronTaskIndexes = make(map[string]uint64) 112 store.cronTasks = make(map[uint64]task.CronTask) 113 store.Unlock() 114 115 s.crons.startForTest(t, s.fetchCronTasks) 116 time.Sleep(time.Second * 3) 117 s.crons.stopForTest() 118 waitJobsCount(t, 0, s, time.Second*10) 119 }) 120 } 121 122 func TestReplaceCronTask(t *testing.T) { 123 runScheduleCronTaskTest(t, func(store *memTaskStorage, s *taskService, ctx context.Context) { 124 assert.NoError(t, s.CreateCronTask(ctx, newTestTaskMetadata("t1"), "*/1 * * * * *")) 125 s.StartScheduleCronTask() 126 defer s.StopScheduleCronTask() 127 time.Sleep(time.Second * 3) 128 s.crons.stopForTest() 129 130 jobInCron := s.crons.jobs[1] 131 taskInStore := store.cronTasks[jobInCron.task.ID] 132 133 require.Equal(t, jobInCron.task.TriggerTimes, taskInStore.TriggerTimes) 134 135 t.Log("set trigger times to 0") 136 jobInCron.task.TriggerTimes = 0 137 require.Equal(t, s.crons.jobs[1].task.TriggerTimes, uint64(0)) 138 139 s.crons.startForTest(t, s.fetchCronTasks) 140 time.Sleep(time.Second * 3) 141 s.crons.stopForTest() 142 143 jobInCron = s.crons.jobs[1] 144 taskInStore = store.cronTasks[jobInCron.task.ID] 145 require.Equal(t, jobInCron.task.TriggerTimes, taskInStore.TriggerTimes) 146 }) 147 } 148 149 func (c *crons) stopForTest() { 150 c.stopper.Stop() 151 <-c.cron.Stop().Done() 152 } 153 154 func (c *crons) startForTest(t *testing.T, fn func(ctx context.Context)) { 155 c.cron.Start() 156 c.stopper = stopper.NewStopper("cronTasks") 157 require.NoError(t, c.stopper.RunTask(fn)) 158 } 159 160 func runScheduleCronTaskTest(t *testing.T, testFunc func(*memTaskStorage, *taskService, context.Context)) { 161 fetchInterval = 300 * time.Millisecond 162 163 store := NewMemTaskStorage().(*memTaskStorage) 164 s := NewTaskService(runtime.DefaultRuntime(), store).(*taskService) 165 defer func() { 166 assert.NoError(t, s.Close()) 167 }() 168 169 ctx, cancel := context.WithTimeout(context.TODO(), time.Second*10) 170 defer cancel() 171 testFunc(store, s, ctx) 172 } 173 174 func waitJobsCount(t *testing.T, n int, s *taskService, timeout time.Duration) { 175 ctx, cancel := context.WithTimeout(context.Background(), timeout) 176 defer cancel() 177 178 defer func() { 179 require.Equal(t, n, len(s.crons.entries)) 180 }() 181 182 for { 183 select { 184 case <-ctx.Done(): 185 return 186 default: 187 if len(s.crons.entries) == n { 188 return 189 } 190 } 191 time.Sleep(time.Millisecond * 10) 192 } 193 } 194 195 func waitHasTasks(t *testing.T, store *memTaskStorage, timeout time.Duration, conds ...Condition) { 196 ctx, cancel := context.WithTimeout(context.Background(), timeout) 197 defer cancel() 198 199 for { 200 select { 201 case <-ctx.Done(): 202 require.Fail(t, "wait any tasks failed") 203 return 204 default: 205 tasks, err := store.QueryAsyncTask(ctx, conds...) 206 require.NoError(t, err) 207 if len(tasks) > 0 { 208 return 209 } 210 } 211 time.Sleep(time.Millisecond * 10) 212 } 213 } 214 215 func assertTaskCountEqual(t *testing.T, store *memTaskStorage, timeout time.Duration, count int, conds ...Condition) { 216 ctx, cancel := context.WithTimeout(context.Background(), timeout) 217 defer cancel() 218 219 for { 220 select { 221 case <-ctx.Done(): 222 return 223 default: 224 tasks, err := store.QueryAsyncTask(ctx, conds...) 225 require.NoError(t, err) 226 require.LessOrEqual(t, len(tasks), count, "err") 227 } 228 time.Sleep(time.Second) 229 } 230 }