github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/framework/registry/registry.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 registry 15 16 import ( 17 "reflect" 18 "sync" 19 20 "github.com/pingcap/log" 21 "github.com/pingcap/tiflow/engine/framework" 22 frameModel "github.com/pingcap/tiflow/engine/framework/model" 23 dcontext "github.com/pingcap/tiflow/engine/pkg/context" 24 "github.com/pingcap/tiflow/pkg/errors" 25 "github.com/pingcap/tiflow/pkg/errorutil" 26 "go.uber.org/zap" 27 ) 28 29 // WorkerConfig alias to framework.WorkerConfig 30 type WorkerConfig = framework.WorkerConfig 31 32 // Registry defines an interface to worker as worker register bridge. Business 33 // can register any worker or job master implementation into a registry 34 type Registry interface { 35 MustRegisterWorkerType(tp frameModel.WorkerType, factory WorkerFactory) 36 RegisterWorkerType(tp frameModel.WorkerType, factory WorkerFactory) (ok bool) 37 CreateWorker( 38 ctx *dcontext.Context, 39 tp framework.WorkerType, 40 workerID frameModel.WorkerID, 41 masterID frameModel.MasterID, 42 config []byte, 43 epoch frameModel.Epoch, 44 ) (framework.Worker, error) 45 // IsRetryableError returns whether error is treated as retryable from the 46 // perspective of business logic. 47 IsRetryableError(err error, tp framework.WorkerType) (bool, error) 48 } 49 50 type registryImpl struct { 51 mu sync.RWMutex 52 factoryMap map[frameModel.WorkerType]WorkerFactory 53 } 54 55 // NewRegistry creates a new registryImpl instance 56 func NewRegistry() Registry { 57 return ®istryImpl{ 58 factoryMap: make(map[frameModel.WorkerType]WorkerFactory), 59 } 60 } 61 62 // MustRegisterWorkerType implements Registry.MustRegisterWorkerType 63 func (r *registryImpl) MustRegisterWorkerType(tp frameModel.WorkerType, factory WorkerFactory) { 64 if ok := r.RegisterWorkerType(tp, factory); !ok { 65 log.Panic("duplicate worker type", zap.Stringer("worker-type", tp)) 66 } 67 log.Info("register worker", zap.Stringer("worker-type", tp)) 68 } 69 70 // RegisterWorkerType implements Registry.RegisterWorkerType 71 func (r *registryImpl) RegisterWorkerType(tp frameModel.WorkerType, factory WorkerFactory) (ok bool) { 72 r.mu.Lock() 73 defer r.mu.Unlock() 74 75 if _, exists := r.factoryMap[tp]; exists { 76 return false 77 } 78 r.factoryMap[tp] = factory 79 return true 80 } 81 82 // CreateWorker implements Registry.CreateWorker 83 func (r *registryImpl) CreateWorker( 84 ctx *dcontext.Context, 85 tp framework.WorkerType, 86 workerID frameModel.WorkerID, 87 masterID frameModel.MasterID, 88 configBytes []byte, 89 epoch frameModel.Epoch, 90 ) (framework.Worker, error) { 91 factory, ok := r.getWorkerFactory(tp) 92 if !ok { 93 return nil, errors.ErrWorkerTypeNotFound.GenWithStackByArgs(tp) 94 } 95 96 config, err := factory.DeserializeConfig(configBytes) 97 if err != nil { 98 // dm will connect upstream to adjust some config, so we need check whether 99 // the error is retryable too. 100 err2 := errorutil.ConvertErr(tp, err) 101 if factory.IsRetryableError(err2) { 102 return nil, errors.ErrCreateWorkerNonTerminate.Wrap(err).GenWithStackByArgs() 103 } 104 return nil, errors.ErrCreateWorkerTerminate.Wrap(err).GenWithStackByArgs() 105 } 106 107 impl, err := factory.NewWorkerImpl(ctx, workerID, masterID, config) 108 if err != nil { 109 return nil, errors.Trace(err) 110 } 111 112 if implHasMember(impl, nameOfBaseWorker) { 113 base := framework.NewBaseWorker( 114 ctx, 115 impl, 116 workerID, 117 masterID, 118 tp, 119 epoch, 120 ) 121 setImplMember(impl, nameOfBaseWorker, base) 122 return base, nil 123 } 124 // TODO: should jobmaster record worker status 125 if implHasMember(impl, nameOfBaseJobMaster) { 126 base := framework.NewBaseJobMaster( 127 ctx, 128 impl.(framework.JobMasterImpl), 129 masterID, 130 workerID, 131 tp, 132 epoch, 133 ) 134 setImplMember(impl, nameOfBaseJobMaster, base) 135 return base, nil 136 } 137 log.Panic("wrong use of CreateWorker", 138 zap.String("reason", "impl has no member BaseWorker or BaseJobMaster"), 139 zap.Any("workerType", tp)) 140 return nil, nil 141 } 142 143 // IsRetryableError checks whether an error is retryable in business logic 144 func (r *registryImpl) IsRetryableError(err error, tp frameModel.WorkerType) (bool, error) { 145 factory, ok := r.getWorkerFactory(tp) 146 if !ok { 147 return false, errors.ErrWorkerTypeNotFound.GenWithStackByArgs(tp) 148 } 149 return factory.IsRetryableError(err), nil 150 } 151 152 func (r *registryImpl) getWorkerFactory(tp frameModel.WorkerType) (factory WorkerFactory, ok bool) { 153 r.mu.RLock() 154 defer r.mu.RUnlock() 155 156 factory, ok = r.factoryMap[tp] 157 return 158 } 159 160 var ( 161 nameOfBaseWorker = getTypeNameOfVarPtr(new(framework.BaseWorker)) 162 nameOfBaseJobMaster = getTypeNameOfVarPtr(new(framework.BaseJobMaster)) 163 ) 164 165 func getTypeNameOfVarPtr(v interface{}) string { 166 return reflect.TypeOf(v).Elem().Name() 167 } 168 169 func implHasMember(impl interface{}, memberName string) bool { 170 defer func() { 171 if v := recover(); v != nil { 172 log.Panic("wrong use of implHasMember", 173 zap.Any("reason", v)) 174 } 175 }() 176 return reflect.ValueOf(impl).Elem().FieldByName(memberName) != reflect.Value{} 177 } 178 179 func setImplMember(impl interface{}, memberName string, value interface{}) { 180 defer func() { 181 if v := recover(); v != nil { 182 log.Panic("wrong use of setImplMember", 183 zap.Any("reason", v)) 184 } 185 }() 186 reflect.ValueOf(impl).Elem().FieldByName(memberName).Set(reflect.ValueOf(value)) 187 }