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 }