github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/processor/processor_test.go (about) 1 // Copyright 2021 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 processor 15 16 import ( 17 "context" 18 "fmt" 19 "testing" 20 21 "github.com/pingcap/check" 22 "github.com/pingcap/errors" 23 "github.com/pingcap/log" 24 "github.com/pingcap/ticdc/cdc/model" 25 tablepipeline "github.com/pingcap/ticdc/cdc/processor/pipeline" 26 cdcContext "github.com/pingcap/ticdc/pkg/context" 27 cerror "github.com/pingcap/ticdc/pkg/errors" 28 "github.com/pingcap/ticdc/pkg/orchestrator" 29 "github.com/pingcap/ticdc/pkg/util/testleak" 30 ) 31 32 func Test(t *testing.T) { check.TestingT(t) } 33 34 type processorSuite struct{} 35 36 var _ = check.Suite(&processorSuite{}) 37 38 func initProcessor4Test(ctx cdcContext.Context, c *check.C) (*processor, *orchestrator.ReactorStateTester) { 39 p := newProcessor4Test(ctx, func(ctx cdcContext.Context, tableID model.TableID, replicaInfo *model.TableReplicaInfo) (tablepipeline.TablePipeline, error) { 40 return &mockTablePipeline{ 41 tableID: tableID, 42 name: fmt.Sprintf("`test`.`table%d`", tableID), 43 status: tablepipeline.TableStatusRunning, 44 resolvedTs: replicaInfo.StartTs, 45 checkpointTs: replicaInfo.StartTs, 46 }, nil 47 }) 48 p.changefeed = model.NewChangefeedReactorState(ctx.ChangefeedVars().ID) 49 return p, orchestrator.NewReactorStateTester(c, p.changefeed, map[string]string{ 50 "/tidb/cdc/capture/" + ctx.GlobalVars().CaptureInfo.ID: `{"id":"` + ctx.GlobalVars().CaptureInfo.ID + `","address":"127.0.0.1:8300"}`, 51 "/tidb/cdc/changefeed/info/" + ctx.ChangefeedVars().ID: `{"sink-uri":"blackhole://","opts":{},"create-time":"2020-02-02T00:00:00.000000+00:00","start-ts":0,"target-ts":0,"admin-job-type":0,"sort-engine":"memory","sort-dir":".","config":{"case-sensitive":true,"enable-old-value":false,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":null,"ddl-allow-list":null},"mounter":{"worker-num":16},"sink":{"dispatchers":null,"protocol":"default"},"cyclic-replication":{"enable":false,"replica-id":0,"filter-replica-ids":null,"id-buckets":0,"sync-ddl":false},"scheduler":{"type":"table-number","polling-time":-1}},"state":"normal","history":null,"error":null,"sync-point-enabled":false,"sync-point-interval":600000000000}`, 52 "/tidb/cdc/job/" + ctx.ChangefeedVars().ID: `{"resolved-ts":0,"checkpoint-ts":0,"admin-job-type":0}`, 53 "/tidb/cdc/task/status/" + ctx.GlobalVars().CaptureInfo.ID + "/" + ctx.ChangefeedVars().ID: `{"tables":{},"operation":null,"admin-job-type":0}`, 54 }) 55 } 56 57 type mockTablePipeline struct { 58 tableID model.TableID 59 name string 60 resolvedTs model.Ts 61 checkpointTs model.Ts 62 barrierTs model.Ts 63 stopTs model.Ts 64 status tablepipeline.TableStatus 65 canceled bool 66 } 67 68 func (m *mockTablePipeline) ID() (tableID int64, markTableID int64) { 69 return m.tableID, 0 70 } 71 72 func (m *mockTablePipeline) Name() string { 73 return m.name 74 } 75 76 func (m *mockTablePipeline) ResolvedTs() model.Ts { 77 return m.resolvedTs 78 } 79 80 func (m *mockTablePipeline) CheckpointTs() model.Ts { 81 return m.checkpointTs 82 } 83 84 func (m *mockTablePipeline) UpdateBarrierTs(ts model.Ts) { 85 m.barrierTs = ts 86 } 87 88 func (m *mockTablePipeline) AsyncStop(targetTs model.Ts) bool { 89 m.stopTs = targetTs 90 return true 91 } 92 93 func (m *mockTablePipeline) Workload() model.WorkloadInfo { 94 return model.WorkloadInfo{Workload: 1} 95 } 96 97 func (m *mockTablePipeline) Status() tablepipeline.TableStatus { 98 return m.status 99 } 100 101 func (m *mockTablePipeline) Cancel() { 102 if m.canceled { 103 log.Panic("cancel a canceled table pipeline") 104 } 105 m.canceled = true 106 } 107 108 func (m *mockTablePipeline) Wait() { 109 // do nothing 110 } 111 112 func (s *processorSuite) TestCheckTablesNum(c *check.C) { 113 defer testleak.AfterTest(c)() 114 ctx := cdcContext.NewBackendContext4Test(true) 115 p, tester := initProcessor4Test(ctx, c) 116 var err error 117 _, err = p.Tick(ctx, p.changefeed) 118 c.Assert(err, check.IsNil) 119 tester.MustApplyPatches() 120 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, 121 &model.TaskPosition{ 122 CheckPointTs: 0, 123 ResolvedTs: 0, 124 Count: 0, 125 Error: nil, 126 }) 127 128 p, tester = initProcessor4Test(ctx, c) 129 p.changefeed.Info.StartTs = 66 130 p.changefeed.Status.CheckpointTs = 88 131 _, err = p.Tick(ctx, p.changefeed) 132 c.Assert(err, check.IsNil) 133 tester.MustApplyPatches() 134 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, 135 &model.TaskPosition{ 136 CheckPointTs: 88, 137 ResolvedTs: 88, 138 Count: 0, 139 Error: nil, 140 }) 141 } 142 143 func (s *processorSuite) TestHandleTableOperation4SingleTable(c *check.C) { 144 defer testleak.AfterTest(c)() 145 ctx := cdcContext.NewBackendContext4Test(true) 146 p, tester := initProcessor4Test(ctx, c) 147 var err error 148 // init tick 149 _, err = p.Tick(ctx, p.changefeed) 150 c.Assert(err, check.IsNil) 151 tester.MustApplyPatches() 152 p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) { 153 status.CheckpointTs = 90 154 status.ResolvedTs = 100 155 return status, true, nil 156 }) 157 p.changefeed.PatchTaskPosition(p.captureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) { 158 position.ResolvedTs = 100 159 return position, true, nil 160 }) 161 tester.MustApplyPatches() 162 163 // no operation 164 _, err = p.Tick(ctx, p.changefeed) 165 c.Assert(err, check.IsNil) 166 tester.MustApplyPatches() 167 168 // add table, in processing 169 // in current implementation of owner, the startTs and BoundaryTs of add table operation should be always equaled. 170 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 171 status.AddTable(66, &model.TableReplicaInfo{StartTs: 60}, 60) 172 return status, true, nil 173 }) 174 tester.MustApplyPatches() 175 _, err = p.Tick(ctx, p.changefeed) 176 c.Assert(err, check.IsNil) 177 tester.MustApplyPatches() 178 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 179 Tables: map[int64]*model.TableReplicaInfo{ 180 66: {StartTs: 60}, 181 }, 182 Operation: map[int64]*model.TableOperation{ 183 66: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed}, 184 }, 185 }) 186 187 // add table, not finished 188 _, err = p.Tick(ctx, p.changefeed) 189 c.Assert(err, check.IsNil) 190 tester.MustApplyPatches() 191 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 192 Tables: map[int64]*model.TableReplicaInfo{ 193 66: {StartTs: 60}, 194 }, 195 Operation: map[int64]*model.TableOperation{ 196 66: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed}, 197 }, 198 }) 199 200 // add table, push the resolvedTs 201 table66 := p.tables[66].(*mockTablePipeline) 202 table66.resolvedTs = 101 203 _, err = p.Tick(ctx, p.changefeed) 204 c.Assert(err, check.IsNil) 205 tester.MustApplyPatches() 206 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 207 Tables: map[int64]*model.TableReplicaInfo{ 208 66: {StartTs: 60}, 209 }, 210 Operation: map[int64]*model.TableOperation{ 211 66: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed}, 212 }, 213 }) 214 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].ResolvedTs, check.Equals, uint64(101)) 215 216 // finish the operation 217 _, err = p.Tick(ctx, p.changefeed) 218 c.Assert(err, check.IsNil) 219 tester.MustApplyPatches() 220 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 221 Tables: map[int64]*model.TableReplicaInfo{ 222 66: {StartTs: 60}, 223 }, 224 Operation: map[int64]*model.TableOperation{ 225 66: {Delete: false, BoundaryTs: 60, Done: true, Status: model.OperFinished}, 226 }, 227 }) 228 229 // clear finished operations 230 cleanUpFinishedOpOperation(p.changefeed, p.captureInfo.ID, tester) 231 232 // remove table, in processing 233 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 234 status.RemoveTable(66, 120, false) 235 return status, true, nil 236 }) 237 tester.MustApplyPatches() 238 _, err = p.Tick(ctx, p.changefeed) 239 c.Assert(err, check.IsNil) 240 tester.MustApplyPatches() 241 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 242 Tables: map[int64]*model.TableReplicaInfo{}, 243 Operation: map[int64]*model.TableOperation{ 244 66: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed}, 245 }, 246 }) 247 c.Assert(table66.stopTs, check.Equals, uint64(120)) 248 249 // remove table, not finished 250 _, err = p.Tick(ctx, p.changefeed) 251 c.Assert(err, check.IsNil) 252 tester.MustApplyPatches() 253 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 254 Tables: map[int64]*model.TableReplicaInfo{}, 255 Operation: map[int64]*model.TableOperation{ 256 66: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed}, 257 }, 258 }) 259 260 // remove table, finished 261 table66.status = tablepipeline.TableStatusStopped 262 table66.checkpointTs = 121 263 _, err = p.Tick(ctx, p.changefeed) 264 c.Assert(err, check.IsNil) 265 tester.MustApplyPatches() 266 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 267 Tables: map[int64]*model.TableReplicaInfo{}, 268 Operation: map[int64]*model.TableOperation{ 269 66: {Delete: true, BoundaryTs: 121, Done: true, Status: model.OperFinished}, 270 }, 271 }) 272 c.Assert(table66.canceled, check.IsTrue) 273 c.Assert(p.tables[66], check.IsNil) 274 } 275 276 func (s *processorSuite) TestHandleTableOperation4MultiTable(c *check.C) { 277 defer testleak.AfterTest(c)() 278 ctx := cdcContext.NewBackendContext4Test(true) 279 p, tester := initProcessor4Test(ctx, c) 280 var err error 281 // init tick 282 _, err = p.Tick(ctx, p.changefeed) 283 c.Assert(err, check.IsNil) 284 tester.MustApplyPatches() 285 p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) { 286 status.CheckpointTs = 20 287 status.ResolvedTs = 20 288 return status, true, nil 289 }) 290 p.changefeed.PatchTaskPosition(p.captureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) { 291 position.ResolvedTs = 100 292 position.CheckPointTs = 90 293 return position, true, nil 294 }) 295 tester.MustApplyPatches() 296 297 // no operation 298 _, err = p.Tick(ctx, p.changefeed) 299 c.Assert(err, check.IsNil) 300 tester.MustApplyPatches() 301 302 // add table, in processing 303 // in current implementation of owner, the startTs and BoundaryTs of add table operation should be always equaled. 304 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 305 status.AddTable(1, &model.TableReplicaInfo{StartTs: 60}, 60) 306 status.AddTable(2, &model.TableReplicaInfo{StartTs: 50}, 50) 307 status.AddTable(3, &model.TableReplicaInfo{StartTs: 40}, 40) 308 status.Tables[4] = &model.TableReplicaInfo{StartTs: 30} 309 return status, true, nil 310 }) 311 tester.MustApplyPatches() 312 _, err = p.Tick(ctx, p.changefeed) 313 c.Assert(err, check.IsNil) 314 tester.MustApplyPatches() 315 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 316 Tables: map[int64]*model.TableReplicaInfo{ 317 1: {StartTs: 60}, 318 2: {StartTs: 50}, 319 3: {StartTs: 40}, 320 4: {StartTs: 30}, 321 }, 322 Operation: map[int64]*model.TableOperation{ 323 1: {Delete: false, BoundaryTs: 60, Done: false, Status: model.OperProcessed}, 324 2: {Delete: false, BoundaryTs: 50, Done: false, Status: model.OperProcessed}, 325 3: {Delete: false, BoundaryTs: 40, Done: false, Status: model.OperProcessed}, 326 }, 327 }) 328 c.Assert(p.tables, check.HasLen, 4) 329 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].CheckPointTs, check.Equals, uint64(30)) 330 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].ResolvedTs, check.Equals, uint64(30)) 331 332 // add table, push the resolvedTs, finished add table 333 table1 := p.tables[1].(*mockTablePipeline) 334 table2 := p.tables[2].(*mockTablePipeline) 335 table3 := p.tables[3].(*mockTablePipeline) 336 table4 := p.tables[4].(*mockTablePipeline) 337 table1.resolvedTs = 101 338 table2.resolvedTs = 101 339 table3.resolvedTs = 102 340 table4.resolvedTs = 103 341 // removed table 3 342 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 343 status.RemoveTable(3, 60, false) 344 return status, true, nil 345 }) 346 tester.MustApplyPatches() 347 _, err = p.Tick(ctx, p.changefeed) 348 c.Assert(err, check.IsNil) 349 tester.MustApplyPatches() 350 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 351 Tables: map[int64]*model.TableReplicaInfo{ 352 1: {StartTs: 60}, 353 2: {StartTs: 50}, 354 4: {StartTs: 30}, 355 }, 356 Operation: map[int64]*model.TableOperation{ 357 1: {Delete: false, BoundaryTs: 60, Done: true, Status: model.OperFinished}, 358 2: {Delete: false, BoundaryTs: 50, Done: true, Status: model.OperFinished}, 359 3: {Delete: true, BoundaryTs: 60, Done: false, Status: model.OperProcessed}, 360 }, 361 }) 362 c.Assert(p.tables, check.HasLen, 4) 363 c.Assert(table3.canceled, check.IsFalse) 364 c.Assert(table3.stopTs, check.Equals, uint64(60)) 365 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].ResolvedTs, check.Equals, uint64(101)) 366 367 // finish remove operations 368 table3.status = tablepipeline.TableStatusStopped 369 table3.checkpointTs = 65 370 _, err = p.Tick(ctx, p.changefeed) 371 c.Assert(err, check.IsNil) 372 tester.MustApplyPatches() 373 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 374 Tables: map[int64]*model.TableReplicaInfo{ 375 1: {StartTs: 60}, 376 2: {StartTs: 50}, 377 4: {StartTs: 30}, 378 }, 379 Operation: map[int64]*model.TableOperation{ 380 1: {Delete: false, BoundaryTs: 60, Done: true, Status: model.OperFinished}, 381 2: {Delete: false, BoundaryTs: 50, Done: true, Status: model.OperFinished}, 382 3: {Delete: true, BoundaryTs: 65, Done: true, Status: model.OperFinished}, 383 }, 384 }) 385 c.Assert(p.tables, check.HasLen, 3) 386 c.Assert(table3.canceled, check.IsTrue) 387 388 // clear finished operations 389 cleanUpFinishedOpOperation(p.changefeed, p.captureInfo.ID, tester) 390 391 // remove table, in processing 392 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 393 status.RemoveTable(1, 120, false) 394 status.RemoveTable(4, 120, false) 395 delete(status.Tables, 2) 396 return status, true, nil 397 }) 398 tester.MustApplyPatches() 399 _, err = p.Tick(ctx, p.changefeed) 400 c.Assert(err, check.IsNil) 401 tester.MustApplyPatches() 402 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 403 Tables: map[int64]*model.TableReplicaInfo{}, 404 Operation: map[int64]*model.TableOperation{ 405 1: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed}, 406 4: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed}, 407 }, 408 }) 409 c.Assert(table1.stopTs, check.Equals, uint64(120)) 410 c.Assert(table4.stopTs, check.Equals, uint64(120)) 411 c.Assert(table2.canceled, check.IsTrue) 412 c.Assert(p.tables, check.HasLen, 2) 413 414 // remove table, not finished 415 _, err = p.Tick(ctx, p.changefeed) 416 c.Assert(err, check.IsNil) 417 tester.MustApplyPatches() 418 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 419 Tables: map[int64]*model.TableReplicaInfo{}, 420 Operation: map[int64]*model.TableOperation{ 421 1: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed}, 422 4: {Delete: true, BoundaryTs: 120, Done: false, Status: model.OperProcessed}, 423 }, 424 }) 425 426 // remove table, finished 427 table1.status = tablepipeline.TableStatusStopped 428 table1.checkpointTs = 121 429 table4.status = tablepipeline.TableStatusStopped 430 table4.checkpointTs = 122 431 _, err = p.Tick(ctx, p.changefeed) 432 c.Assert(err, check.IsNil) 433 tester.MustApplyPatches() 434 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 435 Tables: map[int64]*model.TableReplicaInfo{}, 436 Operation: map[int64]*model.TableOperation{ 437 1: {Delete: true, BoundaryTs: 121, Done: true, Status: model.OperFinished}, 438 4: {Delete: true, BoundaryTs: 122, Done: true, Status: model.OperFinished}, 439 }, 440 }) 441 c.Assert(table1.canceled, check.IsTrue) 442 c.Assert(table4.canceled, check.IsTrue) 443 c.Assert(p.tables, check.HasLen, 0) 444 } 445 446 func (s *processorSuite) TestInitTable(c *check.C) { 447 defer testleak.AfterTest(c)() 448 ctx := cdcContext.NewBackendContext4Test(true) 449 p, tester := initProcessor4Test(ctx, c) 450 var err error 451 // init tick 452 _, err = p.Tick(ctx, p.changefeed) 453 c.Assert(err, check.IsNil) 454 tester.MustApplyPatches() 455 456 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 457 status.Tables[1] = &model.TableReplicaInfo{StartTs: 20} 458 status.Tables[2] = &model.TableReplicaInfo{StartTs: 30} 459 return status, true, nil 460 }) 461 tester.MustApplyPatches() 462 _, err = p.Tick(ctx, p.changefeed) 463 c.Assert(err, check.IsNil) 464 tester.MustApplyPatches() 465 c.Assert(p.tables[1], check.Not(check.IsNil)) 466 c.Assert(p.tables[2], check.Not(check.IsNil)) 467 } 468 469 func (s *processorSuite) TestProcessorError(c *check.C) { 470 defer testleak.AfterTest(c)() 471 ctx := cdcContext.NewBackendContext4Test(true) 472 p, tester := initProcessor4Test(ctx, c) 473 var err error 474 // init tick 475 _, err = p.Tick(ctx, p.changefeed) 476 c.Assert(err, check.IsNil) 477 tester.MustApplyPatches() 478 479 // send a abnormal error 480 p.sendError(cerror.ErrSinkURIInvalid) 481 _, err = p.Tick(ctx, p.changefeed) 482 tester.MustApplyPatches() 483 c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue) 484 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{ 485 Error: &model.RunningError{ 486 Addr: "127.0.0.1:0000", 487 Code: "CDC:ErrSinkURIInvalid", 488 Message: "[CDC:ErrSinkURIInvalid]sink uri invalid", 489 }, 490 }) 491 492 p, tester = initProcessor4Test(ctx, c) 493 // init tick 494 _, err = p.Tick(ctx, p.changefeed) 495 c.Assert(err, check.IsNil) 496 tester.MustApplyPatches() 497 498 // send a normal error 499 p.sendError(context.Canceled) 500 _, err = p.Tick(ctx, p.changefeed) 501 tester.MustApplyPatches() 502 c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue) 503 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{ 504 Error: nil, 505 }) 506 } 507 508 func (s *processorSuite) TestProcessorExit(c *check.C) { 509 defer testleak.AfterTest(c)() 510 ctx := cdcContext.NewBackendContext4Test(true) 511 p, tester := initProcessor4Test(ctx, c) 512 var err error 513 // init tick 514 _, err = p.Tick(ctx, p.changefeed) 515 c.Assert(err, check.IsNil) 516 tester.MustApplyPatches() 517 518 // stop the changefeed 519 p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) { 520 status.AdminJobType = model.AdminStop 521 return status, true, nil 522 }) 523 p.changefeed.PatchTaskStatus(ctx.GlobalVars().CaptureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 524 status.AdminJobType = model.AdminStop 525 return status, true, nil 526 }) 527 tester.MustApplyPatches() 528 _, err = p.Tick(ctx, p.changefeed) 529 c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue) 530 tester.MustApplyPatches() 531 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{ 532 Error: nil, 533 }) 534 } 535 536 func (s *processorSuite) TestProcessorClose(c *check.C) { 537 defer testleak.AfterTest(c)() 538 ctx := cdcContext.NewBackendContext4Test(true) 539 p, tester := initProcessor4Test(ctx, c) 540 var err error 541 // init tick 542 _, err = p.Tick(ctx, p.changefeed) 543 c.Assert(err, check.IsNil) 544 tester.MustApplyPatches() 545 546 // add tables 547 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 548 status.Tables[1] = &model.TableReplicaInfo{StartTs: 20} 549 status.Tables[2] = &model.TableReplicaInfo{StartTs: 30} 550 return status, true, nil 551 }) 552 tester.MustApplyPatches() 553 _, err = p.Tick(ctx, p.changefeed) 554 c.Assert(err, check.IsNil) 555 tester.MustApplyPatches() 556 557 // push the resolvedTs and checkpointTs 558 p.changefeed.PatchStatus(func(status *model.ChangeFeedStatus) (*model.ChangeFeedStatus, bool, error) { 559 status.ResolvedTs = 100 560 return status, true, nil 561 }) 562 tester.MustApplyPatches() 563 p.tables[1].(*mockTablePipeline).resolvedTs = 110 564 p.tables[2].(*mockTablePipeline).resolvedTs = 90 565 p.tables[1].(*mockTablePipeline).checkpointTs = 90 566 p.tables[2].(*mockTablePipeline).checkpointTs = 95 567 _, err = p.Tick(ctx, p.changefeed) 568 c.Assert(err, check.IsNil) 569 tester.MustApplyPatches() 570 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{ 571 CheckPointTs: 90, 572 ResolvedTs: 90, 573 Error: nil, 574 }) 575 c.Assert(p.changefeed.TaskStatuses[p.captureInfo.ID], check.DeepEquals, &model.TaskStatus{ 576 Tables: map[int64]*model.TableReplicaInfo{1: {StartTs: 20}, 2: {StartTs: 30}}, 577 }) 578 c.Assert(p.changefeed.Workloads[p.captureInfo.ID], check.DeepEquals, model.TaskWorkload{1: {Workload: 1}, 2: {Workload: 1}}) 579 580 c.Assert(p.Close(), check.IsNil) 581 tester.MustApplyPatches() 582 c.Assert(p.tables[1].(*mockTablePipeline).canceled, check.IsTrue) 583 c.Assert(p.tables[2].(*mockTablePipeline).canceled, check.IsTrue) 584 585 p, tester = initProcessor4Test(ctx, c) 586 // init tick 587 _, err = p.Tick(ctx, p.changefeed) 588 c.Assert(err, check.IsNil) 589 tester.MustApplyPatches() 590 591 // add tables 592 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 593 status.Tables[1] = &model.TableReplicaInfo{StartTs: 20} 594 status.Tables[2] = &model.TableReplicaInfo{StartTs: 30} 595 return status, true, nil 596 }) 597 tester.MustApplyPatches() 598 _, err = p.Tick(ctx, p.changefeed) 599 c.Assert(err, check.IsNil) 600 tester.MustApplyPatches() 601 602 // send error 603 p.sendError(cerror.ErrSinkURIInvalid) 604 _, err = p.Tick(ctx, p.changefeed) 605 c.Assert(cerror.ErrReactorFinished.Equal(errors.Cause(err)), check.IsTrue) 606 tester.MustApplyPatches() 607 608 c.Assert(p.Close(), check.IsNil) 609 tester.MustApplyPatches() 610 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID].Error, check.DeepEquals, &model.RunningError{ 611 Addr: "127.0.0.1:0000", 612 Code: "CDC:ErrSinkURIInvalid", 613 Message: "[CDC:ErrSinkURIInvalid]sink uri invalid", 614 }) 615 c.Assert(p.tables[1].(*mockTablePipeline).canceled, check.IsTrue) 616 c.Assert(p.tables[2].(*mockTablePipeline).canceled, check.IsTrue) 617 } 618 619 func (s *processorSuite) TestPositionDeleted(c *check.C) { 620 defer testleak.AfterTest(c)() 621 ctx := cdcContext.NewBackendContext4Test(true) 622 p, tester := initProcessor4Test(ctx, c) 623 p.changefeed.PatchTaskStatus(p.captureInfo.ID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 624 status.Tables[1] = &model.TableReplicaInfo{StartTs: 30} 625 status.Tables[2] = &model.TableReplicaInfo{StartTs: 40} 626 return status, true, nil 627 }) 628 var err error 629 // init tick 630 _, err = p.Tick(ctx, p.changefeed) 631 c.Assert(err, check.IsNil) 632 tester.MustApplyPatches() 633 634 // cal position 635 _, err = p.Tick(ctx, p.changefeed) 636 c.Assert(err, check.IsNil) 637 tester.MustApplyPatches() 638 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{ 639 CheckPointTs: 30, 640 ResolvedTs: 30, 641 }) 642 643 // some other delete the task position 644 p.changefeed.PatchTaskPosition(p.captureInfo.ID, func(position *model.TaskPosition) (*model.TaskPosition, bool, error) { 645 return nil, true, nil 646 }) 647 tester.MustApplyPatches() 648 // position created again 649 _, err = p.Tick(ctx, p.changefeed) 650 c.Assert(err, check.IsNil) 651 tester.MustApplyPatches() 652 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{ 653 CheckPointTs: 0, 654 ResolvedTs: 0, 655 }) 656 657 // cal position 658 _, err = p.Tick(ctx, p.changefeed) 659 c.Assert(err, check.IsNil) 660 tester.MustApplyPatches() 661 c.Assert(p.changefeed.TaskPositions[p.captureInfo.ID], check.DeepEquals, &model.TaskPosition{ 662 CheckPointTs: 30, 663 ResolvedTs: 30, 664 }) 665 } 666 667 func cleanUpFinishedOpOperation(state *model.ChangefeedReactorState, captureID model.CaptureID, tester *orchestrator.ReactorStateTester) { 668 state.PatchTaskStatus(captureID, func(status *model.TaskStatus) (*model.TaskStatus, bool, error) { 669 if status == nil || status.Operation == nil { 670 return status, false, nil 671 } 672 for tableID, opt := range status.Operation { 673 if opt.Done && opt.Status == model.OperFinished { 674 delete(status.Operation, tableID) 675 } 676 } 677 return status, true, nil 678 }) 679 tester.MustApplyPatches() 680 }