github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/framework/internal/master/worker_creator.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package master
    15  
    16  import (
    17  	"context"
    18  	"time"
    19  
    20  	pb "github.com/pingcap/tiflow/engine/enginepb"
    21  	frameModel "github.com/pingcap/tiflow/engine/framework/model"
    22  	"github.com/pingcap/tiflow/engine/model"
    23  	"github.com/pingcap/tiflow/engine/pkg/client"
    24  	resModel "github.com/pingcap/tiflow/engine/pkg/externalresource/model"
    25  	pkgOrm "github.com/pingcap/tiflow/engine/pkg/orm"
    26  	"github.com/pingcap/tiflow/engine/pkg/tenant"
    27  	schedModel "github.com/pingcap/tiflow/engine/servermaster/scheduler/model"
    28  	"github.com/pingcap/tiflow/pkg/errors"
    29  	"github.com/pingcap/tiflow/pkg/label"
    30  	"go.uber.org/zap"
    31  )
    32  
    33  const genEpochTimeout = 5 * time.Second
    34  
    35  type createWorkerOpts struct {
    36  	Resources []resModel.ResourceID
    37  	Selectors []*label.Selector
    38  }
    39  
    40  // CreateWorkerOpt represents an option of creating a worker.
    41  type CreateWorkerOpt func(opts *createWorkerOpts)
    42  
    43  // CreateWorkerWithResourceRequirements specifies the resource requirement of a worker.
    44  func CreateWorkerWithResourceRequirements(resources ...resModel.ResourceID) CreateWorkerOpt {
    45  	return func(opts *createWorkerOpts) {
    46  		opts.Resources = append(opts.Resources, resources...)
    47  	}
    48  }
    49  
    50  // CreateWorkerWithSelectors specifies the selectors used to dispatch the worker.
    51  func CreateWorkerWithSelectors(selectors ...*label.Selector) CreateWorkerOpt {
    52  	return func(opts *createWorkerOpts) {
    53  		opts.Selectors = append(opts.Selectors, selectors...)
    54  	}
    55  }
    56  
    57  // StartWorkerCallbackType is the type for a callback that's called before the
    58  // executor launches the worker. It is useful in the 2-phase task submission process.
    59  type StartWorkerCallbackType = func(
    60  	workerID frameModel.WorkerID,
    61  	executorID model.ExecutorID,
    62  	epoch frameModel.Epoch,
    63  )
    64  
    65  // WorkerCreationHooks contains hooks to be called at specifc points of the
    66  // worker creation process.
    67  type WorkerCreationHooks struct {
    68  	// BeforeStartingWorker will be called AFTER the executor has received the task
    69  	// but BEFORE the worker is actually started.
    70  	BeforeStartingWorker StartWorkerCallbackType
    71  }
    72  
    73  // WorkerCreator implements the worker creation logic.
    74  type WorkerCreator struct {
    75  	masterID           frameModel.MasterID
    76  	executorGroup      client.ExecutorGroup
    77  	serverMasterClient client.ServerMasterClient
    78  	frameMetaClient    pkgOrm.Client
    79  	logger             *zap.Logger
    80  	hook               *WorkerCreationHooks
    81  
    82  	inheritedSelectors []*label.Selector
    83  }
    84  
    85  // CreateWorker creates a worker synchronously.
    86  func (c *WorkerCreator) CreateWorker(
    87  	ctx context.Context,
    88  	projectInfo tenant.ProjectInfo,
    89  	workerType frameModel.WorkerType,
    90  	workerID frameModel.WorkerID,
    91  	rawConfig []byte,
    92  	opts ...CreateWorkerOpt,
    93  ) error {
    94  	options := new(createWorkerOpts)
    95  	for _, op := range opts {
    96  		op(options)
    97  	}
    98  
    99  	req, err := c.buildScheduleTaskRequest(c.masterID, workerID, options)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	resp, err := c.serverMasterClient.ScheduleTask(ctx, req)
   105  	if err != nil {
   106  		c.logger.Warn("ScheduleTask returned error", zap.Error(err))
   107  		return err
   108  	}
   109  	c.logger.Info("ScheduleTask succeeded", zap.Any("response", resp))
   110  
   111  	executorID := model.ExecutorID(resp.ExecutorId)
   112  	executorClient, err := c.executorGroup.GetExecutorClientB(ctx, executorID)
   113  	if err != nil {
   114  		return errors.Annotate(err, "CreateWorker")
   115  	}
   116  
   117  	genEpochCtx, cancel := context.WithTimeout(ctx, genEpochTimeout)
   118  	defer cancel()
   119  	epoch, err := c.frameMetaClient.GenEpoch(genEpochCtx)
   120  	if err != nil {
   121  		return errors.Annotate(err, "CreateWorker")
   122  	}
   123  
   124  	dispatchArgs := &client.DispatchTaskArgs{
   125  		ProjectInfo:  projectInfo,
   126  		WorkerID:     workerID,
   127  		MasterID:     c.masterID,
   128  		WorkerType:   int64(workerType),
   129  		WorkerConfig: rawConfig,
   130  		WorkerEpoch:  epoch,
   131  	}
   132  
   133  	err = executorClient.DispatchTask(ctx, dispatchArgs, func() {
   134  		c.hook.BeforeStartingWorker(workerID, executorID, epoch)
   135  	})
   136  
   137  	if err != nil {
   138  		c.logger.Info("DispatchTask failed",
   139  			zap.Error(err))
   140  		return err
   141  	}
   142  
   143  	c.logger.Info("Dispatch Worker succeeded",
   144  		zap.Any("args", dispatchArgs))
   145  	return nil
   146  }
   147  
   148  func (c *WorkerCreator) buildScheduleTaskRequest(
   149  	masterID frameModel.MasterID,
   150  	workerID frameModel.WorkerID,
   151  	opts *createWorkerOpts,
   152  ) (*pb.ScheduleTaskRequest, error) {
   153  	finalSelectors := make([]*label.Selector, 0, len(c.inheritedSelectors)+len(opts.Selectors))
   154  	finalSelectors = append(finalSelectors, c.inheritedSelectors...)
   155  	finalSelectors = append(finalSelectors, opts.Selectors...)
   156  
   157  	selectors, err := toPBSelectors(finalSelectors...)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	return &pb.ScheduleTaskRequest{
   163  		TaskId:    workerID,
   164  		Resources: resModel.ToResourceRequirement(masterID, opts.Resources...),
   165  		Selectors: selectors,
   166  	}, nil
   167  }
   168  
   169  func toPBSelectors(sels ...*label.Selector) ([]*pb.Selector, error) {
   170  	if len(sels) == 0 {
   171  		return nil, nil
   172  	}
   173  
   174  	ret := make([]*pb.Selector, 0, len(sels))
   175  	for _, sel := range sels {
   176  		pbSel, err := schedModel.SelectorToPB(sel)
   177  		if err != nil {
   178  			return nil, err
   179  		}
   180  		ret = append(ret, pbSel)
   181  	}
   182  
   183  	return ret, nil
   184  }
   185  
   186  // WorkerCreatorBuilder is a helper for building a WorkerCreator.
   187  type WorkerCreatorBuilder struct {
   188  	creator *WorkerCreator
   189  }
   190  
   191  // NewWorkerCreatorBuilder returns a new WorkerCreatorBuilder.
   192  func NewWorkerCreatorBuilder() *WorkerCreatorBuilder {
   193  	return &WorkerCreatorBuilder{creator: new(WorkerCreator)}
   194  }
   195  
   196  // WithMasterID specifies the ID of the master that the WorkerCreator belongs to.
   197  func (b *WorkerCreatorBuilder) WithMasterID(masterID frameModel.MasterID) *WorkerCreatorBuilder {
   198  	b.creator.masterID = masterID
   199  	return b
   200  }
   201  
   202  // WithExecutorGroup passes an ExecutorGroup.
   203  func (b *WorkerCreatorBuilder) WithExecutorGroup(executorGroup client.ExecutorGroup) *WorkerCreatorBuilder {
   204  	b.creator.executorGroup = executorGroup
   205  	return b
   206  }
   207  
   208  // WithServerMasterClient passes a ServerMasterClient.
   209  func (b *WorkerCreatorBuilder) WithServerMasterClient(serverMasterClient client.ServerMasterClient) *WorkerCreatorBuilder {
   210  	b.creator.serverMasterClient = serverMasterClient
   211  	return b
   212  }
   213  
   214  // WithFrameMetaClient passes a frameMetaClient.
   215  func (b *WorkerCreatorBuilder) WithFrameMetaClient(frameMetaClient pkgOrm.Client) *WorkerCreatorBuilder {
   216  	b.creator.frameMetaClient = frameMetaClient
   217  	return b
   218  }
   219  
   220  // WithLogger passes a logger.
   221  func (b *WorkerCreatorBuilder) WithLogger(logger *zap.Logger) *WorkerCreatorBuilder {
   222  	b.creator.logger = logger
   223  	return b
   224  }
   225  
   226  // WithHooks passes a WorkerCreationHooks.
   227  func (b *WorkerCreatorBuilder) WithHooks(hooks *WorkerCreationHooks) *WorkerCreatorBuilder {
   228  	b.creator.hook = hooks
   229  	return b
   230  }
   231  
   232  // WithInheritedSelectors passes the selectors that will be inherited from the master.
   233  func (b *WorkerCreatorBuilder) WithInheritedSelectors(selectors ...*label.Selector) *WorkerCreatorBuilder {
   234  	b.creator.inheritedSelectors = selectors
   235  	return b
   236  }
   237  
   238  // Build returns the resulted WorkerCreator.
   239  func (b *WorkerCreatorBuilder) Build() *WorkerCreator {
   240  	return b.creator
   241  }