mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
Fix TestWorkflowStartConflict flaky test (#9776)
## What changed? Fix flaky test `TestUpdateWithStartSuite/TestWorkflowStartConflict/workflow_id_conflict_policy_fail:_use-existing` handling both possible orderings of the race condition it tests. ## Why? The test injects a hook (`UpdateWithStartInBetweenLockAndStart`) that fires a concurrent `StartWorkflowExecution` between the update-with-start lock acquisition and start attempt to simulate a race condition. The test assumes the update always lands in a second speculative WFT: 1. WFT #1 polled with no messages → completed (empty) 2. UWS retry creates speculative WFT #2 → polled with update → accepted/completed This assumption broke after the `parallelsuite` migration, which gives each test its own isolated `testcore.NewEnv` running in parallel. With a dedicated env, the retryable history client's immediate retry of the Unavailable error races tightly with `RecordWorkflowTaskStarted`: - Ordering A (retry wins the lock first): the update is admitted while WFT #1 is still scheduled, so it attaches to WFT #1's messages when the worker polls it. No WFT #2 is ever created. - Ordering B (RecordWorkflowTaskStarted wins): WFT #1 starts and completes with no messages, then the retry runs with no pending WFT, creating speculative WFT #2 with the update. The original two-poll design panicked in ordering A (index out of range [0] with length 0 on task.Messages[0] in the empty-response first poll). The initial single-poll fix panicked in ordering B for the same reason. Neither ordering is guaranteed, so the test must handle both. ## How did you test it? - [ ] built - [X] run locally and tested manually - tested with `-count=50` locally - [ ] covered by existing tests - [ ] added new unit test(s) - [ ] added new functional test(s) ## Potential risks None, test only
This commit is contained in:
@@ -5596,21 +5596,32 @@ func (s *UpdateWithStartSuite) TestWorkflowStartConflict() {
|
||||
|
||||
uwsCh := s.sendUpdateWithStart(env, startReq, updateReq)
|
||||
|
||||
_, err := env.TaskPoller().PollAndHandleWorkflowTask(env.Tv(),
|
||||
func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) {
|
||||
return &workflowservice.RespondWorkflowTaskCompletedRequest{}, nil
|
||||
})
|
||||
s.NoError(err)
|
||||
// Two orderings are possible depending on lock timing:
|
||||
// A) UWS retry acquires the workflow lock before RecordWorkflowTaskStarted: update is admitted
|
||||
// while WFT #1 is still scheduled, so it attaches to WFT #1 messages when WFT #1 starts.
|
||||
// B) RecordWorkflowTaskStarted wins the lock first: WFT #1 starts with no messages, completes,
|
||||
// then UWS retry runs with no pending WFT and creates a speculative WFT #2 for the update.
|
||||
updateHandled := false
|
||||
for range 2 {
|
||||
_, err := env.TaskPoller().PollAndHandleWorkflowTask(env.Tv(),
|
||||
func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) {
|
||||
if len(task.Messages) == 0 {
|
||||
return &workflowservice.RespondWorkflowTaskCompletedRequest{}, nil
|
||||
}
|
||||
updateHandled = true
|
||||
return &workflowservice.RespondWorkflowTaskCompletedRequest{
|
||||
Messages: env.UpdateAcceptCompleteMessages(env.Tv(), task.Messages[0]),
|
||||
}, nil
|
||||
})
|
||||
s.NoError(err)
|
||||
if updateHandled {
|
||||
break
|
||||
}
|
||||
}
|
||||
s.True(updateHandled, "update not processed in either WFT")
|
||||
|
||||
_, err = env.TaskPoller().PollAndHandleWorkflowTask(env.Tv(),
|
||||
func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) {
|
||||
return &workflowservice.RespondWorkflowTaskCompletedRequest{
|
||||
Messages: env.UpdateAcceptCompleteMessages(env.Tv(), task.Messages[0]),
|
||||
}, nil
|
||||
})
|
||||
s.NoError(err)
|
||||
|
||||
<-uwsCh
|
||||
uwsRes := <-uwsCh
|
||||
s.NoError(uwsRes.err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user