github.com/matrixorigin/matrixone@v1.2.0/pkg/taskservice/task_service.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 "errors" 20 "fmt" 21 "time" 22 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/common/runtime" 25 "github.com/matrixorigin/matrixone/pkg/pb/task" 26 "github.com/robfig/cron/v3" 27 ) 28 29 type taskService struct { 30 store TaskStorage 31 cronParser cron.Parser 32 rt runtime.Runtime 33 34 crons crons 35 } 36 37 // NewTaskService create a task service based on a task storage. 38 func NewTaskService( 39 rt runtime.Runtime, 40 store TaskStorage) TaskService { 41 return &taskService{ 42 rt: rt, 43 store: store, 44 cronParser: cron.NewParser( 45 cron.Second | 46 cron.Minute | 47 cron.Hour | 48 cron.Dom | 49 cron.Month | 50 cron.Dow | 51 cron.Descriptor), 52 } 53 } 54 55 func (s *taskService) CreateAsyncTask(ctx context.Context, value task.TaskMetadata) error { 56 for { 57 select { 58 case <-ctx.Done(): 59 return errors.Join(ctx.Err(), ErrNotReady) 60 default: 61 if _, err := s.store.AddAsyncTask(ctx, newTaskFromMetadata(value)); err != nil { 62 if errors.Is(err, ErrNotReady) { 63 time.Sleep(300 * time.Millisecond) 64 continue 65 } 66 return err 67 } 68 return nil 69 } 70 } 71 } 72 73 func (s *taskService) CreateBatch(ctx context.Context, tasks []task.TaskMetadata) error { 74 values := make([]task.AsyncTask, 0, len(tasks)) 75 for _, v := range tasks { 76 values = append(values, newTaskFromMetadata(v)) 77 } 78 79 for { 80 select { 81 case <-ctx.Done(): 82 return errors.Join(ctx.Err(), ErrNotReady) 83 default: 84 if _, err := s.store.AddAsyncTask(ctx, values...); err != nil { 85 if errors.Is(err, ErrNotReady) { 86 time.Sleep(300 * time.Millisecond) 87 continue 88 } 89 return err 90 } 91 return nil 92 } 93 } 94 } 95 96 func (s *taskService) CreateCronTask(ctx context.Context, value task.TaskMetadata, cronExpr string) error { 97 sche, err := s.cronParser.Parse(cronExpr) 98 if err != nil { 99 return err 100 } 101 102 now := time.Now().UnixMilli() 103 next := sche.Next(time.UnixMilli(now)) 104 105 _, err = s.store.AddCronTask(ctx, task.CronTask{ 106 Metadata: value, 107 CronExpr: cronExpr, 108 NextTime: next.UnixMilli(), 109 TriggerTimes: 0, 110 CreateAt: now, 111 UpdateAt: now, 112 }) 113 return err 114 } 115 116 func (s *taskService) CreateDaemonTask(ctx context.Context, metadata task.TaskMetadata, details *task.Details) error { 117 now := time.Now() 118 119 dt := task.DaemonTask{ 120 Metadata: metadata, 121 TaskType: details.Type(), 122 TaskStatus: task.TaskStatus_Created, 123 Details: details, 124 CreateAt: now, 125 UpdateAt: now, 126 } 127 _, err := s.store.AddDaemonTask(ctx, dt) 128 return err 129 } 130 131 func (s *taskService) Allocate(ctx context.Context, value task.AsyncTask, taskRunner string) error { 132 exists, err := s.store.QueryAsyncTask(ctx, WithTaskIDCond(EQ, value.ID)) 133 if err != nil { 134 return err 135 } 136 if len(exists) != 1 { 137 s.rt.Logger().Debug(fmt.Sprintf("queried tasks: %v", exists)) 138 s.rt.Logger().Fatal(fmt.Sprintf("query task by primary key, return %d records", len(exists))) 139 } 140 141 old := exists[0] 142 switch old.Status { 143 case task.TaskStatus_Running: 144 old.Epoch++ 145 old.TaskRunner = taskRunner 146 old.LastHeartbeat = time.Now().UnixMilli() 147 case task.TaskStatus_Created: 148 old.Status = task.TaskStatus_Running 149 old.Epoch = 1 150 old.TaskRunner = taskRunner 151 old.LastHeartbeat = time.Now().UnixMilli() 152 default: 153 return moerr.NewInvalidTask(ctx, taskRunner, value.ID) 154 } 155 156 n, err := s.store.UpdateAsyncTask(ctx, 157 []task.AsyncTask{old}, 158 WithTaskIDCond(EQ, old.ID), 159 WithTaskEpochCond(EQ, old.Epoch-1)) 160 if err != nil { 161 return err 162 } 163 if n == 0 { 164 return moerr.NewInvalidTask(ctx, taskRunner, value.ID) 165 } 166 return nil 167 } 168 169 func (s *taskService) Complete( 170 ctx context.Context, 171 taskRunner string, 172 value task.AsyncTask, 173 result task.ExecuteResult) error { 174 value.CompletedAt = time.Now().UnixMilli() 175 value.Status = task.TaskStatus_Completed 176 value.ExecuteResult = &result 177 n, err := s.store.UpdateAsyncTask(ctx, []task.AsyncTask{value}, 178 WithTaskStatusCond(task.TaskStatus_Running), 179 WithTaskRunnerCond(EQ, taskRunner), 180 WithTaskEpochCond(EQ, value.Epoch)) 181 if err != nil { 182 return err 183 } 184 if n == 0 { 185 return moerr.NewInvalidTask(ctx, value.TaskRunner, value.ID) 186 } 187 return nil 188 } 189 190 func (s *taskService) Heartbeat(ctx context.Context, value task.AsyncTask) error { 191 value.LastHeartbeat = time.Now().UnixMilli() 192 n, err := s.store.UpdateAsyncTask(ctx, []task.AsyncTask{value}, 193 WithTaskIDCond(EQ, value.ID), 194 WithTaskStatusCond(task.TaskStatus_Running), 195 WithTaskEpochCond(LE, value.Epoch), 196 WithTaskRunnerCond(EQ, value.TaskRunner)) 197 if err != nil { 198 return err 199 } 200 if n == 0 { 201 return moerr.NewInvalidTask(ctx, value.TaskRunner, value.ID) 202 } 203 return nil 204 } 205 206 func (s *taskService) QueryAsyncTask(ctx context.Context, conds ...Condition) ([]task.AsyncTask, error) { 207 return s.store.QueryAsyncTask(ctx, conds...) 208 } 209 210 func (s *taskService) QueryCronTask(ctx context.Context, c ...Condition) ([]task.CronTask, error) { 211 return s.store.QueryCronTask(ctx, c...) 212 } 213 214 func (s *taskService) UpdateDaemonTask(ctx context.Context, tasks []task.DaemonTask, conds ...Condition) (int, error) { 215 return s.store.UpdateDaemonTask(ctx, tasks, conds...) 216 } 217 218 func (s *taskService) QueryDaemonTask(ctx context.Context, conds ...Condition) ([]task.DaemonTask, error) { 219 return s.store.QueryDaemonTask(ctx, conds...) 220 } 221 222 func (s *taskService) Close() error { 223 s.StopScheduleCronTask() 224 return s.store.Close() 225 } 226 227 func (s *taskService) GetStorage() TaskStorage { 228 return s.store 229 } 230 231 func (s *taskService) HeartbeatDaemonTask(ctx context.Context, t task.DaemonTask) error { 232 t.LastHeartbeat = time.Now().UTC() 233 n, err := s.store.HeartbeatDaemonTask(ctx, []task.DaemonTask{t}) 234 if err != nil { 235 return err 236 } 237 if n == 0 { 238 return moerr.NewInvalidTask(ctx, t.TaskRunner, t.ID) 239 } 240 return nil 241 }