github.com/matrixorigin/matrixone@v0.7.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/pb/task" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 func TestScheduleCronTask(t *testing.T) { 30 runScheduleCronTaskTest(t, func(store *memTaskStorage, s *taskService, ctx context.Context) { 31 assert.NoError(t, s.CreateCronTask(ctx, newTestTaskMetadata("t1"), "*/1 * * * * *")) 32 33 s.StartScheduleCronTask() 34 defer s.StopScheduleCronTask() 35 36 waitHasTasks(t, store, time.Second*20, WithTaskParentTaskIDCond(EQ, "t1")) 37 }, time.Millisecond, time.Millisecond) 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 }, time.Millisecond, time.Millisecond) 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 }, time.Millisecond, time.Millisecond) 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(EQ, task.TaskStatus_Running)) 97 }, time.Millisecond, time.Millisecond) 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 107 waitJobsCount(t, 1, s, time.Second*10) 108 109 store.Lock() 110 store.cronTaskIndexes = make(map[string]uint64) 111 store.cronTasks = make(map[uint64]task.CronTask) 112 store.Unlock() 113 114 waitJobsCount(t, 0, s, time.Second*10) 115 }, time.Millisecond, time.Millisecond) 116 117 } 118 119 func runScheduleCronTaskTest(t *testing.T, 120 testFunc func(store *memTaskStorage, s *taskService, ctx context.Context), 121 fetch, retry time.Duration) { 122 retryInterval = retry 123 fetchInterval = fetch 124 125 store := NewMemTaskStorage().(*memTaskStorage) 126 s := NewTaskService(runtime.DefaultRuntime(), store).(*taskService) 127 defer func() { 128 assert.NoError(t, s.Close()) 129 }() 130 131 ctx, cancel := context.WithTimeout(context.TODO(), time.Second*10) 132 defer cancel() 133 testFunc(store, s, ctx) 134 } 135 136 func waitJobsCount(t *testing.T, n int, s *taskService, timeout time.Duration) { 137 ctx, cancel := context.WithTimeout(context.Background(), timeout) 138 defer cancel() 139 140 for { 141 select { 142 case <-ctx.Done(): 143 require.Fail(t, "wait jobs count failed") 144 return 145 default: 146 s.crons.Lock() 147 v := len(s.crons.jobs) 148 s.crons.Unlock() 149 150 if v == n { 151 return 152 } 153 } 154 time.Sleep(time.Millisecond * 10) 155 } 156 } 157 158 func waitHasTasks(t *testing.T, store *memTaskStorage, timeout time.Duration, conds ...Condition) { 159 ctx, cancel := context.WithTimeout(context.Background(), timeout) 160 defer cancel() 161 162 for { 163 select { 164 case <-ctx.Done(): 165 require.Fail(t, "wait any tasks failed") 166 return 167 default: 168 tasks, err := store.Query(ctx, conds...) 169 require.NoError(t, err) 170 if len(tasks) > 0 { 171 return 172 } 173 } 174 time.Sleep(time.Millisecond * 10) 175 } 176 } 177 178 func assertTaskCountEqual(t *testing.T, store *memTaskStorage, timeout time.Duration, count int, conds ...Condition) { 179 ctx, cancel := context.WithTimeout(context.Background(), timeout) 180 defer cancel() 181 182 for { 183 select { 184 case <-ctx.Done(): 185 return 186 default: 187 tasks, err := store.Query(ctx, conds...) 188 require.NoError(t, err) 189 require.LessOrEqual(t, len(tasks), count, "err") 190 } 191 time.Sleep(time.Second) 192 } 193 }