github.com/matrixorigin/matrixone@v1.2.0/pkg/cnservice/server_task.go (about)

     1  // Copyright 2021 - 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 cnservice
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    24  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    25  	"github.com/matrixorigin/matrixone/pkg/config"
    26  	"github.com/matrixorigin/matrixone/pkg/frontend"
    27  	"github.com/matrixorigin/matrixone/pkg/logutil"
    28  	"github.com/matrixorigin/matrixone/pkg/objectio"
    29  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    30  	logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    31  	"github.com/matrixorigin/matrixone/pkg/pb/task"
    32  	moconnector "github.com/matrixorigin/matrixone/pkg/stream/connector"
    33  	"github.com/matrixorigin/matrixone/pkg/taskservice"
    34  	"github.com/matrixorigin/matrixone/pkg/util"
    35  	"github.com/matrixorigin/matrixone/pkg/util/executor"
    36  	"github.com/matrixorigin/matrixone/pkg/util/export"
    37  	db_holder "github.com/matrixorigin/matrixone/pkg/util/export/etl/db"
    38  	ie "github.com/matrixorigin/matrixone/pkg/util/internalExecutor"
    39  	"github.com/matrixorigin/matrixone/pkg/util/metric/mometric"
    40  	"go.uber.org/zap"
    41  )
    42  
    43  func (s *service) adjustSQLAddress() {
    44  	if s.cfg.SQLAddress == "" {
    45  		ip := "127.0.0.1"
    46  		if s.cfg.Frontend.Host != "" &&
    47  			s.cfg.Frontend.Host != "0.0.0.0" {
    48  			ip = s.cfg.Frontend.Host
    49  		}
    50  
    51  		s.cfg.SQLAddress = fmt.Sprintf("%s:%d",
    52  			ip,
    53  			s.cfg.Frontend.Port)
    54  	}
    55  }
    56  
    57  func (s *service) initTaskServiceHolder() {
    58  	s.adjustSQLAddress()
    59  
    60  	getClient := func() util.HAKeeperClient {
    61  		client, _ := s.getHAKeeperClient()
    62  		return client
    63  	}
    64  	s.task.Lock()
    65  	defer s.task.Unlock()
    66  	if s.task.storageFactory == nil {
    67  		s.task.holder = taskservice.NewTaskServiceHolder(
    68  			runtime.ProcessLevelRuntime(),
    69  			util.AddressFunc(getClient))
    70  	} else {
    71  		s.task.holder = taskservice.NewTaskServiceHolderWithTaskStorageFactorySelector(
    72  			runtime.ProcessLevelRuntime(),
    73  			util.AddressFunc(getClient),
    74  			func(_, _, _ string) taskservice.TaskStorageFactory {
    75  				return s.task.storageFactory
    76  			})
    77  	}
    78  }
    79  
    80  func (s *service) createTaskService(command *logservicepb.CreateTaskService) {
    81  	// Notify frontend to setup the special account used to task framework create and query async tasks.
    82  	// The account is always in the memory.
    83  	frontend.SetSpecialUser(command.User.Username, []byte(command.User.Password))
    84  
    85  	if err := s.task.holder.Create(*command); err != nil {
    86  		s.logger.Error("create task service failed", zap.Error(err))
    87  		return
    88  	}
    89  	s.startTaskRunner()
    90  
    91  	ts, ok := s.task.holder.Get()
    92  	if !ok {
    93  		panic("no task service is initialized")
    94  	}
    95  	s.pu.TaskService = ts
    96  }
    97  
    98  func (s *service) initSqlWriterFactory() {
    99  	getClient := func() util.HAKeeperClient {
   100  		client, _ := s.getHAKeeperClient()
   101  		return client
   102  	}
   103  	db_holder.SetSQLWriterDBAddressFunc(util.AddressFunc(getClient))
   104  }
   105  
   106  func (s *service) createSQLLogger(command *logservicepb.CreateTaskService) {
   107  	frontend.SetSpecialUser(db_holder.MOLoggerUser, []byte(command.User.Password))
   108  	db_holder.SetSQLWriterDBUser(db_holder.MOLoggerUser, command.User.Password)
   109  }
   110  
   111  func (s *service) canClaimDaemonTask(taskAccount string) bool {
   112  	const accountKey = "account"
   113  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
   114  	defer cancel()
   115  
   116  	state, err := s._hakeeperClient.GetClusterState(ctx)
   117  	if err != nil {
   118  		return false
   119  	}
   120  	stores := state.CNState.Stores
   121  
   122  	info, ok := stores[s.cfg.UUID]
   123  	// 1. Cannot find current CN service UUID in cluster state.
   124  	if !ok {
   125  		return false
   126  	}
   127  
   128  	// We assume that the runner is a shard runner.
   129  	localShared := true
   130  
   131  	// 2. If the current runner has the same account info, return true.
   132  	for _, account := range info.Labels[accountKey].Labels {
   133  		if account != "" {
   134  			// This CN node has account, so it is not a shared one.
   135  			localShared = false
   136  		}
   137  		if strings.EqualFold(account, taskAccount) {
   138  			return true
   139  		}
   140  	}
   141  
   142  	isSysTask := strings.EqualFold(taskAccount, frontend.GetDefaultTenant())
   143  
   144  	var taskHasRunner bool
   145  	for _, store := range stores {
   146  		for key, labelInfo := range store.Labels {
   147  			if strings.EqualFold(accountKey, key) {
   148  				for _, label := range labelInfo.Labels {
   149  					if strings.EqualFold(label, taskAccount) {
   150  						taskHasRunner = true
   151  					}
   152  				}
   153  			}
   154  		}
   155  	}
   156  
   157  	// 3. If there are no other runners for this task, and local runner is a shared one or the
   158  	// task is belongs to sys account, we could run it.
   159  	if !taskHasRunner && (localShared || isSysTask) {
   160  		return true
   161  	}
   162  
   163  	// 4. Otherwise, we could not run this task.
   164  	return false
   165  }
   166  
   167  func (s *service) startTaskRunner() {
   168  	s.task.Lock()
   169  	defer s.task.Unlock()
   170  
   171  	if s.task.runner != nil {
   172  		return
   173  	}
   174  
   175  	ts, ok := s.task.holder.Get()
   176  	if !ok {
   177  		panic("task service must created")
   178  	}
   179  
   180  	s.task.runner = taskservice.NewTaskRunner(s.cfg.UUID,
   181  		ts,
   182  		s.canClaimDaemonTask,
   183  		taskservice.WithRunnerLogger(s.logger),
   184  		taskservice.WithOptions(
   185  			s.cfg.TaskRunner.QueryLimit,
   186  			s.cfg.TaskRunner.Parallelism,
   187  			s.cfg.TaskRunner.MaxWaitTasks,
   188  			s.cfg.TaskRunner.FetchInterval.Duration,
   189  			s.cfg.TaskRunner.FetchTimeout.Duration,
   190  			s.cfg.TaskRunner.RetryInterval.Duration,
   191  			s.cfg.TaskRunner.HeartbeatInterval.Duration,
   192  			s.cfg.TaskRunner.HeartbeatTimeout.Duration,
   193  		),
   194  	)
   195  
   196  	s.registerExecutorsLocked()
   197  	if err := s.task.runner.Start(); err != nil {
   198  		s.logger.Error("start task runner failed",
   199  			zap.Error(err))
   200  	}
   201  }
   202  
   203  func (s *service) GetTaskRunner() taskservice.TaskRunner {
   204  	s.task.RLock()
   205  	defer s.task.RUnlock()
   206  	return s.task.runner
   207  }
   208  
   209  func (s *service) GetTaskService() (taskservice.TaskService, bool) {
   210  	s.task.RLock()
   211  	defer s.task.RUnlock()
   212  	return s.task.holder.Get()
   213  }
   214  
   215  func (s *service) stopTask() error {
   216  	defer logutil.LogClose(s.logger, "cnservice/task")()
   217  
   218  	s.task.Lock()
   219  	defer s.task.Unlock()
   220  	if err := s.task.holder.Close(); err != nil {
   221  		return err
   222  	}
   223  	if s.task.runner != nil {
   224  		return s.task.runner.Stop()
   225  	}
   226  	return nil
   227  }
   228  
   229  func (s *service) registerExecutorsLocked() {
   230  	if s.task.runner == nil {
   231  		return
   232  	}
   233  
   234  	pu := config.NewParameterUnit(
   235  		&s.cfg.Frontend,
   236  		nil,
   237  		nil,
   238  		nil)
   239  	pu.StorageEngine = s.storeEngine
   240  	pu.TxnClient = s._txnClient
   241  	s.cfg.Frontend.SetDefaultValues()
   242  	pu.FileService = s.fileService
   243  	pu.LockService = s.lockService
   244  	ieFactory := func() ie.InternalExecutor {
   245  		return frontend.NewInternalExecutor()
   246  	}
   247  
   248  	ts, ok := s.task.holder.Get()
   249  	if !ok {
   250  		panic(moerr.NewInternalErrorNoCtx("task Service not ok"))
   251  	}
   252  
   253  	// init metric/log merge task executor
   254  	s.task.runner.RegisterExecutor(task.TaskCode_MetricLogMerge,
   255  		export.MergeTaskExecutorFactory(export.WithFileService(s.etlFS)))
   256  	// init metric task
   257  	s.task.runner.RegisterExecutor(task.TaskCode_MetricStorageUsage,
   258  		mometric.GetMetricStorageUsageExecutor(ieFactory))
   259  	// streaming connector task
   260  	s.task.runner.RegisterExecutor(task.TaskCode_ConnectorKafkaSink,
   261  		moconnector.KafkaSinkConnectorExecutor(s.logger, ts, ieFactory, s.task.runner.Attach))
   262  	s.task.runner.RegisterExecutor(task.TaskCode_MergeObject,
   263  		func(ctx context.Context, task task.Task) error {
   264  			metadata := task.GetMetadata()
   265  			var mergeTask api.MergeTaskEntry
   266  			err := mergeTask.Unmarshal(metadata.Context)
   267  			if err != nil {
   268  				return err
   269  			}
   270  
   271  			objs := make([]string, len(mergeTask.ToMergeObjs))
   272  			for i, b := range mergeTask.ToMergeObjs {
   273  				stats := objectio.ObjectStats(b)
   274  				objs[i] = stats.ObjectName().String()
   275  			}
   276  			sql := fmt.Sprintf("select mo_ctl('DN', 'MERGEOBJECTS', '%s.%s:%s')",
   277  				mergeTask.DbName, mergeTask.TableName, strings.Join(objs, ","))
   278  			ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
   279  			defer cancel()
   280  			opts := executor.Options{}.WithAccountID(mergeTask.AccountId).WithWaitCommittedLogApplied()
   281  			_, err = s.sqlExecutor.Exec(ctx, sql, opts)
   282  			return err
   283  		},
   284  	)
   285  }