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 }