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 }