github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/worker/relay_test.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  	"time"
    19  
    20  	"github.com/go-mysql-org/go-mysql/mysql"
    21  	. "github.com/pingcap/check"
    22  	"github.com/pingcap/errors"
    23  	"github.com/pingcap/tiflow/dm/config"
    24  	"github.com/pingcap/tiflow/dm/config/dbconfig"
    25  	"github.com/pingcap/tiflow/dm/pb"
    26  	"github.com/pingcap/tiflow/dm/pkg/binlog"
    27  	"github.com/pingcap/tiflow/dm/pkg/log"
    28  	pkgstreamer "github.com/pingcap/tiflow/dm/pkg/streamer"
    29  	"github.com/pingcap/tiflow/dm/pkg/utils"
    30  	"github.com/pingcap/tiflow/dm/relay"
    31  	"github.com/pingcap/tiflow/dm/unit"
    32  )
    33  
    34  type testRelay struct{}
    35  
    36  var _ = Suite(&testRelay{})
    37  
    38  /*********** dummy relay log process unit, used only for testing *************/
    39  
    40  // DummyRelay is a dummy relay.
    41  type DummyRelay struct {
    42  	initErr error
    43  
    44  	processResult pb.ProcessResult
    45  	errorInfo     *pb.RelayError
    46  	reloadErr     error
    47  }
    48  
    49  func (d *DummyRelay) IsActive(uuid, filename string) (bool, int64) {
    50  	return false, 0
    51  }
    52  
    53  func (d *DummyRelay) NewReader(logger log.Logger, cfg *relay.BinlogReaderConfig) *relay.BinlogReader {
    54  	return nil
    55  }
    56  
    57  func (d *DummyRelay) RegisterListener(el relay.Listener) {
    58  }
    59  
    60  func (d *DummyRelay) UnRegisterListener(el relay.Listener) {
    61  }
    62  
    63  // NewDummyRelay creates an instance of dummy Relay.
    64  func NewDummyRelay(cfg *relay.Config) relay.Process {
    65  	return &DummyRelay{}
    66  }
    67  
    68  // Init implements Process interface.
    69  func (d *DummyRelay) Init(ctx context.Context) error {
    70  	return d.initErr
    71  }
    72  
    73  // InjectInitError injects init error.
    74  func (d *DummyRelay) InjectInitError(err error) {
    75  	d.initErr = err
    76  }
    77  
    78  // Process implements Process interface.
    79  func (d *DummyRelay) Process(ctx context.Context) pb.ProcessResult {
    80  	<-ctx.Done()
    81  	return d.processResult
    82  }
    83  
    84  // InjectProcessResult injects process result.
    85  func (d *DummyRelay) InjectProcessResult(result pb.ProcessResult) {
    86  	d.processResult = result
    87  }
    88  
    89  // ActiveRelayLog implements Process interface.
    90  func (d *DummyRelay) ActiveRelayLog() *pkgstreamer.RelayLogInfo {
    91  	return nil
    92  }
    93  
    94  // Reload implements Process interface.
    95  func (d *DummyRelay) Reload(newCfg *relay.Config) error {
    96  	return d.reloadErr
    97  }
    98  
    99  // InjectReloadError injects reload error.
   100  func (d *DummyRelay) InjectReloadError(err error) {
   101  	d.reloadErr = err
   102  }
   103  
   104  // Update implements Process interface.
   105  func (d *DummyRelay) Update(cfg *config.SubTaskConfig) error {
   106  	return nil
   107  }
   108  
   109  // Resume implements Process interface.
   110  func (d *DummyRelay) Resume(ctx context.Context, pr chan pb.ProcessResult) {}
   111  
   112  // Pause implements Process interface.
   113  func (d *DummyRelay) Pause() {}
   114  
   115  // Error implements Process interface.
   116  func (d *DummyRelay) Error() interface{} {
   117  	return d.errorInfo
   118  }
   119  
   120  // Status implements Process interface.
   121  func (d *DummyRelay) Status(sourceStatus *binlog.SourceStatus) interface{} {
   122  	return &pb.RelayStatus{
   123  		Stage: pb.Stage_New,
   124  	}
   125  }
   126  
   127  // Close implements Process interface.
   128  func (d *DummyRelay) Close() {}
   129  
   130  // IsClosed implements Process interface.
   131  func (d *DummyRelay) IsClosed() bool { return false }
   132  
   133  // SaveMeta implements Process interface.
   134  func (d *DummyRelay) SaveMeta(pos mysql.Position, gset mysql.GTIDSet) error {
   135  	return nil
   136  }
   137  
   138  // ResetMeta implements Process interface.
   139  func (d *DummyRelay) ResetMeta() {}
   140  
   141  // PurgeRelayDir implements Process interface.
   142  func (d *DummyRelay) PurgeRelayDir() error {
   143  	return nil
   144  }
   145  
   146  func (t *testRelay) TestRelay(c *C) {
   147  	originNewRelay := relay.NewRelay
   148  	relay.NewRelay = NewDummyRelay
   149  	originNewPurger := relay.NewPurger
   150  	relay.NewPurger = relay.NewDummyPurger
   151  	defer func() {
   152  		relay.NewRelay = originNewRelay
   153  		relay.NewPurger = originNewPurger
   154  	}()
   155  
   156  	cfg := loadSourceConfigWithoutPassword(c)
   157  
   158  	dir := c.MkDir()
   159  	cfg.RelayDir = dir
   160  	cfg.MetaDir = dir
   161  
   162  	relayHolder := NewRealRelayHolder(cfg)
   163  	c.Assert(relayHolder, NotNil)
   164  
   165  	holder, ok := relayHolder.(*realRelayHolder)
   166  	c.Assert(ok, IsTrue)
   167  
   168  	t.testInit(c, holder)
   169  	t.testStart(c, holder)
   170  	t.testPauseAndResume(c, holder)
   171  	t.testClose(c, holder)
   172  	t.testStop(c, holder)
   173  }
   174  
   175  func (t *testRelay) testInit(c *C, holder *realRelayHolder) {
   176  	ctx := context.Background()
   177  	_, err := holder.Init(ctx, nil)
   178  	c.Assert(err, IsNil)
   179  
   180  	r, ok := holder.relay.(*DummyRelay)
   181  	c.Assert(ok, IsTrue)
   182  
   183  	initErr := errors.New("init error")
   184  	r.InjectInitError(initErr)
   185  	defer r.InjectInitError(nil)
   186  
   187  	_, err = holder.Init(ctx, nil)
   188  	c.Assert(err, ErrorMatches, ".*"+initErr.Error()+".*")
   189  }
   190  
   191  func (t *testRelay) testStart(c *C, holder *realRelayHolder) {
   192  	c.Assert(holder.Stage(), Equals, pb.Stage_New)
   193  	c.Assert(holder.closed.Load(), IsFalse)
   194  	c.Assert(holder.Result(), IsNil)
   195  
   196  	holder.Start()
   197  	c.Assert(waitRelayStage(holder, pb.Stage_Running, 5), IsTrue)
   198  	c.Assert(holder.Result(), IsNil)
   199  	c.Assert(holder.closed.Load(), IsFalse)
   200  
   201  	// test status
   202  	status := holder.Status(nil)
   203  	c.Assert(status.Stage, Equals, pb.Stage_Running)
   204  	c.Assert(status.Result, IsNil)
   205  
   206  	c.Assert(holder.Error(), IsNil)
   207  
   208  	// test update and pause -> resume
   209  	t.testUpdate(c, holder)
   210  	c.Assert(holder.Stage(), Equals, pb.Stage_Paused)
   211  	c.Assert(holder.closed.Load(), IsFalse)
   212  
   213  	err := holder.Operate(context.Background(), pb.RelayOp_ResumeRelay)
   214  	c.Assert(err, IsNil)
   215  	c.Assert(waitRelayStage(holder, pb.Stage_Running, 10), IsTrue)
   216  	c.Assert(holder.Result(), IsNil)
   217  	c.Assert(holder.closed.Load(), IsFalse)
   218  }
   219  
   220  func (t *testRelay) testClose(c *C, holder *realRelayHolder) {
   221  	r, ok := holder.relay.(*DummyRelay)
   222  	c.Assert(ok, IsTrue)
   223  	processResult := &pb.ProcessResult{
   224  		IsCanceled: true,
   225  		Errors: []*pb.ProcessError{
   226  			unit.NewProcessError(errors.New("process error")),
   227  		},
   228  	}
   229  	r.InjectProcessResult(*processResult)
   230  	defer r.InjectProcessResult(pb.ProcessResult{})
   231  
   232  	holder.Close()
   233  	c.Assert(waitRelayStage(holder, pb.Stage_Paused, 10), IsTrue)
   234  	c.Assert(holder.Result(), DeepEquals, processResult)
   235  	c.Assert(holder.closed.Load(), IsTrue)
   236  
   237  	holder.Close()
   238  	c.Assert(holder.Stage(), Equals, pb.Stage_Paused)
   239  	c.Assert(holder.Result(), DeepEquals, processResult)
   240  	c.Assert(holder.closed.Load(), IsTrue)
   241  
   242  	// todo: very strange, and can't resume
   243  	status := holder.Status(nil)
   244  	c.Assert(status.Stage, Equals, pb.Stage_Stopped)
   245  	c.Assert(status.Result, IsNil)
   246  
   247  	errInfo := holder.Error()
   248  	c.Assert(errInfo.Msg, Equals, "relay stopped")
   249  }
   250  
   251  func (t *testRelay) testPauseAndResume(c *C, holder *realRelayHolder) {
   252  	err := holder.Operate(context.Background(), pb.RelayOp_PauseRelay)
   253  	c.Assert(err, IsNil)
   254  	c.Assert(holder.Stage(), Equals, pb.Stage_Paused)
   255  	c.Assert(holder.closed.Load(), IsFalse)
   256  
   257  	err = holder.pauseRelay(context.Background(), pb.RelayOp_PauseRelay)
   258  	c.Assert(err, ErrorMatches, ".*current stage is Paused.*")
   259  
   260  	// test status
   261  	status := holder.Status(nil)
   262  	c.Assert(status.Stage, Equals, pb.Stage_Paused)
   263  
   264  	// test update
   265  	t.testUpdate(c, holder)
   266  
   267  	err = holder.Operate(context.Background(), pb.RelayOp_ResumeRelay)
   268  	c.Assert(err, IsNil)
   269  	c.Assert(waitRelayStage(holder, pb.Stage_Running, 10), IsTrue)
   270  	c.Assert(holder.Result(), IsNil)
   271  	c.Assert(holder.closed.Load(), IsFalse)
   272  
   273  	err = holder.Operate(context.Background(), pb.RelayOp_ResumeRelay)
   274  	c.Assert(err, ErrorMatches, ".*current stage is Running.*")
   275  
   276  	// test status
   277  	status = holder.Status(nil)
   278  	c.Assert(status.Stage, Equals, pb.Stage_Running)
   279  	c.Assert(status.Result, IsNil)
   280  
   281  	// invalid operation
   282  	err = holder.Operate(context.Background(), pb.RelayOp_InvalidRelayOp)
   283  	c.Assert(err, ErrorMatches, ".*not supported.*")
   284  }
   285  
   286  func (t *testRelay) testUpdate(c *C, holder *realRelayHolder) {
   287  	cfg := &config.SourceConfig{
   288  		From: dbconfig.DBConfig{
   289  			Host:     "127.0.0.1",
   290  			Port:     3306,
   291  			User:     "root",
   292  			Password: "1234",
   293  		},
   294  	}
   295  
   296  	originStage := holder.Stage()
   297  	c.Assert(holder.Update(context.Background(), cfg), IsNil)
   298  	c.Assert(waitRelayStage(holder, originStage, 10), IsTrue)
   299  	c.Assert(holder.closed.Load(), IsFalse)
   300  
   301  	r, ok := holder.relay.(*DummyRelay)
   302  	c.Assert(ok, IsTrue)
   303  
   304  	err := errors.New("reload error")
   305  	r.InjectReloadError(err)
   306  	defer r.InjectReloadError(nil)
   307  	c.Assert(holder.Update(context.Background(), cfg), Equals, err)
   308  }
   309  
   310  func (t *testRelay) testStop(c *C, holder *realRelayHolder) {
   311  	err := holder.Operate(context.Background(), pb.RelayOp_StopRelay)
   312  	c.Assert(err, IsNil)
   313  	c.Assert(holder.Stage(), Equals, pb.Stage_Stopped)
   314  	c.Assert(holder.closed.Load(), IsTrue)
   315  
   316  	err = holder.Operate(context.Background(), pb.RelayOp_StopRelay)
   317  	c.Assert(err, ErrorMatches, ".*current stage is already stopped.*")
   318  }
   319  
   320  func waitRelayStage(holder *realRelayHolder, expect pb.Stage, backoff int) bool {
   321  	return utils.WaitSomething(backoff, 10*time.Millisecond, func() bool {
   322  		return holder.Stage() == expect
   323  	})
   324  }