github.com/matrixorigin/matrixone@v0.7.0/pkg/hakeeper/task/task_scheduler.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 task 16 17 import ( 18 "context" 19 "time" 20 21 "github.com/matrixorigin/matrixone/pkg/common/moerr" 22 "github.com/matrixorigin/matrixone/pkg/common/runtime" 23 "github.com/matrixorigin/matrixone/pkg/hakeeper" 24 "github.com/matrixorigin/matrixone/pkg/pb/logservice" 25 "github.com/matrixorigin/matrixone/pkg/pb/task" 26 "github.com/matrixorigin/matrixone/pkg/taskservice" 27 "go.uber.org/zap" 28 ) 29 30 const ( 31 taskSchedulerDefaultTimeout = 2 * time.Second 32 ) 33 34 type scheduler struct { 35 cfg hakeeper.Config 36 taskServiceGetter func() taskservice.TaskService 37 } 38 39 var _ hakeeper.TaskScheduler = (*scheduler)(nil) 40 41 func NewScheduler(taskServiceGetter func() taskservice.TaskService, cfg hakeeper.Config) hakeeper.TaskScheduler { 42 cfg.Fill() 43 s := &scheduler{ 44 taskServiceGetter: taskServiceGetter, 45 cfg: cfg, 46 } 47 return s 48 } 49 50 func (s *scheduler) Schedule(cnState logservice.CNState, currentTick uint64) { 51 workingCN, expiredCN := parseCNStores(s.cfg, cnState, currentTick) 52 53 runningTasks := s.queryTasks(task.TaskStatus_Running) 54 createdTasks := s.queryTasks(task.TaskStatus_Created) 55 tasks := append(runningTasks, createdTasks...) 56 for _, task := range tasks { 57 if task.IsInitTask() { 58 runtime.ProcessLevelRuntime(). 59 SubLogger(runtime.SystemInit).Debug( 60 "task schedule query init task", 61 zap.String("task", task.Metadata.String())) 62 } 63 } 64 65 runtime.ProcessLevelRuntime().Logger().Debug("task schedule query tasks", zap.Int("created", len(createdTasks)), 66 zap.Int("running", len(runningTasks))) 67 if len(tasks) == 0 { 68 return 69 } 70 orderedCN := getCNOrdered(runningTasks, workingCN) 71 72 s.allocateTasks(createdTasks, orderedCN) 73 74 expiredTasks := getExpiredTasks(runningTasks, expiredCN) 75 s.allocateTasks(expiredTasks, orderedCN) 76 } 77 78 func (s *scheduler) Create(ctx context.Context, tasks []task.TaskMetadata) error { 79 ts := s.taskServiceGetter() 80 if ts == nil { 81 return moerr.NewInternalError(ctx, "failed to get task service") 82 } 83 if err := ts.CreateBatch(ctx, tasks); err != nil { 84 runtime.ProcessLevelRuntime().Logger().Error("failed to create new tasks", zap.Error(err)) 85 return err 86 } 87 runtime.ProcessLevelRuntime().Logger().Debug("new tasks created", zap.Int("created", len(tasks))) 88 v, err := ts.QueryTask(ctx) 89 if len(v) == 0 && err == nil { 90 panic("cannot read created tasks") 91 } 92 runtime.ProcessLevelRuntime().Logger().Debug("new tasks created, query", zap.Int("created", len(v)), zap.Error(err)) 93 return nil 94 } 95 96 func (s *scheduler) StartScheduleCronTask() { 97 if ts := s.taskServiceGetter(); ts != nil { 98 ts.StartScheduleCronTask() 99 } 100 } 101 102 func (s *scheduler) StopScheduleCronTask() { 103 if ts := s.taskServiceGetter(); ts != nil { 104 ts.StopScheduleCronTask() 105 } 106 } 107 108 func (s *scheduler) queryTasks(status task.TaskStatus) []task.Task { 109 ts := s.taskServiceGetter() 110 if ts == nil { 111 return nil 112 } 113 ctx, cancel := context.WithTimeout(context.Background(), taskSchedulerDefaultTimeout) 114 defer cancel() 115 116 tasks, err := ts.QueryTask(ctx, taskservice.WithTaskStatusCond(taskservice.EQ, status)) 117 if err != nil { 118 runtime.ProcessLevelRuntime().Logger().Error("failed to query tasks", 119 zap.String("status", status.String()), 120 zap.Error(err)) 121 return nil 122 } 123 return tasks 124 } 125 126 func (s *scheduler) allocateTasks(tasks []task.Task, orderedCN *cnMap) { 127 ts := s.taskServiceGetter() 128 if ts == nil { 129 return 130 } 131 132 for _, t := range tasks { 133 s.allocateTask(ts, t, orderedCN) 134 } 135 } 136 137 func (s *scheduler) allocateTask(ts taskservice.TaskService, t task.Task, orderedCN *cnMap) { 138 runner := orderedCN.min() 139 if runner == "" { 140 runtime.ProcessLevelRuntime().Logger().Warn("no CN available") 141 return 142 } 143 ctx, cancel := context.WithTimeout(context.Background(), taskSchedulerDefaultTimeout) 144 defer cancel() 145 146 if err := ts.Allocate(ctx, t, runner); err != nil { 147 runtime.ProcessLevelRuntime().Logger().Error("failed to allocate task", 148 zap.Uint64("task-id", t.ID), 149 zap.String("task-metadata-id", t.Metadata.ID), 150 zap.String("task-runner", runner), 151 zap.Error(err)) 152 return 153 } 154 orderedCN.inc(t.TaskRunner) 155 } 156 157 func getExpiredTasks(tasks []task.Task, expiredCN []string) (expired []task.Task) { 158 for _, t := range tasks { 159 if contains(expiredCN, t.TaskRunner) { 160 expired = append(expired, t) 161 } 162 } 163 return 164 } 165 166 func getCNOrdered(tasks []task.Task, workingCN []string) *cnMap { 167 orderedMap := newOrderedMap(workingCN) 168 for _, t := range tasks { 169 if contains(workingCN, t.TaskRunner) { 170 orderedMap.inc(t.TaskRunner) 171 } 172 } 173 174 return orderedMap 175 }