AWS SWF Simple Workflow — A Demonstration Using AWS CLI

Let us demystify SWF without having to use Python, PHP, Ruby, Java etc.

Image for post
Image for post

Though this demo is not meant to be an explanation of AWS SWF, let me quickly summarize the key components of SWF.

First What is a Workflow?

A workflow is a sequence of actions like in a flow chart. It has decision making actions and activity actions.

What is AWS SWF?

You can think of it as a static repository that maintains the state of any workflow. It has no computing elements (just to keep it simple). It maintains the entire history of the workflow.

We need to do all the decisions and actions need to happen in the workflow!

AWS CLI Based SWF Demonstration

In our demo we will just pass a variable containing a number depicting the hour of the day to our workflow. Then the workflow will decide how to greet us and start the activity to greet us!

SWF Domain: Controls a workflow’s scope. Let us create a domain to encapsulate our workflow.

dom=MyGreetingSWF
aws swf register-domain --name $dom --workflow-execution-retention-period-in-days 1

SWF WorkFlowType: Each SWF Domain must have one work flow type registered. This actually is just one sequence of actions or one flowchart, just to understand it. Let us create our work flow type under our SWF domain. We will keep the version as v1 hard coded to keep it simple.

We will also need to task list, where all our tasks will stay. Essentially here is the association of the work flow and a collection of tasks. One workflow can have multiple task lists.

wf=DailyGreetingWorkflow
tl=GreetTaskList
aws swf register-workflow-type --domain $dom --name $wf --workflow-version "v1" --default-task-list name=$tl

SWF ActivityType: Activity type is any specific activity that you want to perform in a work flow, in our case we will call it ShowGreatingsActivityType. This also versioned like the workflow type, let us hard code this as av1.

at=ShowGreatingsActivityType
aws swf register-activity-type --domain $dom --name $at --activity-version av1 --default-task-list name=$tl

Start a Workflow Execution: This is what initializes the workflow’s beginning. We will send an input variable 9 to indicate 9am as the parameter for our workflow’s execution. We will hard code the workflow id to wf-id-01. We will get the id of this execution state as runID as seen below. Let us save this to a variable.

input=9
aws swf start-workflow-execution --domain $dom --workflow-id wf-id-01 --workflow-type name=$wf,version=v1 --task-list name=$tl --input $input --execution-start-to-close-timeout 600 --task-start-to-close-timeout 600 --child-policy ABANDON
{
"runId": "23f6U6YRO+9M0wmmEpULgEUE07W7eTkF8saZF2CpWrunQ="
}
runId="23f6U6YRO+9M0wmmEpULgEUE07W7eTkF8saZF2CpWrunQ="

Events in History

If we look at the events in the history you will see these two events.

  1. WorkflowExecutionStarted
  2. DecisionTaskScheduled
aws swf get-workflow-execution-history  --domain $dom --execution workflowId=wf-id-01,runId=$runId
{
"events": [
{
"eventTimestamp": 1542552218.283,
"eventType": "WorkflowExecutionStarted",
"eventId": 1,
"workflowExecutionStartedEventAttributes": {
"input": "9",
"executionStartToCloseTimeout": "600",
"taskStartToCloseTimeout": "600",
"childPolicy": "ABANDON",
"taskList": {
"name": "GreetTaskList"
},
"workflowType": {
"name": "DailyGreetingWorkflow",
"version": "v1"
},
"parentInitiatedEventId": 0
}
},
{
"eventTimestamp": 1542552218.283,
"eventType": "DecisionTaskScheduled",
"eventId": 2,
"decisionTaskScheduledEventAttributes": {
"taskList": {
"name": "GreetTaskList"
},
"startToCloseTimeout": "600"
}
}
]
}

Begin Polling: First task in a SWF workflow will always be a decision task, we will need to use same task list we have used.

In the output of this command we can see these events.

  1. WorkflowExecutionStarted
  2. DecisionTaskScheduled
  3. DecisionTaskStarted

We also get the taskToken.

aws swf poll-for-decision-task --domain $dom --task-list name=$tl{
"events": [
{
"eventTimestamp": 1542363855.6,
"eventType": "WorkflowExecutionStarted",
"eventId": 1,
"workflowExecutionStartedEventAttributes": {
"input": "9",
"executionStartToCloseTimeout": "600",
"taskStartToCloseTimeout": "600",
"childPolicy": "ABANDON",
"taskList": {
"name": "GreetTaskList"
},
"workflowType": {
"name": "DailyGreetingWorkflow",
"version": "v1"
},
"parentInitiatedEventId": 0
}
},
{
"eventTimestamp": 1542363855.6,
"eventType": "DecisionTaskScheduled",
"eventId": 2,
"decisionTaskScheduledEventAttributes": {
"taskList": {
"name": "GreetTaskList"
},
"startToCloseTimeout": "600"
}
},
{
"eventTimestamp": 1542363859.063,
"eventType": "DecisionTaskStarted",
"eventId": 3,
"decisionTaskStartedEventAttributes": {
"scheduledEventId": 2
}
}
],
"taskToken": "AAAAKgAAAAIAAAAAAAAAA3P11zBUr7GW6tcACtZHcMl9WsjCRO+aYyn908OXBQwsUa4TN0xNsuZxUeM1/etilQnZEmfc8kaDhhJuAw9wQCN38EddOn7H4FCE0lS4dz9An0FrSxbFO72dezZaFr4QSFELoqkWl05mhZObDlNV2WQgWJVdtoXHgWr16v1fPygT4qxMnpwcphCuvV6bLF8r4Lb8V9tEvAOFCdewduVxqrocZCx4wpJHUq8l6Cr/AtF/8IDYjpVlpazOlnKUN7bhrp41ERXuz/5G8dKgHg18shclTLPGiyrLKPF32ryatzmj",
"startedEventId": 3,
"workflowExecution": {
"workflowId": "wf-id-01",
"runId": "23f6U6YRO+9M0wmmEpULgEUE07W7eTkF8saZF2CpWrunQ="
},
"workflowType": {
"name": "DailyGreetingWorkflow",
"version": "v1"
},
"previousStartedEventId": 0
}
taskToken="AAAAKgAAAAIAAAAAAAAAA3P11zBUr7GW6tcACtZHcMl9WsjCRO+aYyn908OXBQwsUa4TN0xNsuZxUeM1/etilQnZEmfc8kaDhhJuAw9wQCN38EddOn7H4FCE0lS4dz9An0FrSxbFO72dezZaFr4QSFELoqkWl05mhZObDlNV2WQgWJVdtoXHgWr16v1fPygT4qxMnpwcphCuvV6bLF8r4Lb8V9tEvAOFCdewduVxqrocZCx4wpJHUq8l6Cr/AtF/8IDYjpVlpazOlnKUN7bhrp41ERXuz/5G8dKgHg18shclTLPGiyrLKPF32ryatzmj"

Now, in real use case scenarios, our application program will receive the above output. It can be parsed to fetch the input variable passed under the event id 1 in out case we get the value 9.

Now the decision making application (decision worker) will decide the greeting to be used. eg: as seen below.

decInput=$(cat poll-for-decision-task.out.txt | jq ".events[0].workflowExecutionStartedEventAttributes.input"|sed 's/[",A-Z]//g')
if [ $decInput -lt 10 ]; then
inp="Good morning!"
elif [ $decInput -ge 10 -a $decInput -lt 5 ]; then
inp="Good afternoon!"
else
inp="Good evening!"
fi

Now, the decided value $inp will be passed back to the workflow so that it can allow the activity poller to pass it. This time we will create the JSON file since there are many more parameters in this command.

cat > swf-rdtc.json <<EOT
{
"taskToken": "$taskToken",
"decisions": [
{
"decisionType": "ScheduleActivityTask",
"scheduleActivityTaskDecisionAttributes": {
"activityType": {
"name": "$at",
"version": "av1"
},
"activityId": "SreeACT-ID",
"control": "Sree Control",
"input": "$inp",
"scheduleToCloseTimeout": "600",
"taskList": {
"name": "$tl"
},
"scheduleToStartTimeout": "600",
"startToCloseTimeout": "600",
"heartbeatTimeout": "600"
}
}
]
}
EOT

Now using the above JSON we will call the respond decision task completed function. This call will ready the event for the activity worker.

aws swf respond-decision-task-completed --cli-input-json file://./swf-rdtc.json

Events in History

Let us see how the history looks now. You can see DecisionTaskCompleted and ActivityTaskScheduled events! It also has our input from the decision task, Good Morning.

aws swf get-workflow-execution-history  --domain $dom --execution workflowId=wf-id-01,runId=$runId
{
"events": [
{
"eventTimestamp": 1542553244.86,
"eventType": "WorkflowExecutionStarted",
"eventId": 1,
"workflowExecutionStartedEventAttributes": {
"input": "9",
"executionStartToCloseTimeout": "900",
"taskStartToCloseTimeout": "900",
"childPolicy": "ABANDON",
"taskList": {
"name": "GreetTaskList"
},
"workflowType": {
"name": "DailyGreetingWorkflow",
"version": "v1"
},
"parentInitiatedEventId": 0
}
},
{
"eventTimestamp": 1542553244.86,
"eventType": "DecisionTaskScheduled",
"eventId": 2,
"decisionTaskScheduledEventAttributes": {
"taskList": {
"name": "GreetTaskList"
},
"startToCloseTimeout": "900"
}
},
{
"eventTimestamp": 1542553272.704,
"eventType": "DecisionTaskStarted",
"eventId": 3,
"decisionTaskStartedEventAttributes": {
"scheduledEventId": 2
}
},
{
"eventTimestamp": 1542553348.896,
"eventType": "DecisionTaskCompleted",
"eventId": 4,
"decisionTaskCompletedEventAttributes": {
"scheduledEventId": 2,
"startedEventId": 3
}
},
{
"eventTimestamp": 1542553348.896,
"eventType": "ActivityTaskScheduled",
"eventId": 5,
"activityTaskScheduledEventAttributes": {
"activityType": {
"name": "ShowGreatingsActivityType",
"version": "av1"
},
"activityId": "SreeACT-ID",
"input": "Good morning",
"control": "Sree Control",
"scheduleToStartTimeout": "600",
"scheduleToCloseTimeout": "600",
"startToCloseTimeout": "600",
"taskList": {
"name": "GreetTaskList"
},
"decisionTaskCompletedEventId": 4,
"heartbeatTimeout": "600"
}
}
]
}

Polling for Activity: Once the decision has been completed the activity worker application can poll for any activity to be done. In the output JSON we can see the “Good morning!” being passed by the decision box!

aws swf poll-for-activity-task --domain $dom --task-list name=$tl{
"taskToken": "AAAAKgAAAAIAAAAAAAAAA6a9+JJ5FqXijFbnrb1JMLF7hV1Z8m5OGA5be3i6agq1yBui6Jyu0ydWLyBrTto3vxIFfJOpgtl6RQBPeof4ZdzVuM5KUU+A3ga1LrXaP8XT0uvTGDWsbwS98aTd2XFU0Dbi3Iyh0xriCIweADOHlEn11IkXW6MoCkxbvoTENRVOFMqQvc/vCKFOttyDooHaOLFbfTfo6T+q8rj9Dl4IYZzplReWfxr7ZGGraHBNaG/Y53uxoYqCn5W7sUy9R2ctV92VI3x9GW/OLLLblZOUv7JnMgqvFTtQI1TsKOkrqI43",
"activityId": "SreeACT-ID",
"startedEventId": 6,
"workflowExecution": {
"workflowId": "wf-id-01",
"runId": "23f6U6YRO+9M0wmmEpULgEUE07W7eTkF8saZF2CpWrunQ="
},
"activityType": {
"name": "ShowGreatingsActivityType",
"version": "av1"
},
"input": "Good morning!"
}
taskToken2="AAAAKgAAAAIAAAAAAAAAA6a9+JJ5FqXijFbnrb1JMLF7hV1Z8m5OGA5be3i6agq1yBui6Jyu0ydWLyBrTto3vxIFfJOpgtl6RQBPeof4ZdzVuM5KUU+A3ga1LrXaP8XT0uvTGDWsbwS98aTd2XFU0Dbi3Iyh0xriCIweADOHlEn11IkXW6MoCkxbvoTENRVOFMqQvc/vCKFOttyDooHaOLFbfTfo6T+q8rj9Dl4IYZzplReWfxr7ZGGraHBNaG/Y53uxoYqCn5W7sUy9R2ctV92VI3x9GW/OLLLblZOUv7JnMgqvFTtQI1TsKOkrqI43"

Events in History

At this stage let us see the events added to the history. You will see ActivityTaskStarted event now.

aws swf get-workflow-execution-history  --domain $dom --execution workflowId=wf-id-01,runId=$runId
{
"events": [
{
"eventTimestamp": 1542553244.86,
"eventType": "WorkflowExecutionStarted",
"eventId": 1,
"workflowExecutionStartedEventAttributes": {
"input": "9",
"executionStartToCloseTimeout": "900",
"taskStartToCloseTimeout": "900",
"childPolicy": "ABANDON",
"taskList": {
"name": "GreetTaskList"
},
"workflowType": {
"name": "DailyGreetingWorkflow",
"version": "v1"
},
"parentInitiatedEventId": 0
}
},
{
"eventTimestamp": 1542553244.86,
"eventType": "DecisionTaskScheduled",
"eventId": 2,
"decisionTaskScheduledEventAttributes": {
"taskList": {
"name": "GreetTaskList"
},
"startToCloseTimeout": "900"
}
},
{
"eventTimestamp": 1542553272.704,
"eventType": "DecisionTaskStarted",
"eventId": 3,
"decisionTaskStartedEventAttributes": {
"scheduledEventId": 2
}
},
{
"eventTimestamp": 1542553348.896,
"eventType": "DecisionTaskCompleted",
"eventId": 4,
"decisionTaskCompletedEventAttributes": {
"scheduledEventId": 2,
"startedEventId": 3
}
},
{
"eventTimestamp": 1542553348.896,
"eventType": "ActivityTaskScheduled",
"eventId": 5,
"activityTaskScheduledEventAttributes": {
"activityType": {
"name": "ShowGreatingsActivityType",
"version": "av1"
},
"activityId": "SreeACT-ID",
"input": "Good morning",
"control": "Sree Control",
"scheduleToStartTimeout": "600",
"scheduleToCloseTimeout": "600",
"startToCloseTimeout": "600",
"taskList": {
"name": "GreetTaskList"
},
"decisionTaskCompletedEventId": 4,
"heartbeatTimeout": "600"
}
},
{
"eventTimestamp": 1542553476.907,
"eventType": "ActivityTaskStarted",
"eventId": 6,
"activityTaskStartedEventAttributes": {
"scheduledEventId": 5
}
}
]
}

Here the activity worker gets the message passed, in our case “Good Morning!” and may speak out loud. That completes our demo work flow execution.

Task Completion: We can now call the task completion command.

aws swf respond-activity-task-completed --task-token=$taskToken2

Events in History

Image for post
Image for post

Now you will see the magic, immediately after ActivityTaskCompleted SWF has schdueled an DecisionTaskScheduled! So it alternates between decision tasks and activity tasks as the flow progresses.

aws swf get-workflow-execution-history  --domain $dom --execution workflowId=wf-id-01,runId=$runId
{
"events": [
{
"eventTimestamp": 1542553244.86,
"eventType": "WorkflowExecutionStarted",
"eventId": 1,
"workflowExecutionStartedEventAttributes": {
"input": "9",
"executionStartToCloseTimeout": "900",
"taskStartToCloseTimeout": "900",
"childPolicy": "ABANDON",
"taskList": {
"name": "GreetTaskList"
},
"workflowType": {
"name": "DailyGreetingWorkflow",
"version": "v1"
},
"parentInitiatedEventId": 0
}
},
{
"eventTimestamp": 1542553244.86,
"eventType": "DecisionTaskScheduled",
"eventId": 2,
"decisionTaskScheduledEventAttributes": {
"taskList": {
"name": "GreetTaskList"
},
"startToCloseTimeout": "900"
}
},
{
"eventTimestamp": 1542553272.704,
"eventType": "DecisionTaskStarted",
"eventId": 3,
"decisionTaskStartedEventAttributes": {
"scheduledEventId": 2
}
},
{
"eventTimestamp": 1542553348.896,
"eventType": "DecisionTaskCompleted",
"eventId": 4,
"decisionTaskCompletedEventAttributes": {
"scheduledEventId": 2,
"startedEventId": 3
}
},
{
"eventTimestamp": 1542553348.896,
"eventType": "ActivityTaskScheduled",
"eventId": 5,
"activityTaskScheduledEventAttributes": {
"activityType": {
"name": "ShowGreatingsActivityType",
"version": "av1"
},
"activityId": "SreeACT-ID",
"input": "Good morning",
"control": "Sree Control",
"scheduleToStartTimeout": "600",
"scheduleToCloseTimeout": "600",
"startToCloseTimeout": "600",
"taskList": {
"name": "GreetTaskList"
},
"decisionTaskCompletedEventId": 4,
"heartbeatTimeout": "600"
}
},
{
"eventTimestamp": 1542553476.907,
"eventType": "ActivityTaskStarted",
"eventId": 6,
"activityTaskStartedEventAttributes": {
"scheduledEventId": 5
}
},
{
"eventTimestamp": 1542553645.283,
"eventType": "ActivityTaskCompleted",
"eventId": 7,
"activityTaskCompletedEventAttributes": {
"scheduledEventId": 5,
"startedEventId": 6
}
},
{
"eventTimestamp": 1542553645.283,
"eventType": "DecisionTaskScheduled",
"eventId": 8,
"decisionTaskScheduledEventAttributes": {
"taskList": {
"name": "GreetTaskList"
},
"startToCloseTimeout": "900"
}
}
]
}

Cleaning up

aws swf terminate-workflow-execution --domain $dom --workflow-id wf-id-01aws swf deprecate-activity-type --domain $dom --activity-type name=$at,version=av1aws swf deprecate-workflow-type --domain $dom --workflow-type name=$wf,version=v1aws swf deprecate-domain --name $domaws swf list-domains --registration-status REGISTERED
{
"domainInfos": []
}
aws swf list-workflow-types --domain $dom --registration-status REGISTERED
{
"typeInfos": []
}
aws swf list-activity-types --domain $dom --registration-status REGISTERED
{
"typeInfos": []
}

When you study AWS SWF you can use the above demo to easily understand what is happening behind the seen. Hope this was useful? for more such demos remember to follow. Thank you!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store