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)