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 &registryImpl{
    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  }