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 }