SWF: Start just one decision task at a time (#5669)
This commit is contained in:
parent
6ab2497a12
commit
d6b4b9718d
@ -212,9 +212,27 @@ class SWFBackend(BaseBackend):
|
|||||||
#
|
#
|
||||||
# TODO: handle long polling (case 2) for decision tasks
|
# TODO: handle long polling (case 2) for decision tasks
|
||||||
candidates = []
|
candidates = []
|
||||||
for _task_list, tasks in domain.decision_task_lists.items():
|
|
||||||
if _task_list == task_list:
|
# Collect candidate scheduled tasks from open workflow executions
|
||||||
candidates += [t for t in tasks if t.state == "SCHEDULED"]
|
# matching the selected task list.
|
||||||
|
#
|
||||||
|
# If another decision task is already started, then no candidates
|
||||||
|
# will be produced for that workflow execution. This is because only one
|
||||||
|
# decision task can be started at any given time.
|
||||||
|
# See https://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-dev-tasks.html
|
||||||
|
for wfe in domain.workflow_executions:
|
||||||
|
if wfe.task_list == task_list and wfe.open:
|
||||||
|
wfe_candidates = []
|
||||||
|
found_started = False
|
||||||
|
for task in wfe.decision_tasks:
|
||||||
|
if task.state == "STARTED":
|
||||||
|
found_started = True
|
||||||
|
break
|
||||||
|
elif task.state == "SCHEDULED":
|
||||||
|
wfe_candidates.append(task)
|
||||||
|
if not found_started:
|
||||||
|
candidates += wfe_candidates
|
||||||
|
|
||||||
if any(candidates):
|
if any(candidates):
|
||||||
# TODO: handle task priorities (but not supported by boto for now)
|
# TODO: handle task priorities (but not supported by boto for now)
|
||||||
task = min(candidates, key=lambda d: d.scheduled_at)
|
task = min(candidates, key=lambda d: d.scheduled_at)
|
||||||
|
@ -68,6 +68,95 @@ def test_poll_for_decision_task_previous_started_event_id_boto3():
|
|||||||
assert resp["previousStartedEventId"] == 3
|
assert resp["previousStartedEventId"] == 3
|
||||||
|
|
||||||
|
|
||||||
|
@mock_swf
|
||||||
|
def test_poll_for_decision_task_ensure_single_started_task():
|
||||||
|
client = setup_workflow_boto3()
|
||||||
|
|
||||||
|
resp = client.poll_for_decision_task(
|
||||||
|
domain="test-domain", taskList={"name": "queue"}
|
||||||
|
)
|
||||||
|
resp.should.have.key("taskToken")
|
||||||
|
first_decision_task = resp["taskToken"]
|
||||||
|
|
||||||
|
# History should have just the decision task triggered on workflow start
|
||||||
|
types = [evt["eventType"] for evt in resp["events"]]
|
||||||
|
types.should.equal(
|
||||||
|
["WorkflowExecutionStarted", "DecisionTaskScheduled", "DecisionTaskStarted"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Schedule another decision task, before first one is completed
|
||||||
|
client.signal_workflow_execution(
|
||||||
|
domain="test-domain",
|
||||||
|
signalName="my_signal",
|
||||||
|
workflowId="uid-abcd1234",
|
||||||
|
input="my_input",
|
||||||
|
runId=client.run_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Second poll should return no new tasks
|
||||||
|
resp = client.poll_for_decision_task(
|
||||||
|
domain="test-domain", taskList={"name": "queue"}
|
||||||
|
)
|
||||||
|
assert resp["previousStartedEventId"] == 0
|
||||||
|
assert resp["startedEventId"] == 0
|
||||||
|
assert resp.should_not.have.key("taskToken")
|
||||||
|
|
||||||
|
resp = client.get_workflow_execution_history(
|
||||||
|
domain="test-domain",
|
||||||
|
execution={"runId": client.run_id, "workflowId": "uid-abcd1234"},
|
||||||
|
)
|
||||||
|
types = [evt["eventType"] for evt in resp["events"]]
|
||||||
|
types.should.equal(
|
||||||
|
[
|
||||||
|
"WorkflowExecutionStarted",
|
||||||
|
"DecisionTaskScheduled",
|
||||||
|
"DecisionTaskStarted",
|
||||||
|
"WorkflowExecutionSignaled",
|
||||||
|
"DecisionTaskScheduled",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
client.respond_decision_task_completed(taskToken=first_decision_task)
|
||||||
|
|
||||||
|
resp = client.poll_for_decision_task(
|
||||||
|
domain="test-domain", taskList={"name": "queue"}
|
||||||
|
)
|
||||||
|
resp.should.have.key("taskToken")
|
||||||
|
|
||||||
|
types = [evt["eventType"] for evt in resp["events"]]
|
||||||
|
types.should.equal(
|
||||||
|
[
|
||||||
|
"WorkflowExecutionStarted",
|
||||||
|
"DecisionTaskScheduled",
|
||||||
|
"DecisionTaskStarted",
|
||||||
|
"WorkflowExecutionSignaled",
|
||||||
|
"DecisionTaskScheduled",
|
||||||
|
"DecisionTaskCompleted",
|
||||||
|
"DecisionTaskStarted",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_swf
|
||||||
|
def test_poll_for_decision_task_exclude_completed_executions():
|
||||||
|
client = setup_workflow_boto3()
|
||||||
|
|
||||||
|
resp = client.get_workflow_execution_history(
|
||||||
|
domain="test-domain",
|
||||||
|
execution={"runId": client.run_id, "workflowId": "uid-abcd1234"},
|
||||||
|
)
|
||||||
|
types = [evt["eventType"] for evt in resp["events"]]
|
||||||
|
types.should.equal(["WorkflowExecutionStarted", "DecisionTaskScheduled"])
|
||||||
|
|
||||||
|
client.terminate_workflow_execution(
|
||||||
|
domain="test-domain", runId=client.run_id, workflowId="uid-abcd1234"
|
||||||
|
)
|
||||||
|
resp = client.poll_for_decision_task(
|
||||||
|
domain="test-domain", taskList={"name": "queue"}
|
||||||
|
)
|
||||||
|
resp.should_not.have.key("taskToken")
|
||||||
|
|
||||||
|
|
||||||
@mock_swf
|
@mock_swf
|
||||||
def test_poll_for_decision_task_when_none_boto3():
|
def test_poll_for_decision_task_when_none_boto3():
|
||||||
client = setup_workflow_boto3()
|
client = setup_workflow_boto3()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user