1

I have created a simple SWF workflow but seem to be receiving multiple notifications of new Decision tasks available. I am using the boto3 python sdk.

There is no good boto3 swf sample code that I can find, so I started from the boto2 sample at http://boto.cloudhackers.com/en/latest/swf_tut.html.

I've created my simple workflow with this script which creates a domain, a workflow, and a single task in that workflow:

#!/usr/bin/python

import boto3
from botocore.exceptions import ClientError


swf = boto3.client('swf')

try:
  swf.register_domain(
    name="surroundiotest-swf",
    description="Surroundio test SWF domain",
    workflowExecutionRetentionPeriodInDays="10"
  )
except ClientError as e:
  print "Domain already exists: ", e.response.get("Error", {}).get("Code")

try:
  swf.register_workflow_type(
    domain="surroundiotest-swf",
    name="testflow",
    version="0.1",
    description="testworkflow",
    defaultExecutionStartToCloseTimeout="250",
    defaultTaskStartToCloseTimeout="NONE",
    defaultChildPolicy="TERMINATE",
    defaultTaskList={"name": "testflow"}
  )
  print "testflow created!"
except ClientError as e:
  print "Workflow already exists: ", e.response.get("Error", {}).get("Code")

try:
  swf.register_activity_type(
    domain="surroundiotest-swf",
    name="testworker",
    version="0.1",
    description="testworker",
    defaultTaskStartToCloseTimeout="NONE",
    defaultTaskList={"name": "testflow"}
  )
  print "testworker created!"
except ClientError as e:
  print "Activity already exists: ", e.response.get("Error", {}).get("Code")

my worker code:

#!/usr/bin/python

import boto3
from botocore.client import Config

botoConfig = Config(connect_timeout=50, read_timeout=70)
swf = boto3.client('swf', config=botoConfig)

print "Listening for Worker Tasks"

while True:

  task = swf.poll_for_activity_task(
    domain='surroundiotest-swf',
    taskList={'name': 'testflow'},
    identity='worker-1')

  if 'taskToken' not in task:
    print "Poll timed out, no new task.  Repoll"

  else:
    print "New task arrived"

    swf.respond_activity_task_completed(
        taskToken=task['taskToken'],
        result='success'
    )

    print "Task Done"

my decider code:

#!/usr/bin/python

import boto3
from botocore.client import Config

botoConfig = Config(connect_timeout=50, read_timeout=70)
swf = boto3.client('swf', config=botoConfig)


print "Listening for Decision Tasks"

while True:

  newTask = swf.poll_for_decision_task(
    domain='surroundiotest-swf',
    taskList={'name': 'testflow'},
    identity='decider-1',
    reverseOrder=True)

  if 'taskToken' not in newTask:
    print "Poll timed out, no new task.  Repoll"

  elif 'events' in newTask:

    eventHistory = [evt for evt in newTask['events'] if not evt['eventType'].startswith('Decision')]
    lastEvent = eventHistory[-1]

    if lastEvent['eventType'] == 'WorkflowExecutionStarted':
      print "Dispatching task to worker", newTask['workflowExecution'], newTask['workflowType']
      swf.respond_decision_task_completed(
        taskToken=newTask['taskToken'],
        decisions=[
          {
            'decisionType': 'ScheduleActivityTask',
            'scheduleActivityTaskDecisionAttributes': {
                'activityType':{
                    'name': 'testworker',
                    'version': '0.1'
                    },
                'activityId': 'activityid-1001',
                'input': '',
                'scheduleToCloseTimeout': 'NONE',
                'scheduleToStartTimeout': 'NONE',
                'startToCloseTimeout': 'NONE',
                'heartbeatTimeout': 'NONE',
                'taskList': {'name': 'testflow'},
            }
          }
        ]
      )
      print "Task Dispatched"
      # print json.dumps(newTask, default=json_serial, sort_keys=True, indent=4, separators=(',', ': '))

    elif lastEvent['eventType'] == 'ActivityTaskCompleted':
      swf.respond_decision_task_completed(
        taskToken=newTask['taskToken'],
        decisions=[
          {
            'decisionType': 'CompleteWorkflowExecution',
            'completeWorkflowExecutionDecisionAttributes': {
              'result': 'success'
            }
          }
        ]
      )
      print "Task Completed!"

When I run these scripts, and submit a request, the decider receives the task and dispatches it, and the worker receives it and executes it. However, the decider is notified of the task on every subsequent poll, so I see output like this from my decider:

Listening for Decision Tasks
Poll timed out, no new task.  Repoll
Dispatching task to worker {u'workflowId': u'surroundtest-1001', u'runId': u'23oPrHZ/d9kR43V/hr0ykZCI7Dks/FzLhfDeA9PPWFuPE='} {u'version': u'0.1', u'name': u'testflow'}
Task Dispatched
Dispatching task to worker {u'workflowId': u'surroundtest-1001', u'runId': u'23oPrHZ/d9kR43V/hr0ykZCI7Dks/FzLhfDeA9PPWFuPE='} {u'version': u'0.1', u'name': u'testflow'}
Task Dispatched
Dispatching task to worker {u'workflowId': u'surroundtest-1001', u'runId': u'23oPrHZ/d9kR43V/hr0ykZCI7Dks/FzLhfDeA9PPWFuPE='} {u'version': u'0.1', u'name': u'testflow'}
Task Dispatched
Dispatching task to worker {u'workflowId': u'surroundtest-1001', u'runId': u'23oPrHZ/d9kR43V/hr0ykZCI7Dks/FzLhfDeA9PPWFuPE='} {u'version': u'0.1', u'name': u'testflow'}
Task Dispatched
Dispatching task to worker {u'workflowId': u'surroundtest-1001', u'runId': u'23oPrHZ/d9kR43V/hr0ykZCI7Dks/FzLhfDeA9PPWFuPE='} {u'version': u'0.1', u'name': u'testflow'}
Task Dispatched

It would seem that SWF is not receiving a notification that the decider has dispatched the task, and is continually delivering the task to the decider. Is there some piece of the decider setup I am doing wrong, is there some additional info I need to convey to SWF?

jhludwig
  • 45
  • 6

1 Answers1

1

You have a small bug. You are polling for decision tasks using reverseOrder=True. From the API documentation of PollForDecisionTask:

reverseOrder
When set to true, returns the events in reverse order. By default the results are returned in ascending order of the eventTimestamp of the events.

With reverseOrder=True you get the oldest event last, which is always WorkflowExecutionStarted. After the task is done, you always scheduled it again.

Typically, you want to use previousStartedEventId and handle only new events that happened after the previous decision task. Handling only the last activity task is not always enough, because there could have been multiple events to handle.

Kobi
  • 135,331
  • 41
  • 252
  • 292
  • Perfect, I don't know how I picked that setting up, but you have set me on the right path! Thanks much – jhludwig Feb 07 '16 at 20:31