github.com/matrixorigin/matrixone@v1.2.0/pkg/taskservice/types.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 "fmt" 20 "strings" 21 "time" 22 23 logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 24 "github.com/matrixorigin/matrixone/pkg/pb/task" 25 "golang.org/x/exp/constraints" 26 ) 27 28 // Condition options for query tasks 29 type Condition func(*conditions) 30 31 // Op condition op 32 type Op int 33 34 var ( 35 // EQ record == condition 36 EQ = Op(1) 37 // GT record > condition 38 GT = Op(2) 39 // GE record >= condition 40 GE = Op(3) 41 // LT record < condition 42 LT = Op(4) 43 // LE record <= condition 44 LE = Op(5) 45 // IN record in condition 46 IN = Op(6) 47 // LIKE record LIKE condition 48 LIKE = Op(7) 49 50 OpName = map[Op]string{ 51 EQ: "=", 52 GT: ">", 53 GE: ">=", 54 LT: "<", 55 LE: "<=", 56 IN: "IN", 57 LIKE: "LIKE", 58 } 59 ) 60 61 type condition interface { 62 eval(v any) bool 63 sql() string 64 } 65 66 type limitCond struct { 67 limit int 68 } 69 70 func (c *limitCond) eval(v any) bool { 71 limit, ok := v.(int) 72 if !ok { 73 return false 74 } 75 return c.limit > 0 && limit >= c.limit 76 } 77 78 func (c *limitCond) sql() string { 79 return fmt.Sprintf(" limit %d", c.limit) 80 } 81 82 type taskIDCond struct { 83 op Op 84 taskID uint64 85 } 86 87 func (c *taskIDCond) eval(v any) bool { 88 taskID, ok := v.(uint64) 89 if !ok { 90 return false 91 } 92 return compare(c.op, taskID, c.taskID) 93 } 94 95 func (c *taskIDCond) sql() string { 96 return fmt.Sprintf("task_id%s%d", OpName[c.op], c.taskID) 97 } 98 99 type taskRunnerCond struct { 100 op Op 101 taskRunner string 102 } 103 104 func (c *taskRunnerCond) eval(v any) bool { 105 runner, ok := v.(string) 106 if !ok { 107 return false 108 } 109 return compare(c.op, runner, c.taskRunner) 110 } 111 112 func (c *taskRunnerCond) sql() string { 113 return fmt.Sprintf("task_runner%s'%s'", OpName[c.op], c.taskRunner) 114 } 115 116 type taskStatusCond struct { 117 op Op 118 taskStatus []task.TaskStatus 119 } 120 121 func (c *taskStatusCond) eval(v any) bool { 122 status, ok := v.(task.TaskStatus) 123 if !ok { 124 return false 125 } 126 for _, t := range c.taskStatus { 127 if compare(EQ, status, t) { 128 return true 129 } 130 } 131 return false 132 } 133 134 func (c *taskStatusCond) sql() string { 135 expr := strings.Trim(strings.Join(strings.Fields(fmt.Sprintf("%d", c.taskStatus)), ","), "[]") 136 // task_status in (1,2,3) 137 return fmt.Sprintf("task_status %s (%s)", OpName[c.op], expr) 138 } 139 140 type taskEpochCond struct { 141 op Op 142 taskEpoch uint32 143 } 144 145 func (c *taskEpochCond) eval(v any) bool { 146 epoch, ok := v.(uint32) 147 if !ok { 148 return false 149 } 150 return compare(c.op, epoch, c.taskEpoch) 151 } 152 153 func (c *taskEpochCond) sql() string { 154 return fmt.Sprintf("task_epoch%s%d", OpName[c.op], c.taskEpoch) 155 } 156 157 type taskParentTaskIDCond struct { 158 op Op 159 taskParentTaskID string 160 } 161 162 func (c *taskParentTaskIDCond) eval(v any) bool { 163 taskID, ok := v.(string) 164 if !ok { 165 return false 166 } 167 return compare(c.op, taskID, c.taskParentTaskID) 168 } 169 170 func (c *taskParentTaskIDCond) sql() string { 171 return fmt.Sprintf("task_parent_id%s'%s'", OpName[c.op], c.taskParentTaskID) 172 } 173 174 type taskExecutorCond struct { 175 op Op 176 taskExecutor task.TaskCode 177 } 178 179 func (c *taskExecutorCond) eval(v any) bool { 180 executor, ok := v.(task.TaskCode) 181 if !ok { 182 return false 183 } 184 return compare(c.op, executor, c.taskExecutor) 185 } 186 187 func (c *taskExecutorCond) sql() string { 188 return fmt.Sprintf("task_metadata_executor%s%d", OpName[c.op], c.taskExecutor) 189 } 190 191 type taskTypeCond struct { 192 op Op 193 taskType string 194 } 195 196 func (c *taskTypeCond) eval(v any) bool { 197 type_, ok := v.(string) 198 if !ok { 199 return false 200 } 201 return compare(c.op, type_, c.taskType) 202 } 203 204 func (c *taskTypeCond) sql() string { 205 return fmt.Sprintf("task_type%s'%s'", OpName[c.op], c.taskType) 206 } 207 208 type orderByDescCond struct{} 209 210 func (c *orderByDescCond) eval(_ any) bool { 211 return true 212 } 213 214 func (c *orderByDescCond) sql() string { 215 return " order by task_id desc" 216 } 217 218 type accountIDCond struct { 219 op Op 220 accountID uint32 221 } 222 223 func (c *accountIDCond) eval(v any) bool { 224 aid, ok := v.(uint32) 225 if !ok { 226 return false 227 } 228 return compare(c.op, aid, c.accountID) 229 } 230 231 func (c *accountIDCond) sql() string { 232 return fmt.Sprintf("account_id%s%d", OpName[c.op], c.accountID) 233 } 234 235 type accountCond struct { 236 op Op 237 account string 238 } 239 240 func (c *accountCond) eval(v any) bool { 241 acc, ok := v.(string) 242 if !ok { 243 return false 244 } 245 return compare(c.op, acc, c.account) 246 } 247 248 func (c *accountCond) sql() string { 249 return fmt.Sprintf("account%s%s", OpName[c.op], c.account) 250 } 251 252 type lastHeartbeatCond struct { 253 op Op 254 hb int64 255 } 256 257 func (c *lastHeartbeatCond) eval(v any) bool { 258 hb, ok := v.(int64) 259 if !ok { 260 return false 261 } 262 return compare(c.op, hb, c.hb) 263 } 264 265 func (c *lastHeartbeatCond) sql() string { 266 if c.op == LT || c.op == LE { 267 return fmt.Sprintf("(last_heartbeat%s'%s' or last_heartbeat is NULL)", 268 OpName[c.op], 269 time.Unix(c.hb/1e9, c.hb%1e9).Format("2006-01-02 15:04:05")) 270 } else { 271 return fmt.Sprintf("last_heartbeat%s'%s'", OpName[c.op], 272 time.Unix(c.hb/1e9, c.hb%1e9).Format("2006-01-02 15:04:05")) 273 } 274 } 275 276 type cronTaskIDCond struct { 277 op Op 278 cronTaskID uint64 279 } 280 281 func (c *cronTaskIDCond) eval(v any) bool { 282 taskID, ok := v.(uint64) 283 if !ok { 284 return false 285 } 286 return compare(c.op, taskID, c.cronTaskID) 287 } 288 289 func (c *cronTaskIDCond) sql() string { 290 return fmt.Sprintf("cron_task_id%s%d", OpName[c.op], c.cronTaskID) 291 } 292 293 type taskMetadataIDCond struct { 294 op Op 295 taskMetadataID string 296 } 297 298 func (c *taskMetadataIDCond) eval(v any) bool { 299 return false 300 } 301 302 func (c *taskMetadataIDCond) sql() string { 303 return fmt.Sprintf("task_metadata_id %s '%s'", OpName[c.op], c.taskMetadataID) 304 } 305 306 func compare[T constraints.Ordered](op Op, a T, b T) bool { 307 switch op { 308 case EQ: 309 return a == b 310 case GT: 311 return a > b 312 case GE: 313 return a >= b 314 case LT: 315 return a < b 316 case LE: 317 return a <= b 318 default: 319 return false 320 } 321 } 322 323 type condCode uint32 324 325 const ( 326 CondLimit condCode = iota 327 CondTaskID 328 CondTaskRunner 329 CondTaskStatus 330 CondTaskEpoch 331 CondTaskParentTaskID 332 CondTaskExecutor 333 CondTaskType 334 CondOrderByDesc 335 CondAccountID 336 CondAccount 337 CondLastHeartbeat 338 CondCronTaskId 339 CondTaskMetadataId 340 ) 341 342 var ( 343 whereConditionCodes = map[condCode]struct{}{ 344 CondTaskID: {}, 345 CondTaskRunner: {}, 346 CondTaskStatus: {}, 347 CondTaskEpoch: {}, 348 CondTaskParentTaskID: {}, 349 CondTaskExecutor: {}, 350 CondCronTaskId: {}, 351 CondTaskMetadataId: {}, 352 } 353 daemonWhereConditionCodes = map[condCode]struct{}{ 354 CondTaskID: {}, 355 CondTaskRunner: {}, 356 CondTaskStatus: {}, 357 CondTaskType: {}, 358 CondAccountID: {}, 359 CondAccount: {}, 360 CondLastHeartbeat: {}, 361 } 362 ) 363 364 type conditions map[condCode]condition 365 366 func newConditions(conds ...Condition) *conditions { 367 c := &conditions{} 368 for _, cond := range conds { 369 cond(c) 370 } 371 return c 372 } 373 374 // WithTaskIDDesc set query with order by task id desc 375 func WithTaskIDDesc() Condition { 376 return func(c *conditions) { 377 (*c)[CondOrderByDesc] = &orderByDescCond{} 378 } 379 } 380 381 // WithTaskExecutorCond set task executor condition 382 func WithTaskExecutorCond(op Op, value task.TaskCode) Condition { 383 return func(c *conditions) { 384 (*c)[CondTaskExecutor] = &taskExecutorCond{op: op, taskExecutor: value} 385 } 386 } 387 388 // WithLimitCond set query result limit 389 func WithLimitCond(limit int) Condition { 390 return func(c *conditions) { 391 (*c)[CondLimit] = &limitCond{limit: limit} 392 } 393 } 394 395 // WithTaskIDCond set task id condition 396 func WithTaskIDCond(op Op, value uint64) Condition { 397 return func(c *conditions) { 398 (*c)[CondTaskID] = &taskIDCond{op: op, taskID: value} 399 } 400 } 401 402 // WithTaskRunnerCond set task runner condition 403 func WithTaskRunnerCond(op Op, value string) Condition { 404 return func(c *conditions) { 405 (*c)[CondTaskRunner] = &taskRunnerCond{op: op, taskRunner: value} 406 } 407 } 408 409 // WithTaskStatusCond set status condition 410 func WithTaskStatusCond(value ...task.TaskStatus) Condition { 411 op := IN 412 return func(c *conditions) { 413 (*c)[CondTaskStatus] = &taskStatusCond{op: op, taskStatus: value} 414 } 415 } 416 417 // WithTaskEpochCond set task epoch condition 418 func WithTaskEpochCond(op Op, value uint32) Condition { 419 return func(c *conditions) { 420 (*c)[CondTaskEpoch] = &taskEpochCond{op: op, taskEpoch: value} 421 } 422 } 423 424 // WithTaskParentTaskIDCond set task ParentTaskID condition 425 func WithTaskParentTaskIDCond(op Op, value string) Condition { 426 return func(c *conditions) { 427 (*c)[CondTaskParentTaskID] = &taskParentTaskIDCond{op: op, taskParentTaskID: value} 428 } 429 } 430 431 // WithTaskType set task type condition. 432 func WithTaskType(op Op, value string) Condition { 433 return func(c *conditions) { 434 (*c)[CondTaskType] = &taskTypeCond{op: op, taskType: value} 435 } 436 } 437 438 // WithAccountID set task account ID condition. 439 func WithAccountID(op Op, value uint32) Condition { 440 return func(c *conditions) { 441 (*c)[CondAccountID] = &accountIDCond{op: op, accountID: value} 442 } 443 } 444 445 // WithAccount set task account condition. 446 func WithAccount(op Op, value string) Condition { 447 return func(c *conditions) { 448 (*c)[CondAccount] = &accountCond{op: op, account: value} 449 } 450 } 451 452 // WithLastHeartbeat set last heartbeat condition. 453 func WithLastHeartbeat(op Op, value int64) Condition { 454 return func(c *conditions) { 455 (*c)[CondLastHeartbeat] = &lastHeartbeatCond{op: op, hb: value} 456 } 457 } 458 459 func WithCronTaskId(op Op, value uint64) Condition { 460 return func(c *conditions) { 461 (*c)[CondCronTaskId] = &cronTaskIDCond{op: op, cronTaskID: value} 462 } 463 } 464 465 func WithTaskMetadataId(op Op, value string) Condition { 466 return func(c *conditions) { 467 (*c)[CondTaskMetadataId] = &taskMetadataIDCond{op: op, taskMetadataID: value} 468 } 469 } 470 471 // TaskService Asynchronous Task Service, which provides scheduling execution and management of 472 // asynchronous tasks. CN, DN, HAKeeper, LogService will all hold this service. 473 type TaskService interface { 474 // Close close the task service 475 Close() error 476 477 // CreateAsyncTask Creates an asynchronous task that executes a single time, this method is idempotent, the 478 // same task is not created repeatedly based on multiple calls. 479 CreateAsyncTask(context.Context, task.TaskMetadata) error 480 // CreateBatch is similar to Create, but with a batch task list 481 CreateBatch(context.Context, []task.TaskMetadata) error 482 // CreateCronTask is similar to Create, but create a task that runs periodically, with the period 483 // described using a Cron expression. 484 CreateCronTask(ctx context.Context, task task.TaskMetadata, cronExpr string) error 485 // Allocate allocate task runner fot spec task. 486 Allocate(ctx context.Context, value task.AsyncTask, taskRunner string) error 487 // Complete task completed. The result used to indicate whether the execution succeeded or failed 488 Complete(ctx context.Context, taskRunner string, task task.AsyncTask, result task.ExecuteResult) error 489 // Heartbeat sending a heartbeat tells the scheduler that the specified task is running normally. 490 // If the scheduler does not receive the heartbeat for a long time, it will reassign the task executor 491 // to execute the task. Returning `ErrInvalidTask` means that the Task has been reassigned or has 492 // ended, and the Task execution needs to be terminated immediately。 493 Heartbeat(ctx context.Context, task task.AsyncTask) error 494 // QueryAsyncTask query tasks by conditions 495 QueryAsyncTask(context.Context, ...Condition) ([]task.AsyncTask, error) 496 // QueryCronTask query cron tasks by conditions 497 QueryCronTask(context.Context, ...Condition) ([]task.CronTask, error) 498 499 // CreateDaemonTask creates a daemon task that will run in background for long time. 500 CreateDaemonTask(ctx context.Context, value task.TaskMetadata, details *task.Details) error 501 // QueryDaemonTask returns all daemon tasks which match the conditions. 502 QueryDaemonTask(ctx context.Context, conds ...Condition) ([]task.DaemonTask, error) 503 // UpdateDaemonTask updates the daemon task record. 504 UpdateDaemonTask(ctx context.Context, tasks []task.DaemonTask, cond ...Condition) (int, error) 505 // HeartbeatDaemonTask sends heartbeat to daemon task. 506 HeartbeatDaemonTask(ctx context.Context, task task.DaemonTask) error 507 508 // StartScheduleCronTask start schedule cron tasks. A timer will be started to pull the latest CronTask 509 // from the TaskStore at regular intervals, and a timer will be maintained in memory for all Cron's to be 510 // triggered at regular intervals. 511 StartScheduleCronTask() 512 // StopScheduleCronTask stop schedule cron tasks. 513 StopScheduleCronTask() 514 515 // GetStorage returns the task storage 516 GetStorage() TaskStorage 517 } 518 519 // TaskExecutor which is responsible for the execution logic of a specific Task, and the function exists to 520 // represent the completion of the task execution. In the process of task execution task may be interrupted 521 // at any time, so the implementation needs to frequently check the state of the Context, in the 522 // Context.Done(), as soon as possible to exit. Epoch is 1 means the task is executed for the first time, 523 // otherwise it means that the task is rescheduled, the task may be completed or not. 524 type TaskExecutor func(ctx context.Context, task task.Task) error 525 526 // TaskRunner each runner can execute multiple task concurrently 527 type TaskRunner interface { 528 // ID returns the TaskRunner ID 529 ID() string 530 // Start start the runner, after runner starts it will start to periodically load the tasks assigned to 531 // the current executor, as well as periodically send heartbeats. 532 Start() error 533 // Stop stop the runner, all running tasks will be terminated 534 Stop() error 535 // Parallelism maximum number of concurrently executing Tasks 536 Parallelism() int 537 // RegisterExecutor register the task executor 538 RegisterExecutor(code task.TaskCode, executor TaskExecutor) 539 // GetExecutor returns the task executor 540 GetExecutor(code task.TaskCode) TaskExecutor 541 // Attach attaches the active go-routine to the daemon task. 542 Attach(ctx context.Context, taskID uint64, routine ActiveRoutine) error 543 } 544 545 // TaskStorage task storage 546 type TaskStorage interface { 547 // Close close the task storage 548 Close() error 549 550 // AddAsyncTask adds async tasks and returns number of successful added 551 AddAsyncTask(context.Context, ...task.AsyncTask) (int, error) 552 // UpdateAsyncTask updates async tasks and returns number of successful updated 553 UpdateAsyncTask(context.Context, []task.AsyncTask, ...Condition) (int, error) 554 // DeleteAsyncTask deletes tasks and returns number of successful deleted 555 DeleteAsyncTask(context.Context, ...Condition) (int, error) 556 // QueryAsyncTask queries tasks by conditions 557 QueryAsyncTask(context.Context, ...Condition) ([]task.AsyncTask, error) 558 559 // AddCronTask add cron task and returns number of successful added 560 AddCronTask(context.Context, ...task.CronTask) (int, error) 561 // QueryCronTask query all cron tasks 562 QueryCronTask(context.Context, ...Condition) ([]task.CronTask, error) 563 // UpdateCronTask crontask generates tasks periodically, and this update 564 // needs to be in a transaction. Update cron task and insert a new task. 565 // This update must be transactional and needs to be done conditionally 566 // using CronTask.TriggerTimes and the task.Metadata.ID field. 567 UpdateCronTask(context.Context, task.CronTask, task.AsyncTask) (int, error) 568 569 // AddDaemonTask adds daemon tasks and returns number of successful added. 570 AddDaemonTask(ctx context.Context, tasks ...task.DaemonTask) (int, error) 571 // UpdateDaemonTask updates daemon tasks and returns number of successful updated. 572 UpdateDaemonTask(ctx context.Context, tasks []task.DaemonTask, conds ...Condition) (int, error) 573 // DeleteDaemonTask deletes daemon tasks and returns number of successful deleted. 574 DeleteDaemonTask(ctx context.Context, condition ...Condition) (int, error) 575 // QueryDaemonTask queries daemon tasks by conditions. 576 QueryDaemonTask(ctx context.Context, condition ...Condition) ([]task.DaemonTask, error) 577 // HeartbeatDaemonTask update the last heartbeat field of the task. 578 HeartbeatDaemonTask(ctx context.Context, task []task.DaemonTask) (int, error) 579 } 580 581 // TaskServiceHolder create and hold the task service in the cn, tn and log node. Create 582 // the TaskService from the heartbeat's CreateTaskService schedule command. 583 type TaskServiceHolder interface { 584 // Close close the holder 585 Close() error 586 // Get returns the taskservice 587 Get() (TaskService, bool) 588 // Create create the taskservice 589 Create(command logservicepb.CreateTaskService) error 590 } 591 592 type TaskStorageFactory interface { 593 Create(address string) (TaskStorage, error) 594 } 595 596 type Getter func() (TaskService, bool)