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  }