github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/safe_mode_test.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  package syncer
    14  
    15  import (
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/go-mysql-org/go-mysql/mysql"
    20  	"github.com/pingcap/tiflow/dm/config"
    21  	"github.com/pingcap/tiflow/dm/pkg/binlog"
    22  	tcontext "github.com/pingcap/tiflow/dm/pkg/context"
    23  	"github.com/pingcap/tiflow/dm/pkg/log"
    24  	mode "github.com/pingcap/tiflow/dm/syncer/safe-mode"
    25  	"github.com/stretchr/testify/require"
    26  	"go.etcd.io/etcd/tests/v3/integration"
    27  	"go.uber.org/zap"
    28  )
    29  
    30  type mockCheckpointForSafeMode struct {
    31  	CheckPoint
    32  
    33  	safeModeExitPoint *binlog.Location
    34  	globalPoint       binlog.Location
    35  	tablePoint        map[string]map[string]binlog.Location
    36  }
    37  
    38  func (c *mockCheckpointForSafeMode) SafeModeExitPoint() *binlog.Location {
    39  	return c.safeModeExitPoint
    40  }
    41  
    42  func (c *mockCheckpointForSafeMode) GlobalPoint() binlog.Location {
    43  	return c.globalPoint
    44  }
    45  
    46  func (c *mockCheckpointForSafeMode) TablePoint() map[string]map[string]binlog.Location {
    47  	return c.tablePoint
    48  }
    49  
    50  func TestEnableSafeModeInitializationPhase(t *testing.T) {
    51  	integration.BeforeTestExternal(t)
    52  	mockCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
    53  	defer mockCluster.Terminate(t)
    54  	etcdTestCli := mockCluster.RandClient()
    55  
    56  	require.NoError(t, log.InitLogger(&log.Config{Level: "debug"}))
    57  	l := log.With(zap.String("unit test", "TestEnableSafeModeInitializationPhase"))
    58  	s := &Syncer{
    59  		tctx:     tcontext.Background().WithLogger(l),
    60  		safeMode: mode.NewSafeMode(), cli: etcdTestCli,
    61  		cfg: &config.SubTaskConfig{
    62  			Name: "test", SourceID: "test",
    63  			SyncerConfig: config.SyncerConfig{
    64  				CheckpointFlushInterval: 1,
    65  			},
    66  			Flavor: mysql.MySQLFlavor,
    67  		},
    68  	}
    69  
    70  	// test enable by task cliArgs (disable is tested in it test)
    71  	duration, err := time.ParseDuration("2s")
    72  	require.NoError(t, err)
    73  	s.cliArgs = &config.TaskCliArgs{SafeModeDuration: duration.String()}
    74  	s.enableSafeModeInitializationPhase(s.tctx)
    75  	require.True(t, s.safeMode.Enable())
    76  	s.Lock()
    77  	require.Nil(t, s.exitSafeModeTS) // not meet the first binlog
    78  	firstBinlogTS := int64(1)
    79  	require.NoError(t, s.initSafeModeExitTS(firstBinlogTS))
    80  	require.NotNil(t, s.exitSafeModeTS) // not meet the first binlog
    81  	require.Equal(t, int64(3), *s.exitSafeModeTS)
    82  	require.Equal(t, firstBinlogTS, *s.firstMeetBinlogTS)
    83  	s.Unlock()
    84  	require.NoError(t, s.checkAndExitSafeModeByBinlogTS(s.tctx, *s.exitSafeModeTS)) // not exit when binlog TS == exit TS
    85  	require.True(t, s.safeMode.Enable())
    86  	require.NoError(t, s.checkAndExitSafeModeByBinlogTS(s.tctx, *s.exitSafeModeTS+int64(1))) // exit when binlog TS > exit TS
    87  	require.False(t, s.safeMode.Enable())
    88  	s.Lock()
    89  	require.Nil(t, s.exitSafeModeTS)
    90  	require.Equal(t, "", s.cliArgs.SafeModeDuration)
    91  	s.Unlock()
    92  
    93  	// test enable by config
    94  	s.cliArgs = nil
    95  	s.cfg.SafeMode = true
    96  	s.cfg.SafeModeDuration = "0s" // test safeMode's priority higher than SafeModeDuration's
    97  	mockCheckpoint := &mockCheckpointForSafeMode{}
    98  	mockCheckpoint.globalPoint = binlog.Location{}
    99  	mockCheckpoint.tablePoint = make(map[string]map[string]binlog.Location)
   100  	s.checkpoint = mockCheckpoint
   101  	s.enableSafeModeInitializationPhase(s.tctx)
   102  	require.True(t, s.safeMode.Enable())
   103  
   104  	// test enable by SafeModeExitPoint (disable is tested in it test)
   105  	s.cfg.SafeMode = false
   106  	s.cfg.SafeModeDuration = ""
   107  	mockCheckpoint.safeModeExitPoint = &binlog.Location{Position: mysql.Position{Name: "mysql-bin.000123", Pos: 123}}
   108  	mockCheckpoint.globalPoint = binlog.Location{Position: mysql.Position{Name: "mysql-bin.000123", Pos: 120}}
   109  	s.initInitExecutedLoc()
   110  	s.enableSafeModeInitializationPhase(s.tctx)
   111  	require.True(t, s.safeMode.Enable())
   112  
   113  	// test enable by initPhaseSeconds
   114  	s.checkpoint = &mockCheckpointForSafeMode{}
   115  	s.enableSafeModeInitializationPhase(s.tctx)
   116  	time.Sleep(time.Second) // wait for enableSafeModeInitializationPhase running
   117  	require.True(t, s.safeMode.Enable())
   118  	time.Sleep(time.Second * 2) // wait for enableSafeModeInitializationPhase exit
   119  	require.False(t, s.safeMode.Enable())
   120  
   121  	// test SafeModeDuration="3s"
   122  	s = &Syncer{
   123  		tctx:     tcontext.Background().WithLogger(l),
   124  		safeMode: mode.NewSafeMode(), cli: etcdTestCli,
   125  		cfg: &config.SubTaskConfig{
   126  			Name: "test", SourceID: "test",
   127  			SyncerConfig: config.SyncerConfig{
   128  				CheckpointFlushInterval: 1,
   129  				SafeModeDuration:        "3s",
   130  			},
   131  			Flavor: mysql.MySQLFlavor,
   132  		},
   133  		checkpoint: &mockCheckpointForSafeMode{},
   134  	}
   135  	s.enableSafeModeInitializationPhase(s.tctx)
   136  	time.Sleep(time.Second * 2) // wait for enableSafeModeInitializationPhase running
   137  	require.True(t, s.safeMode.Enable())
   138  	time.Sleep(time.Second * 4) // wait for enableSafeModeInitializationPhase exit
   139  	require.False(t, s.safeMode.Enable())
   140  }