github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/worker/subtask_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 "strings" 19 "testing" 20 "time" 21 22 "github.com/go-mysql-org/go-mysql/mysql" 23 . "github.com/pingcap/check" 24 "github.com/pingcap/errors" 25 "github.com/pingcap/tiflow/dm/config" 26 "github.com/pingcap/tiflow/dm/dumpling" 27 "github.com/pingcap/tiflow/dm/loader" 28 "github.com/pingcap/tiflow/dm/pb" 29 "github.com/pingcap/tiflow/dm/pkg/binlog" 30 "github.com/pingcap/tiflow/dm/pkg/terror" 31 "github.com/pingcap/tiflow/dm/pkg/utils" 32 "github.com/pingcap/tiflow/dm/relay" 33 "github.com/pingcap/tiflow/dm/syncer" 34 "github.com/pingcap/tiflow/dm/unit" 35 "github.com/stretchr/testify/require" 36 clientv3 "go.etcd.io/etcd/client/v3" 37 ) 38 39 const ( 40 // mocked loadMetaBinlog must be greater than relayHolderBinlog. 41 loadMetaBinlog = "(mysql-bin.00001,154)" 42 relayHolderBinlog = "(mysql-bin.00001,150)" 43 ) 44 45 type testSubTask struct{} 46 47 var _ = Suite(&testSubTask{}) 48 49 func (t *testSubTask) TestCreateUnits(c *C) { 50 cfg := &config.SubTaskConfig{ 51 Mode: "xxx", 52 Flavor: mysql.MySQLFlavor, 53 } 54 worker := "worker" 55 c.Assert(createUnits(cfg, nil, worker, nil), HasLen, 0) 56 57 cfg.Mode = config.ModeFull 58 unitsFull := createUnits(cfg, nil, worker, nil) 59 c.Assert(unitsFull, HasLen, 2) 60 _, ok := unitsFull[0].(*dumpling.Dumpling) 61 c.Assert(ok, IsTrue) 62 _, ok = unitsFull[1].(*loader.LightningLoader) 63 c.Assert(ok, IsTrue) 64 65 cfg.Mode = config.ModeIncrement 66 unitsIncr := createUnits(cfg, nil, worker, nil) 67 c.Assert(unitsIncr, HasLen, 1) 68 _, ok = unitsIncr[0].(*syncer.Syncer) 69 c.Assert(ok, IsTrue) 70 71 cfg.Mode = config.ModeAll 72 unitsAll := createUnits(cfg, nil, worker, nil) 73 c.Assert(unitsAll, HasLen, 3) 74 _, ok = unitsAll[0].(*dumpling.Dumpling) 75 c.Assert(ok, IsTrue) 76 _, ok = unitsAll[1].(*loader.LightningLoader) 77 c.Assert(ok, IsTrue) 78 _, ok = unitsAll[2].(*syncer.Syncer) 79 c.Assert(ok, IsTrue) 80 } 81 82 type MockUnit struct { 83 processErrorCh chan error 84 errInit error 85 errUpdate error 86 87 errFresh error 88 89 typ pb.UnitType 90 isFresh bool 91 } 92 93 func NewMockUnit(typ pb.UnitType) *MockUnit { 94 return &MockUnit{ 95 typ: typ, 96 processErrorCh: make(chan error), 97 isFresh: true, 98 } 99 } 100 101 func (m *MockUnit) Init(_ context.Context) error { 102 return m.errInit 103 } 104 105 func (m *MockUnit) Process(ctx context.Context, pr chan pb.ProcessResult) { 106 select { 107 case <-ctx.Done(): 108 pr <- pb.ProcessResult{ 109 IsCanceled: true, 110 Errors: nil, 111 } 112 case err := <-m.processErrorCh: 113 if err == nil { 114 pr <- pb.ProcessResult{} 115 } else { 116 pr <- pb.ProcessResult{ 117 Errors: []*pb.ProcessError{unit.NewProcessError(err)}, 118 } 119 } 120 } 121 } 122 123 func (m *MockUnit) Close() {} 124 125 func (m *MockUnit) Kill() {} 126 127 func (m MockUnit) Pause() {} 128 129 func (m *MockUnit) Resume(ctx context.Context, pr chan pb.ProcessResult) { m.Process(ctx, pr) } 130 131 func (m *MockUnit) Update(context.Context, *config.SubTaskConfig) error { 132 return m.errUpdate 133 } 134 135 func (m *MockUnit) Status(_ *binlog.SourceStatus) interface{} { 136 switch m.typ { 137 case pb.UnitType_Check: 138 return &pb.CheckStatus{} 139 case pb.UnitType_Dump: 140 return &pb.DumpStatus{} 141 case pb.UnitType_Load: 142 return &pb.LoadStatus{MetaBinlog: loadMetaBinlog} 143 case pb.UnitType_Sync: 144 return &pb.SyncStatus{} 145 default: 146 return struct{}{} 147 } 148 } 149 150 func (m *MockUnit) Type() pb.UnitType { return m.typ } 151 152 func (m *MockUnit) IsFreshTask(_ context.Context) (bool, error) { return m.isFresh, m.errFresh } 153 154 func (m *MockUnit) InjectProcessError(ctx context.Context, err error) error { 155 newCtx, cancel := context.WithTimeout(ctx, time.Second) 156 defer cancel() 157 158 select { 159 case <-newCtx.Done(): 160 return newCtx.Err() 161 case m.processErrorCh <- err: 162 } 163 164 return nil 165 } 166 167 func (m *MockUnit) InjectInitError(err error) { m.errInit = err } 168 169 func (m *MockUnit) InjectUpdateError(err error) { m.errUpdate = err } 170 171 func (m *MockUnit) InjectFreshError(isFresh bool, err error) { m.isFresh, m.errFresh = isFresh, err } 172 173 func (t *testSubTask) TestSubTaskNormalUsage(c *C) { 174 cfg := &config.SubTaskConfig{ 175 Name: "testSubtaskScene", 176 Mode: config.ModeFull, 177 } 178 179 st := NewSubTask(cfg, nil, "worker") 180 c.Assert(st.Stage(), DeepEquals, pb.Stage_New) 181 182 // test empty and fail 183 defer func() { 184 createUnits = createRealUnits 185 }() 186 createUnits = func(cfg *config.SubTaskConfig, etcdClient *clientv3.Client, worker string, relay relay.Process) []unit.Unit { 187 return nil 188 } 189 st.Run(pb.Stage_Running, pb.Stage_Running, nil) 190 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 191 c.Assert(strings.Contains(st.Result().Errors[0].String(), "has no dm units for mode"), IsTrue) 192 193 mockDumper := NewMockUnit(pb.UnitType_Dump) 194 mockLoader := NewMockUnit(pb.UnitType_Load) 195 createUnits = func(cfg *config.SubTaskConfig, etcdClient *clientv3.Client, worker string, relay relay.Process) []unit.Unit { 196 return []unit.Unit{mockDumper, mockLoader} 197 } 198 199 st.Run(pb.Stage_Running, pb.Stage_Running, nil) 200 c.Assert(st.Stage(), Equals, pb.Stage_Running) 201 c.Assert(st.CurrUnit(), Equals, mockDumper) 202 c.Assert(st.Result(), IsNil) 203 204 // finish dump 205 c.Assert(mockDumper.InjectProcessError(context.Background(), nil), IsNil) 206 for i := 0; i < 10; i++ { 207 if st.CurrUnit().Type() == pb.UnitType_Load { 208 break 209 } 210 time.Sleep(time.Millisecond) 211 } 212 c.Assert(st.CurrUnit(), Equals, mockLoader) 213 c.Assert(st.Result(), IsNil) 214 c.Assert(st.Stage(), Equals, pb.Stage_Running) 215 216 // fail loader 217 c.Assert(mockLoader.InjectProcessError(context.Background(), errors.New("loader process error")), IsNil) 218 for i := 0; i < 10; i++ { 219 res := st.Result() 220 if res != nil && st.Stage() == pb.Stage_Paused { 221 break 222 } 223 time.Sleep(time.Millisecond) 224 } 225 c.Assert(st.CurrUnit(), Equals, mockLoader) 226 c.Assert(st.Result(), NotNil) 227 c.Assert(st.Result().Errors, HasLen, 1) 228 c.Assert(strings.Contains(st.Result().Errors[0].Message, "loader process error"), IsTrue) 229 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 230 231 // restore from pausing 232 c.Assert(st.Resume(nil), IsNil) 233 c.Assert(st.CurrUnit(), Equals, mockLoader) 234 c.Assert(st.Result(), IsNil) 235 c.Assert(st.Stage(), Equals, pb.Stage_Running) 236 237 // update in running 238 c.Assert(st.Update(context.Background(), nil), NotNil) 239 c.Assert(st.CurrUnit(), Equals, mockLoader) 240 c.Assert(st.Result(), IsNil) 241 c.Assert(st.Stage(), Equals, pb.Stage_Running) 242 243 // Pause 244 c.Assert(st.Pause(), IsNil) 245 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 246 c.Assert(st.CurrUnit(), Equals, mockLoader) 247 if st.Result() != nil && (!st.Result().IsCanceled || len(st.Result().Errors) > 0) { 248 c.Fatalf("result %+v is not right after closing", st.Result()) 249 } 250 251 // update again 252 c.Assert(st.Update(context.Background(), &config.SubTaskConfig{Name: "updateSubtask"}), IsNil) 253 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 254 c.Assert(st.CurrUnit(), Equals, mockLoader) 255 if st.Result() != nil && (!st.Result().IsCanceled || len(st.Result().Errors) > 0) { 256 c.Fatalf("result %+v is not right after closing", st.Result()) 257 } 258 259 // run again 260 c.Assert(st.Resume(nil), IsNil) 261 c.Assert(st.CurrUnit(), Equals, mockLoader) 262 c.Assert(st.Result(), IsNil) 263 c.Assert(st.Stage(), Equals, pb.Stage_Running) 264 265 // pause again 266 c.Assert(st.Pause(), IsNil) 267 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 268 c.Assert(st.CurrUnit(), Equals, mockLoader) 269 if st.Result() != nil && (!st.Result().IsCanceled || len(st.Result().Errors) > 0) { 270 c.Fatalf("result %+v is not right after closing", st.Result()) 271 } 272 273 // run again 274 c.Assert(st.Resume(nil), IsNil) 275 c.Assert(st.CurrUnit(), Equals, mockLoader) 276 c.Assert(st.Result(), IsNil) 277 c.Assert(st.Stage(), Equals, pb.Stage_Running) 278 279 // finish loader 280 c.Assert(mockLoader.InjectProcessError(context.Background(), nil), IsNil) 281 for i := 0; i < 1000; i++ { 282 if st.Stage() == pb.Stage_Finished { 283 break 284 } 285 time.Sleep(time.Millisecond) 286 } 287 c.Assert(st.CurrUnit(), Equals, mockLoader) 288 c.Assert(st.Stage(), Equals, pb.Stage_Finished) 289 c.Assert(st.Result().Errors, HasLen, 0) 290 } 291 292 func (t *testSubTask) TestPauseAndResumeSubtask(c *C) { 293 cfg := &config.SubTaskConfig{ 294 Name: "testSubtaskScene", 295 Mode: config.ModeFull, 296 } 297 298 st := NewSubTask(cfg, nil, "worker") 299 c.Assert(st.Stage(), DeepEquals, pb.Stage_New) 300 301 mockDumper := NewMockUnit(pb.UnitType_Dump) 302 mockLoader := NewMockUnit(pb.UnitType_Load) 303 defer func() { 304 createUnits = createRealUnits 305 }() 306 createUnits = func(cfg *config.SubTaskConfig, etcdClient *clientv3.Client, worker string, relay relay.Process) []unit.Unit { 307 return []unit.Unit{mockDumper, mockLoader} 308 } 309 310 st.Run(pb.Stage_Running, pb.Stage_Running, nil) 311 c.Assert(st.Stage(), Equals, pb.Stage_Running) 312 c.Assert(st.CurrUnit(), Equals, mockDumper) 313 c.Assert(st.Result(), IsNil) 314 c.Assert(st.CheckUnit(), IsFalse) 315 316 // pause twice 317 c.Assert(st.Pause(), IsNil) 318 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 319 c.Assert(st.CurrUnit(), Equals, mockDumper) 320 if st.Result() != nil && (!st.Result().IsCanceled || len(st.Result().Errors) > 0) { 321 c.Fatalf("result %+v is not right after closing", st.Result()) 322 } 323 324 c.Assert(st.Pause(), NotNil) 325 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 326 c.Assert(st.CurrUnit(), Equals, mockDumper) 327 if st.Result() != nil && (!st.Result().IsCanceled || len(st.Result().Errors) > 0) { 328 c.Fatalf("result %+v is not right after closing", st.Result()) 329 } 330 331 // resume 332 c.Assert(st.Resume(nil), IsNil) 333 c.Assert(st.Stage(), Equals, pb.Stage_Running) 334 c.Assert(st.CurrUnit(), Equals, mockDumper) 335 c.Assert(st.Result(), IsNil) 336 337 c.Assert(st.Pause(), IsNil) 338 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 339 c.Assert(st.CurrUnit(), Equals, mockDumper) 340 if st.Result() != nil && (!st.Result().IsCanceled || len(st.Result().Errors) > 0) { 341 c.Fatalf("result %+v is not right after closing", st.Result()) 342 } 343 344 // resume 345 c.Assert(st.Resume(nil), IsNil) 346 c.Assert(st.Stage(), Equals, pb.Stage_Running) 347 c.Assert(st.CurrUnit(), Equals, mockDumper) 348 c.Assert(st.Result(), IsNil) 349 350 // fail dumper 351 c.Assert(mockDumper.InjectProcessError(context.Background(), errors.New("dumper process error")), IsNil) 352 // InjectProcessError need 1 second, here we wait 1.5 second 353 utils.WaitSomething(15, 100*time.Millisecond, func() bool { 354 return st.Result() != nil 355 }) 356 c.Assert(st.CurrUnit(), Equals, mockDumper) 357 c.Assert(st.Result(), NotNil) 358 c.Assert(st.Result().Errors, HasLen, 1) 359 c.Assert(strings.Contains(st.Result().Errors[0].Message, "dumper process error"), IsTrue) 360 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 361 362 // pause 363 c.Assert(st.Pause(), IsNil) 364 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 365 c.Assert(st.CurrUnit(), Equals, mockDumper) 366 c.Assert(st.Result(), NotNil) 367 c.Assert(st.Result().Errors, HasLen, 1) 368 c.Assert(st.Result().IsCanceled, IsTrue) 369 c.Assert(strings.Contains(st.Result().Errors[0].Message, "dumper process error"), IsTrue) 370 371 // resume twice 372 c.Assert(st.Resume(nil), IsNil) 373 c.Assert(st.Stage(), Equals, pb.Stage_Running) 374 c.Assert(st.CurrUnit(), Equals, mockDumper) 375 c.Assert(st.Result(), IsNil) 376 377 c.Assert(st.Resume(nil), NotNil) 378 c.Assert(st.Stage(), Equals, pb.Stage_Running) 379 c.Assert(st.CurrUnit(), Equals, mockDumper) 380 c.Assert(st.Result(), IsNil) 381 // finish dump 382 c.Assert(mockDumper.InjectProcessError(context.Background(), nil), IsNil) 383 utils.WaitSomething(20, 50*time.Millisecond, func() bool { 384 return st.CurrUnit().Type() == pb.UnitType_Load 385 }) 386 c.Assert(st.CurrUnit(), Equals, mockLoader) 387 c.Assert(st.Result(), IsNil) 388 c.Assert(st.Stage(), Equals, pb.Stage_Running) 389 390 // finish loader 391 c.Assert(mockLoader.InjectProcessError(context.Background(), nil), IsNil) 392 utils.WaitSomething(20, 50*time.Millisecond, func() bool { 393 return st.Stage() == pb.Stage_Finished 394 }) 395 c.Assert(st.CurrUnit(), Equals, mockLoader) 396 c.Assert(st.Stage(), Equals, pb.Stage_Finished) 397 c.Assert(st.Result().Errors, HasLen, 0) 398 399 st.Run(pb.Stage_Finished, pb.Stage_Stopped, nil) 400 c.Assert(st.CurrUnit(), Equals, mockLoader) 401 c.Assert(st.Stage(), Equals, pb.Stage_Finished) 402 c.Assert(st.Result().Errors, HasLen, 0) 403 } 404 405 func (t *testSubTask) TestSubtaskWithStage(c *C) { 406 cfg := &config.SubTaskConfig{ 407 SourceID: "source", 408 Name: "testSubtaskScene", 409 Mode: config.ModeFull, 410 } 411 c.Assert(cfg.Adjust(false), IsNil) 412 413 st := NewSubTaskWithStage(cfg, pb.Stage_Paused, nil, "worker") 414 c.Assert(st.Stage(), DeepEquals, pb.Stage_Paused) 415 416 mockDumper := NewMockUnit(pb.UnitType_Dump) 417 mockLoader := NewMockUnit(pb.UnitType_Load) 418 defer func() { 419 createUnits = createRealUnits 420 }() 421 createUnits = func(cfg *config.SubTaskConfig, etcdClient *clientv3.Client, worker string, relay relay.Process) []unit.Unit { 422 return []unit.Unit{mockDumper, mockLoader} 423 } 424 425 // pause 426 c.Assert(st.Pause(), NotNil) 427 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 428 c.Assert(st.CurrUnit(), Equals, nil) 429 c.Assert(st.Result(), IsNil) 430 431 c.Assert(st.Resume(nil), IsNil) 432 c.Assert(st.Stage(), Equals, pb.Stage_Running) 433 c.Assert(st.CurrUnit(), Equals, mockDumper) 434 c.Assert(st.Result(), IsNil) 435 436 // pause again 437 c.Assert(st.Pause(), IsNil) 438 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 439 c.Assert(st.CurrUnit(), Equals, mockDumper) 440 if st.Result() != nil && (!st.Result().IsCanceled || len(st.Result().Errors) > 0) { 441 c.Fatalf("result %+v is not right after closing", st.Result()) 442 } 443 444 st = NewSubTaskWithStage(cfg, pb.Stage_Finished, nil, "worker") 445 c.Assert(st.Stage(), DeepEquals, pb.Stage_Finished) 446 createUnits = func(cfg *config.SubTaskConfig, etcdClient *clientv3.Client, worker string, relay relay.Process) []unit.Unit { 447 return []unit.Unit{mockDumper, mockLoader} 448 } 449 450 st.Run(pb.Stage_Finished, pb.Stage_Stopped, nil) 451 c.Assert(st.Stage(), Equals, pb.Stage_Finished) 452 c.Assert(st.CurrUnit(), Equals, nil) 453 c.Assert(st.Result(), IsNil) 454 } 455 456 func (t *testSubTask) TestSubtaskFastQuit(c *C) { 457 // case: test subtask stuck into unitTransWaitCondition 458 cfg := &config.SubTaskConfig{ 459 Name: "testSubtaskFastQuit", 460 Mode: config.ModeAll, 461 } 462 463 ctx, cancel := context.WithCancel(context.Background()) 464 defer cancel() 465 466 w := &SourceWorker{ 467 ctx: ctx, 468 // loadStatus relay MetaBinlog must be greater 469 relayHolder: NewDummyRelayHolderWithRelayBinlog(config.NewSourceConfig(), relayHolderBinlog), 470 } 471 InitConditionHub(w) 472 473 mockLoader := NewMockUnit(pb.UnitType_Load) 474 mockSyncer := NewMockUnit(pb.UnitType_Sync) 475 476 st := NewSubTaskWithStage(cfg, pb.Stage_Paused, nil, "worker") 477 st.prevUnit = mockLoader 478 st.currUnit = mockSyncer 479 480 finished := make(chan struct{}) 481 go func() { 482 st.run() 483 close(finished) 484 }() 485 486 // test Pause 487 time.Sleep(time.Second) // wait for task to run for some time 488 c.Assert(st.Stage(), Equals, pb.Stage_Running) 489 c.Assert(st.Pause(), IsNil) 490 select { 491 case <-time.After(500 * time.Millisecond): 492 c.Fatal("fail to pause subtask in 0.5s when stuck into unitTransWaitCondition") 493 case <-finished: 494 } 495 c.Assert(st.Stage(), Equals, pb.Stage_Paused) 496 497 st = NewSubTaskWithStage(cfg, pb.Stage_Paused, nil, "worker") 498 st.units = []unit.Unit{mockLoader, mockSyncer} 499 st.prevUnit = mockLoader 500 st.currUnit = mockSyncer 501 502 finished = make(chan struct{}) 503 go func() { 504 st.run() 505 close(finished) 506 }() 507 508 c.Assert(utils.WaitSomething(10, 100*time.Millisecond, func() bool { 509 return st.Stage() == pb.Stage_Running 510 }), IsTrue) 511 // test Close 512 st.Close() 513 select { 514 case <-time.After(500 * time.Millisecond): 515 c.Fatal("fail to stop subtask in 0.5s when stuck into unitTransWaitCondition") 516 case <-finished: 517 } 518 c.Assert(st.Stage(), Equals, pb.Stage_Stopped) 519 } 520 521 func TestGetValidatorError(t *testing.T) { 522 cfg := &config.SubTaskConfig{ 523 Name: "test-validate-error", 524 ValidatorCfg: config.ValidatorConfig{ 525 Mode: config.ValidationFast, 526 }, 527 } 528 st := NewSubTaskWithStage(cfg, pb.Stage_Paused, nil, "worker") 529 // validator == nil 530 validatorErrs, err := st.GetValidatorError(pb.ValidateErrorState_InvalidErr) 531 require.Nil(t, validatorErrs) 532 require.True(t, terror.ErrValidatorNotFound.Equal(err)) 533 // validator != nil: will be tested in IT 534 } 535 536 func TestOperateValidatorError(t *testing.T) { 537 cfg := &config.SubTaskConfig{ 538 Name: "test-validate-error", 539 ValidatorCfg: config.ValidatorConfig{ 540 Mode: config.ValidationFast, 541 }, 542 } 543 st := NewSubTaskWithStage(cfg, pb.Stage_Paused, nil, "worker") 544 // validator == nil 545 require.True(t, terror.ErrValidatorNotFound.Equal(st.OperateValidatorError(pb.ValidationErrOp_ClearErrOp, 0, true))) 546 // validator != nil: will be tested in IT 547 } 548 549 func TestValidatorStatus(t *testing.T) { 550 cfg := &config.SubTaskConfig{ 551 Name: "test-validate-status", 552 ValidatorCfg: config.ValidatorConfig{ 553 Mode: config.ValidationFast, 554 }, 555 } 556 st := NewSubTaskWithStage(cfg, pb.Stage_Paused, nil, "worker") 557 // validator == nil 558 stats, err := st.GetValidatorStatus() 559 require.Nil(t, stats) 560 require.True(t, terror.ErrValidatorNotFound.Equal(err)) 561 // validator != nil: will be tested in IT 562 } 563 564 func TestSubtaskRace(t *testing.T) { 565 // to test data race of Marshal() and markResultCanceled() 566 tempErrors := []*pb.ProcessError{} 567 tempDetail := []byte{} 568 tempProcessResult := pb.ProcessResult{ 569 IsCanceled: false, 570 Errors: tempErrors, 571 Detail: tempDetail, 572 } 573 cfg := &config.SubTaskConfig{ 574 Name: "test-subtask-race", 575 ValidatorCfg: config.ValidatorConfig{ 576 Mode: config.ValidationFast, 577 }, 578 } 579 st := NewSubTaskWithStage(cfg, pb.Stage_Paused, nil, "worker") 580 st.result = &tempProcessResult 581 tempQueryStatusResponse := pb.QueryStatusResponse{} 582 tempQueryStatusResponse.SubTaskStatus = make([]*pb.SubTaskStatus, 1) 583 tempSubTaskStatus := pb.SubTaskStatus{} 584 tempSubTaskStatus.Result = st.Result() 585 tempQueryStatusResponse.SubTaskStatus[0] = &tempSubTaskStatus 586 st.result.IsCanceled = false 587 go func() { 588 for i := 0; i < 10; i++ { 589 _, _ = tempQueryStatusResponse.Marshal() 590 } 591 }() 592 st.markResultCanceled() 593 // this test is to test data race, so don't need assert here 594 }