github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/worker/relay.go (about)

     1  // Copyright 2019 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 worker
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  
    20  	"github.com/pingcap/errors"
    21  	"github.com/pingcap/tiflow/dm/config"
    22  	"github.com/pingcap/tiflow/dm/pb"
    23  	"github.com/pingcap/tiflow/dm/pkg/binlog"
    24  	"github.com/pingcap/tiflow/dm/pkg/log"
    25  	"github.com/pingcap/tiflow/dm/pkg/streamer"
    26  	"github.com/pingcap/tiflow/dm/pkg/terror"
    27  	"github.com/pingcap/tiflow/dm/relay"
    28  	"go.uber.org/atomic"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  // RelayHolder for relay unit.
    33  type RelayHolder interface {
    34  	// Init initializes the holder
    35  	Init(ctx context.Context, interceptors []relay.PurgeInterceptor) (relay.Purger, error)
    36  	// Start starts run the relay
    37  	Start()
    38  	// Close closes the holder
    39  	Close()
    40  	// Status returns relay unit's status
    41  	Status(sourceStatus *binlog.SourceStatus) *pb.RelayStatus
    42  	// Stage returns the stage of the relay
    43  	Stage() pb.Stage
    44  	// Error returns relay unit's status
    45  	Error() *pb.RelayError
    46  	// Operate operates relay unit
    47  	Operate(ctx context.Context, op pb.RelayOp) error
    48  	// Result returns the result of the relay
    49  	Result() *pb.ProcessResult
    50  	// Update updates relay config online
    51  	Update(ctx context.Context, cfg *config.SourceConfig) error
    52  	// Relay returns relay object
    53  	Relay() relay.Process
    54  }
    55  
    56  // NewRelayHolder is relay holder initializer
    57  // it can be used for testing.
    58  var NewRelayHolder = NewRealRelayHolder
    59  
    60  // realRelayHolder used to hold the relay unit.
    61  type realRelayHolder struct {
    62  	sync.RWMutex
    63  	wg sync.WaitGroup
    64  
    65  	relay relay.Process
    66  	cfg   *config.SourceConfig
    67  
    68  	ctx    context.Context
    69  	cancel context.CancelFunc
    70  
    71  	l log.Logger
    72  
    73  	closed atomic.Bool
    74  	stage  pb.Stage
    75  	result *pb.ProcessResult // the process result, nil when is processing
    76  }
    77  
    78  // NewRealRelayHolder creates a new RelayHolder.
    79  func NewRealRelayHolder(sourceCfg *config.SourceConfig) RelayHolder {
    80  	cfg := relay.FromSourceCfg(sourceCfg)
    81  
    82  	h := &realRelayHolder{
    83  		cfg:   sourceCfg,
    84  		stage: pb.Stage_New,
    85  		relay: relay.NewRelay(cfg),
    86  		l:     log.With(zap.String("component", "relay holder")),
    87  	}
    88  	h.closed.Store(true)
    89  	return h
    90  }
    91  
    92  // Init initializes the holder.
    93  func (h *realRelayHolder) Init(ctx context.Context, interceptors []relay.PurgeInterceptor) (relay.Purger, error) {
    94  	h.closed.Store(false)
    95  
    96  	// initial relay purger
    97  	operators := []relay.Operator{
    98  		h,
    99  		streamer.GetReaderHub(),
   100  	}
   101  
   102  	if err := h.relay.Init(ctx); err != nil {
   103  		return nil, terror.Annotate(err, "initial relay unit")
   104  	}
   105  
   106  	return relay.NewPurger(h.cfg.Purge, h.cfg.RelayDir, operators, interceptors), nil
   107  }
   108  
   109  // Start starts run the relay.
   110  func (h *realRelayHolder) Start() {
   111  	h.wg.Add(1)
   112  	go func() {
   113  		defer h.wg.Done()
   114  		h.run()
   115  	}()
   116  }
   117  
   118  // Close closes the holder.
   119  func (h *realRelayHolder) Close() {
   120  	if !h.closed.CAS(false, true) {
   121  		return
   122  	}
   123  
   124  	if h.cancel != nil {
   125  		h.cancel()
   126  	}
   127  	h.wg.Wait() // wait process return
   128  
   129  	h.relay.Close()
   130  }
   131  
   132  func (h *realRelayHolder) run() {
   133  	if !h.setStageIfNot(pb.Stage_Running, pb.Stage_Running) {
   134  		return
   135  	}
   136  	h.ctx, h.cancel = context.WithCancel(context.Background())
   137  	h.setResult(nil) // clear previous result
   138  
   139  	r := h.relay.Process(h.ctx)
   140  	h.cancel()
   141  
   142  	h.setResult(&r)
   143  	for _, err := range r.Errors {
   144  		h.l.Error("process error", zap.Stringer("type", err))
   145  	}
   146  
   147  	h.setStageIfNot(pb.Stage_Stopped, pb.Stage_Paused)
   148  }
   149  
   150  // Status returns relay unit's status.
   151  func (h *realRelayHolder) Status(sourceStatus *binlog.SourceStatus) *pb.RelayStatus {
   152  	if h.closed.Load() || h.relay.IsClosed() {
   153  		return &pb.RelayStatus{
   154  			Stage: pb.Stage_Stopped,
   155  		}
   156  	}
   157  
   158  	s := h.relay.Status(sourceStatus).(*pb.RelayStatus)
   159  	s.Stage = h.Stage()
   160  	s.Result = h.Result()
   161  
   162  	return s
   163  }
   164  
   165  // Error returns relay unit's status.
   166  func (h *realRelayHolder) Error() *pb.RelayError {
   167  	if h.closed.Load() || h.relay.IsClosed() {
   168  		return &pb.RelayError{
   169  			Msg: "relay stopped",
   170  		}
   171  	}
   172  
   173  	s := h.relay.Error().(*pb.RelayError)
   174  	return s
   175  }
   176  
   177  // Operate operates relay unit.
   178  func (h *realRelayHolder) Operate(ctx context.Context, op pb.RelayOp) error {
   179  	switch op {
   180  	case pb.RelayOp_PauseRelay:
   181  		return h.pauseRelay(ctx, op)
   182  	case pb.RelayOp_ResumeRelay:
   183  		return h.resumeRelay(ctx, op)
   184  	case pb.RelayOp_StopRelay:
   185  		return h.stopRelay(ctx, op)
   186  	}
   187  	return terror.ErrWorkerRelayOperNotSupport.Generate(op.String())
   188  }
   189  
   190  func (h *realRelayHolder) pauseRelay(_ context.Context, op pb.RelayOp) error {
   191  	h.Lock()
   192  	if h.stage != pb.Stage_Running {
   193  		h.Unlock()
   194  		return terror.ErrWorkerRelayStageNotValid.Generate(h.stage, pb.Stage_Running, op)
   195  	}
   196  	h.stage = pb.Stage_Paused
   197  
   198  	if h.cancel != nil {
   199  		h.cancel()
   200  	}
   201  	h.Unlock()  // unlock to make `run` can return
   202  	h.wg.Wait() // wait process return
   203  
   204  	h.relay.Pause()
   205  
   206  	return nil
   207  }
   208  
   209  func (h *realRelayHolder) resumeRelay(_ context.Context, op pb.RelayOp) error {
   210  	h.Lock()
   211  	defer h.Unlock()
   212  	if h.stage != pb.Stage_Paused {
   213  		return terror.ErrWorkerRelayStageNotValid.Generate(h.stage, pb.Stage_Paused, op)
   214  	}
   215  
   216  	h.wg.Add(1)
   217  	go func() {
   218  		defer h.wg.Done()
   219  		h.run()
   220  	}()
   221  	return nil
   222  }
   223  
   224  func (h *realRelayHolder) stopRelay(_ context.Context, op pb.RelayOp) error {
   225  	h.Lock()
   226  	if h.stage == pb.Stage_Stopped {
   227  		h.Unlock()
   228  		return terror.ErrWorkerRelayStageNotValid.Generatef("current stage is already stopped not valid, relayop %s", op)
   229  	}
   230  	h.stage = pb.Stage_Stopped
   231  	h.Unlock() // unlock to make `run` can return
   232  
   233  	// now, when try to stop relay unit, we close relay holder
   234  	h.Close()
   235  	return nil
   236  }
   237  
   238  // Stage returns the stage of the relay.
   239  func (h *realRelayHolder) Stage() pb.Stage {
   240  	h.RLock()
   241  	defer h.RUnlock()
   242  	return h.stage
   243  }
   244  
   245  // setStageIfNot sets stage to newStage if its current value is not oldStage, similar to CAS.
   246  func (h *realRelayHolder) setStageIfNot(oldStage, newStage pb.Stage) bool {
   247  	h.Lock()
   248  	defer h.Unlock()
   249  	if h.stage != oldStage {
   250  		h.stage = newStage
   251  		return true
   252  	}
   253  	return false
   254  }
   255  
   256  func (h *realRelayHolder) setResult(result *pb.ProcessResult) {
   257  	h.Lock()
   258  	defer h.Unlock()
   259  	if result == nil {
   260  		h.result = nil
   261  	} else {
   262  		clone := *result
   263  		h.result = &clone
   264  	}
   265  }
   266  
   267  // Result returns the result of the relay
   268  // Note this method will omit the `Error` field in `pb.ProcessError`, so no duplicated
   269  // error message information will be displayed in `query-status`, as the `Msg` field
   270  // contains enough error information.
   271  func (h *realRelayHolder) Result() *pb.ProcessResult {
   272  	h.RLock()
   273  	defer h.RUnlock()
   274  	return h.result
   275  }
   276  
   277  // Update update relay config online.
   278  func (h *realRelayHolder) Update(ctx context.Context, sourceCfg *config.SourceConfig) error {
   279  	relayCfg := relay.FromSourceCfg(sourceCfg)
   280  
   281  	if stage := h.Stage(); stage == pb.Stage_Paused {
   282  		err := h.relay.Reload(relayCfg)
   283  		if err != nil {
   284  			return err
   285  		}
   286  	} else if stage == pb.Stage_Running {
   287  		err := h.Operate(ctx, pb.RelayOp_PauseRelay)
   288  		if err != nil {
   289  			return err
   290  		}
   291  
   292  		err = h.relay.Reload(relayCfg)
   293  		if err != nil {
   294  			return err
   295  		}
   296  
   297  		err = h.Operate(ctx, pb.RelayOp_ResumeRelay)
   298  		if err != nil {
   299  			return err
   300  		}
   301  	}
   302  
   303  	return nil
   304  }
   305  
   306  // EarliestActiveRelayLog implements Operator.EarliestActiveRelayLog.
   307  func (h *realRelayHolder) EarliestActiveRelayLog() *streamer.RelayLogInfo {
   308  	return h.relay.ActiveRelayLog()
   309  }
   310  
   311  func (h *realRelayHolder) Relay() relay.Process {
   312  	return h.relay
   313  }
   314  
   315  /******************** dummy relay holder ********************/
   316  
   317  type dummyRelayHolder struct {
   318  	sync.RWMutex
   319  	initError   error
   320  	stage       pb.Stage
   321  	relayBinlog string
   322  
   323  	cfg    *config.SourceConfig
   324  	relay2 relay.Process
   325  }
   326  
   327  // NewDummyRelayHolder creates a new RelayHolder.
   328  func NewDummyRelayHolder(cfg *config.SourceConfig) RelayHolder {
   329  	return &dummyRelayHolder{
   330  		cfg:    cfg,
   331  		stage:  pb.Stage_New,
   332  		relay2: &relay.Relay{},
   333  	}
   334  }
   335  
   336  // NewDummyRelayHolderWithRelayBinlog creates a new RelayHolder with relayBinlog in relayStatus.
   337  func NewDummyRelayHolderWithRelayBinlog(cfg *config.SourceConfig, relayBinlog string) RelayHolder {
   338  	return &dummyRelayHolder{
   339  		cfg:         cfg,
   340  		relayBinlog: relayBinlog,
   341  	}
   342  }
   343  
   344  // NewDummyRelayHolderWithInitError creates a new RelayHolder with init error.
   345  func NewDummyRelayHolderWithInitError(cfg *config.SourceConfig) RelayHolder {
   346  	return &dummyRelayHolder{
   347  		initError: errors.New("init error"),
   348  		cfg:       cfg,
   349  	}
   350  }
   351  
   352  // Init implements interface of RelayHolder.
   353  func (d *dummyRelayHolder) Init(ctx context.Context, interceptors []relay.PurgeInterceptor) (relay.Purger, error) {
   354  	// initial relay purger
   355  	operators := []relay.Operator{
   356  		d,
   357  	}
   358  
   359  	return relay.NewDummyPurger(d.cfg.Purge, d.cfg.RelayDir, operators, interceptors), d.initError
   360  }
   361  
   362  // Start implements interface of RelayHolder.
   363  func (d *dummyRelayHolder) Start() {
   364  	d.Lock()
   365  	defer d.Unlock()
   366  	d.stage = pb.Stage_Running
   367  }
   368  
   369  // Close implements interface of RelayHolder.
   370  func (d *dummyRelayHolder) Close() {
   371  	d.Lock()
   372  	defer d.Unlock()
   373  	d.stage = pb.Stage_Stopped
   374  }
   375  
   376  // Status implements interface of RelayHolder.
   377  func (d *dummyRelayHolder) Status(sourceStatus *binlog.SourceStatus) *pb.RelayStatus {
   378  	d.Lock()
   379  	defer d.Unlock()
   380  	return &pb.RelayStatus{
   381  		Stage:       d.stage,
   382  		RelayBinlog: d.relayBinlog,
   383  	}
   384  }
   385  
   386  // Error implements interface of RelayHolder.
   387  func (d *dummyRelayHolder) Error() *pb.RelayError {
   388  	return nil
   389  }
   390  
   391  // Operate implements interface of RelayHolder.
   392  func (d *dummyRelayHolder) Operate(ctx context.Context, op pb.RelayOp) error {
   393  	d.Lock()
   394  	defer d.Unlock()
   395  	switch op {
   396  	case pb.RelayOp_PauseRelay:
   397  		if d.stage != pb.Stage_Running {
   398  			return terror.ErrWorkerRelayStageNotValid.Generate(d.stage, pb.Stage_Running, op)
   399  		}
   400  		d.stage = pb.Stage_Paused
   401  	case pb.RelayOp_ResumeRelay:
   402  		if d.stage != pb.Stage_Paused {
   403  			return terror.ErrWorkerRelayStageNotValid.Generate(d.stage, pb.Stage_Paused, op)
   404  		}
   405  		d.stage = pb.Stage_Running
   406  	case pb.RelayOp_StopRelay:
   407  		if d.stage == pb.Stage_Stopped {
   408  			return terror.ErrWorkerRelayStageNotValid.Generatef("current stage is already stopped not valid, relayop %s", op)
   409  		}
   410  		d.stage = pb.Stage_Stopped
   411  	}
   412  	return nil
   413  }
   414  
   415  // Result implements interface of RelayHolder.
   416  func (d *dummyRelayHolder) Result() *pb.ProcessResult {
   417  	return nil
   418  }
   419  
   420  // Update implements interface of RelayHolder.
   421  func (d *dummyRelayHolder) Update(ctx context.Context, cfg *config.SourceConfig) error {
   422  	return nil
   423  }
   424  
   425  func (d *dummyRelayHolder) EarliestActiveRelayLog() *streamer.RelayLogInfo {
   426  	return nil
   427  }
   428  
   429  func (d *dummyRelayHolder) Stage() pb.Stage {
   430  	d.Lock()
   431  	defer d.Unlock()
   432  	return d.stage
   433  }
   434  
   435  func (d *dummyRelayHolder) Relay() relay.Process {
   436  	return d.relay2
   437  }