github.com/matrixorigin/matrixone@v1.2.0/pkg/taskservice/task_service_holder.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  	"sync"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/common/log"
    23  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    24  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    25  	"github.com/matrixorigin/matrixone/pkg/common/stopper"
    26  	logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/task"
    28  	"go.uber.org/zap"
    29  )
    30  
    31  var (
    32  	ErrNotReady = moerr.NewInvalidStateNoCtx("task store not ready")
    33  )
    34  
    35  type taskServiceHolder struct {
    36  	rt                         runtime.Runtime
    37  	addressFactory             func(context.Context, bool) (string, error)
    38  	taskStorageFactorySelector func(string, string, string) TaskStorageFactory
    39  	mu                         struct {
    40  		sync.RWMutex
    41  		closed  bool
    42  		store   TaskStorage
    43  		service TaskService
    44  	}
    45  }
    46  
    47  // NewTaskServiceHolder create a task service hold, it will create task storage and task service from the hakeeper's schedule command.
    48  func NewTaskServiceHolder(
    49  	rt runtime.Runtime,
    50  	addressFactory func(context.Context, bool) (string, error)) TaskServiceHolder {
    51  	return NewTaskServiceHolderWithTaskStorageFactorySelector(rt, addressFactory, func(username, password, database string) TaskStorageFactory {
    52  		return NewMySQLBasedTaskStorageFactory(username, password, database)
    53  	})
    54  }
    55  
    56  // NewTaskServiceHolderWithTaskStorageFactorySelector is similar to NewTaskServiceHolder, but with a special
    57  // task storage facroty selector
    58  func NewTaskServiceHolderWithTaskStorageFactorySelector(
    59  	rt runtime.Runtime,
    60  	addressFactory func(context.Context, bool) (string, error),
    61  	selector func(string, string, string) TaskStorageFactory) TaskServiceHolder {
    62  	return &taskServiceHolder{
    63  		rt:                         rt,
    64  		addressFactory:             addressFactory,
    65  		taskStorageFactorySelector: selector,
    66  	}
    67  }
    68  
    69  func (h *taskServiceHolder) Close() error {
    70  	defer h.rt.Logger().LogAction("close service-holder",
    71  		log.DefaultLogOptions().WithLevel(zap.DebugLevel))()
    72  
    73  	h.mu.Lock()
    74  	defer h.mu.Unlock()
    75  
    76  	if h.mu.closed {
    77  		return nil
    78  	}
    79  	h.mu.closed = true
    80  	if h.mu.store == nil {
    81  		return nil
    82  	}
    83  	return h.mu.service.Close()
    84  }
    85  
    86  func (h *taskServiceHolder) Create(command logservicepb.CreateTaskService) error {
    87  	// TODO: In any case, the username and password are not printed in the log, morpc needs to fix
    88  	if command.User.Username == "" || command.User.Password == "" {
    89  		h.rt.Logger().Debug("start task runner skipped",
    90  			zap.String("reason", "empty task user and passwd"))
    91  		return moerr.NewInvalidStateNoCtx("empty task user and passwd")
    92  	}
    93  
    94  	h.mu.Lock()
    95  	defer h.mu.Unlock()
    96  	if h.mu.service != nil {
    97  		return nil
    98  	}
    99  
   100  	store := newRefreshableTaskStorage(
   101  		h.rt,
   102  		h.addressFactory,
   103  		h.taskStorageFactorySelector(command.User.Username,
   104  			command.User.Password,
   105  			command.TaskDatabase))
   106  	h.mu.store = store
   107  	h.mu.service = NewTaskService(h.rt, store)
   108  	return nil
   109  }
   110  
   111  func (h *taskServiceHolder) Get() (TaskService, bool) {
   112  	h.mu.RLock()
   113  	defer h.mu.RUnlock()
   114  	if h.mu.service == nil {
   115  		return nil, false
   116  	}
   117  	return h.mu.service, true
   118  }
   119  
   120  type refreshableTaskStorage struct {
   121  	rt             runtime.Runtime
   122  	refreshC       chan string
   123  	stopper        *stopper.Stopper
   124  	addressFactory func(context.Context, bool) (string, error)
   125  	storeFactory   TaskStorageFactory
   126  	mu             struct {
   127  		sync.RWMutex
   128  		closed      bool
   129  		lastAddress string
   130  		store       TaskStorage
   131  	}
   132  }
   133  
   134  func newRefreshableTaskStorage(
   135  	rt runtime.Runtime,
   136  	addressFactory func(context.Context, bool) (string, error),
   137  	storeFactory TaskStorageFactory) TaskStorage {
   138  	s := &refreshableTaskStorage{
   139  		rt:             rt,
   140  		refreshC:       make(chan string, 1),
   141  		addressFactory: addressFactory,
   142  		storeFactory:   storeFactory,
   143  		stopper: stopper.NewStopper("refresh-taskstorage",
   144  			stopper.WithLogger(rt.Logger().RawLogger())),
   145  	}
   146  	s.refresh(context.Background(), "")
   147  	if err := s.stopper.RunTask(s.refreshTask); err != nil {
   148  		panic(err)
   149  	}
   150  	return s
   151  }
   152  
   153  func (s *refreshableTaskStorage) Close() error {
   154  	defer s.rt.Logger().LogAction("close refreshable-storage",
   155  		log.DefaultLogOptions().WithLevel(zap.DebugLevel))()
   156  
   157  	var err error
   158  	s.mu.Lock()
   159  	if s.mu.closed {
   160  		s.mu.Unlock()
   161  		return nil
   162  	}
   163  	s.mu.closed = true
   164  	if s.mu.store != nil {
   165  		err = s.mu.store.Close()
   166  	}
   167  	s.mu.Unlock()
   168  	s.stopper.Stop()
   169  	close(s.refreshC)
   170  	return err
   171  }
   172  
   173  func (s *refreshableTaskStorage) AddAsyncTask(ctx context.Context, tasks ...task.AsyncTask) (int, error) {
   174  	var v int
   175  	var err error
   176  	s.mu.RLock()
   177  	lastAddress := s.mu.lastAddress
   178  	if s.mu.store == nil {
   179  		err = ErrNotReady
   180  	} else {
   181  		v, err = s.mu.store.AddAsyncTask(ctx, tasks...)
   182  	}
   183  	s.mu.RUnlock()
   184  	if err != nil {
   185  		s.maybeRefresh(lastAddress)
   186  	}
   187  	return v, err
   188  }
   189  
   190  func (s *refreshableTaskStorage) UpdateAsyncTask(ctx context.Context, tasks []task.AsyncTask, conditions ...Condition) (int, error) {
   191  	var v int
   192  	var err error
   193  	s.mu.RLock()
   194  	lastAddress := s.mu.lastAddress
   195  	if s.mu.store == nil {
   196  		err = ErrNotReady
   197  	} else {
   198  		v, err = s.mu.store.UpdateAsyncTask(ctx, tasks, conditions...)
   199  	}
   200  	s.mu.RUnlock()
   201  	if err != nil {
   202  		s.maybeRefresh(lastAddress)
   203  	}
   204  	return v, err
   205  }
   206  
   207  func (s *refreshableTaskStorage) DeleteAsyncTask(ctx context.Context, conditions ...Condition) (int, error) {
   208  	var v int
   209  	var err error
   210  	s.mu.RLock()
   211  	lastAddress := s.mu.lastAddress
   212  	if s.mu.store == nil {
   213  		err = ErrNotReady
   214  	} else {
   215  		v, err = s.mu.store.DeleteAsyncTask(ctx, conditions...)
   216  	}
   217  	s.mu.RUnlock()
   218  	if err != nil {
   219  		s.maybeRefresh(lastAddress)
   220  	}
   221  	return v, err
   222  }
   223  
   224  func (s *refreshableTaskStorage) QueryAsyncTask(ctx context.Context, conditions ...Condition) ([]task.AsyncTask, error) {
   225  	var v []task.AsyncTask
   226  	var err error
   227  	s.mu.RLock()
   228  	lastAddress := s.mu.lastAddress
   229  	if s.mu.store == nil {
   230  		err = ErrNotReady
   231  	} else {
   232  		v, err = s.mu.store.QueryAsyncTask(ctx, conditions...)
   233  	}
   234  	s.mu.RUnlock()
   235  	if err != nil {
   236  		s.maybeRefresh(lastAddress)
   237  	}
   238  	return v, err
   239  }
   240  
   241  func (s *refreshableTaskStorage) AddCronTask(ctx context.Context, tasks ...task.CronTask) (int, error) {
   242  	var v int
   243  	var err error
   244  	s.mu.RLock()
   245  	lastAddress := s.mu.lastAddress
   246  	if s.mu.store == nil {
   247  		err = ErrNotReady
   248  	} else {
   249  		v, err = s.mu.store.AddCronTask(ctx, tasks...)
   250  	}
   251  	s.mu.RUnlock()
   252  	if err != nil {
   253  		s.maybeRefresh(lastAddress)
   254  	}
   255  	return v, err
   256  }
   257  
   258  func (s *refreshableTaskStorage) QueryCronTask(ctx context.Context, c ...Condition) ([]task.CronTask, error) {
   259  	var v []task.CronTask
   260  	var err error
   261  	s.mu.RLock()
   262  	lastAddress := s.mu.lastAddress
   263  	if s.mu.store == nil {
   264  		err = ErrNotReady
   265  	} else {
   266  		v, err = s.mu.store.QueryCronTask(ctx, c...)
   267  	}
   268  	s.mu.RUnlock()
   269  	if err != nil {
   270  		s.maybeRefresh(lastAddress)
   271  	}
   272  	return v, err
   273  }
   274  
   275  func (s *refreshableTaskStorage) UpdateCronTask(ctx context.Context, cronTask task.CronTask, task task.AsyncTask) (int, error) {
   276  	var v int
   277  	var err error
   278  	s.mu.RLock()
   279  	lastAddress := s.mu.lastAddress
   280  	if s.mu.store == nil {
   281  		err = ErrNotReady
   282  	} else {
   283  		v, err = s.mu.store.UpdateCronTask(ctx, cronTask, task)
   284  	}
   285  	s.mu.RUnlock()
   286  	if err != nil {
   287  		s.maybeRefresh(lastAddress)
   288  	}
   289  	return v, err
   290  }
   291  
   292  func (s *refreshableTaskStorage) AddDaemonTask(ctx context.Context, tasks ...task.DaemonTask) (int, error) {
   293  	var v int
   294  	var err error
   295  	s.mu.RLock()
   296  	lastAddress := s.mu.lastAddress
   297  	if s.mu.store == nil {
   298  		err = ErrNotReady
   299  	} else {
   300  		v, err = s.mu.store.AddDaemonTask(ctx, tasks...)
   301  	}
   302  	s.mu.RUnlock()
   303  	if err != nil {
   304  		s.maybeRefresh(lastAddress)
   305  	}
   306  	return v, err
   307  }
   308  
   309  func (s *refreshableTaskStorage) UpdateDaemonTask(ctx context.Context, tasks []task.DaemonTask, conditions ...Condition) (int, error) {
   310  	var v int
   311  	var err error
   312  	s.mu.RLock()
   313  	lastAddress := s.mu.lastAddress
   314  	if s.mu.store == nil {
   315  		err = ErrNotReady
   316  	} else {
   317  		v, err = s.mu.store.UpdateDaemonTask(ctx, tasks, conditions...)
   318  	}
   319  	s.mu.RUnlock()
   320  	if err != nil {
   321  		s.maybeRefresh(lastAddress)
   322  	}
   323  	return v, err
   324  }
   325  
   326  func (s *refreshableTaskStorage) DeleteDaemonTask(ctx context.Context, conditions ...Condition) (int, error) {
   327  	var v int
   328  	var err error
   329  	s.mu.RLock()
   330  	lastAddress := s.mu.lastAddress
   331  	if s.mu.store == nil {
   332  		err = ErrNotReady
   333  	} else {
   334  		v, err = s.mu.store.DeleteDaemonTask(ctx, conditions...)
   335  	}
   336  	s.mu.RUnlock()
   337  	if err != nil {
   338  		s.maybeRefresh(lastAddress)
   339  	}
   340  	return v, err
   341  }
   342  
   343  func (s *refreshableTaskStorage) QueryDaemonTask(ctx context.Context, conditions ...Condition) ([]task.DaemonTask, error) {
   344  	var v []task.DaemonTask
   345  	var err error
   346  	s.mu.RLock()
   347  	lastAddress := s.mu.lastAddress
   348  	if s.mu.store == nil {
   349  		err = ErrNotReady
   350  	} else {
   351  		v, err = s.mu.store.QueryDaemonTask(ctx, conditions...)
   352  	}
   353  	s.mu.RUnlock()
   354  	if err != nil {
   355  		s.maybeRefresh(lastAddress)
   356  	}
   357  	return v, err
   358  }
   359  
   360  func (s *refreshableTaskStorage) HeartbeatDaemonTask(ctx context.Context, tasks []task.DaemonTask) (int, error) {
   361  	var v int
   362  	var err error
   363  	s.mu.RLock()
   364  	lastAddress := s.mu.lastAddress
   365  	if s.mu.store == nil {
   366  		err = ErrNotReady
   367  	} else {
   368  		v, err = s.mu.store.HeartbeatDaemonTask(ctx, tasks)
   369  	}
   370  	s.mu.RUnlock()
   371  	if err != nil {
   372  		s.maybeRefresh(lastAddress)
   373  	}
   374  	return v, err
   375  }
   376  
   377  func (s *refreshableTaskStorage) maybeRefresh(lastAddress string) bool {
   378  	s.mu.Lock()
   379  	defer s.mu.Unlock()
   380  	if s.mu.closed {
   381  		return false
   382  	}
   383  
   384  	select {
   385  	case s.refreshC <- lastAddress:
   386  		return true
   387  	default:
   388  		return false
   389  	}
   390  }
   391  
   392  func (s *refreshableTaskStorage) refreshTask(ctx context.Context) {
   393  	defer s.rt.Logger().LogAction("close refresh-task",
   394  		log.DefaultLogOptions().WithLevel(zap.DebugLevel))()
   395  
   396  	for {
   397  		select {
   398  		case <-ctx.Done():
   399  			return
   400  		case lastAddress := <-s.refreshC:
   401  			s.refresh(ctx, lastAddress)
   402  			// see pkg/logservice/service_commands.go#132
   403  			select {
   404  			case <-ctx.Done():
   405  				return
   406  			default:
   407  			}
   408  		}
   409  	}
   410  }
   411  
   412  func (s *refreshableTaskStorage) refresh(ctx context.Context, lastAddress string) {
   413  	s.mu.Lock()
   414  	defer s.mu.Unlock()
   415  
   416  	if s.mu.store != nil {
   417  		_ = s.mu.store.Close()
   418  	}
   419  
   420  	if s.mu.closed {
   421  		return
   422  	}
   423  	if lastAddress != "" && lastAddress != s.mu.lastAddress {
   424  		return
   425  	}
   426  	connectAddress, err := s.addressFactory(ctx, true)
   427  	if err != nil {
   428  		s.rt.Logger().Error("failed to refresh task storage",
   429  			zap.Error(err))
   430  		return
   431  	}
   432  
   433  	s.mu.lastAddress = connectAddress
   434  	s.rt.Logger().Debug("trying to refresh task storage", zap.String("address", connectAddress))
   435  	store, err := s.storeFactory.Create(connectAddress)
   436  	if err != nil {
   437  		s.rt.Logger().Error("failed to refresh task storage",
   438  			zap.String("address", connectAddress),
   439  			zap.Error(err))
   440  		return
   441  	}
   442  	s.mu.store = store
   443  	s.rt.Logger().Debug("refresh task storage completed", zap.String("sql-address", connectAddress))
   444  }
   445  
   446  type mysqlBasedStorageFactory struct {
   447  	username string
   448  	password string
   449  	database string
   450  }
   451  
   452  // NewMySQLBasedTaskStorageFactory creates a mysql based task storage factory using the special username, password and database
   453  func NewMySQLBasedTaskStorageFactory(username, password, database string) TaskStorageFactory {
   454  	return &mysqlBasedStorageFactory{
   455  		username: username,
   456  		password: password,
   457  		database: database,
   458  	}
   459  }
   460  
   461  func (f *mysqlBasedStorageFactory) Create(address string) (TaskStorage, error) {
   462  	dsn := fmt.Sprintf("%s:%s@tcp(%s)/?readTimeout=15s&writeTimeout=15s&timeout=15s&parseTime=true&loc=Local",
   463  		f.username,
   464  		f.password,
   465  		address)
   466  	return NewMysqlTaskStorage(dsn, f.database)
   467  }
   468  
   469  type fixedTaskStorageFactory struct {
   470  	store TaskStorage
   471  }
   472  
   473  // NewFixedTaskStorageFactory creates a fixed task storage factory which always returns the special taskstorage
   474  func NewFixedTaskStorageFactory(store TaskStorage) TaskStorageFactory {
   475  	return &fixedTaskStorageFactory{
   476  		store: store,
   477  	}
   478  }
   479  
   480  func (f *fixedTaskStorageFactory) Create(address string) (TaskStorage, error) {
   481  	return f.store, nil
   482  }