Implementing Workflows

A workflow represents the path of execution required to perform a sequence of tasks, which are usually activities, but which can also be child workflows (which might also have activities and child workflows of their own).

In some cases, you don't need to implement your own workflow. If you would like to run a single activity, you can use the start method to run a single activity using a default workflow. For more information, see the Hello World tutorial for an example, and Running Activities for detailed information about the AWS Flow Framework for Ruby-supplied default decider.

A Decider Implementation Defines Your Workflow#

At the center of a workflow implementation is your decider logic. Similarly to activities, you provide a workflow implementation by declaring a class based on the Workflows class and provide methods that define your deciders. For example, here is the implementation of the workflow used in the Booking sample:

require 'flow/activities'

# BookingWorkflow class defines the workflows for the Booking sample
class BookingWorkflow
  extend AWS::Flow::Workflows

  # Use the workflow method to define workflow entry point.
  workflow :make_booking do
    {
      version: "1.0",
      default_execution_start_to_close_timeout: 120
    }
  end

  # Create an activity client using the activity_client method to schedule
  # activities
  activity_client(:client) { { from_class: "BookingActivity" } }

  # This is the entry point for the workflow
  def make_booking options

    puts "Workflow has started\n" unless is_replaying?
    # This array will hold all futures that are created when asynchronous
    # activities are scheduled
    futures = []

    if options[:reserve_car]
      puts "Reserving a car for customer\n" unless is_replaying?
      # The activity client can be used to schedule activities
      # asynchronously by using the send_async method
      futures << client.send_async(:reserve_car, options[:request_id])
    end
    if options[:reserve_air]
      puts "Reserving air ticket\n" unless is_replaying?
      futures << client.send_async(:reserve_air, options[:customer_id])
    end

    puts "Waiting for reservation to complete\n" unless is_replaying?
    # wait_for_all is a flow construct that will wait on the array of
    # futures passed to it
    wait_for_all(futures)

    # After waiting on the reservation activities to complete, the workflow
    # will call the send_confirmation activity.
    client.send_confirmation(options[:customer_id])

    puts "Workflow has completed\n" unless is_replaying?
  end

  # Helper method to check if Flow is replaying the workflow. This is used to
  # avoid duplicate log messages
  def is_replaying?
    decision_context.workflow_clock.replaying
  end
end

This example defines a single decider, make_booking, which declares an activity client using the Workflows#activity_client method to schedule activities with. The activity client takes a set of ActivityOptions that it will use when an activity is scheduled using the client.

Your decider can schedule activities synchronously or asynchronously, can spawn child workflows, and can perform many other functions to allow you to customize how your workflow progresses. While you design your workflow classes and decider methods, keep the following points in mind:

  • Do not use decider methods to perform long-running tasks. The AWS Flow Framework for Ruby replay mechanism will repeat that task multiple times. Even asynchronous workflow methods will typically run more than once. Instead, use activities for long running tasks; the replay mechanism executes activities only once.
  • Your workflow logic must be completely deterministic. Every episode (a single replay of the workflow) must take the same control flow path. For example, the control flow path should not depend on the current time.

Registering Workflows#

Amazon SWF workflows are represented by a workflow type that is registered with Amazon SWF. As with activities, the AWS Flow Framework for Ruby handles workflow registration automatically for your workflow types when necessary.

Workflow types registered by the framework are named using a combination of the workflow class name and decider method name; in the Booking example, the workflow type registered for the make_booking decider method will be BookingWorkflow.make_booking.

You can set default options for workflow types when you declare decider methods in your Workflows-based class:

workflow :make_booking do
  {
    version: "1.0",
    default_execution_start_to_close_timeout: 120
  }
end

The block of options associated with the make_booking declaration are used as default options whenever the workflow is run, unless they are overridden when launching the workflow. As with activities, workflow types are immutable once registered, so if you need to change the default options for a workflow, you will also need to change its name, version or both in order to keep it from interfering with workflows associated with the previously-registered type.

When using the default decider, the AWS Flow Framework for Ruby will register and use its own workflow type, named RubyFlowDefaultWorkflow.start, with a version number of 1.0. You cannot redefine or change the default options associated with this workflow type.

Launching and Running Workflows#

Your decider code won't be run until:

  1. A workflow execution is started.
  2. A workflow worker receives a decision task to start the workflow.

You can start a workflow worker that polls for decision tasks by implementing one yourself or by using the aws-flow-ruby to spawn workers for you. Information about how to use each method is provided in the linked topics.

To start a workflow execution, you can use the start_workflow method, providing it with your registered workflow name, a block of input data for your workflow, and a set of WorkflowOptions used to start the workflow. For example:

require 'aws/decider'

input = {
  request_id: "1234567890",
  customer_id: "1234567890",
  reserve_car: true,
  reserve_air: true
}

opts = {
  domain: "Booking",
  version: "1.0"
}

AWS::Flow::start_workflow("BookingWorkflow.make_booking", input, opts)

For More Information#

  • Specifying Workflow and Activity Options – Provides information about setting workflow options during registration or when launching a workflow.
  • Amazon SWF Timeout Types – Provides information about timeouts for workflows and what they mean in the context of the workflow's life-cycle.
  • Implementing Workflow Patterns – Provides information about how to design your decider code to replicate many common workflow patterns.
  • Setting Task Priority – Provides information about how to set a task priority value to your workflows to affect which decider tasks are delivered to your workers first.
  • aws-flow-ruby – Provides information about how to set up and spawn workers for your workflows and activities with a simple configuration file and the aws-flow-ruby utility.
  • awslabs/aws-flow-ruby-samples – A GitHub repository with examples and recipes that provide code examples of workflow and activity implementations using the AWS Flow Framework for Ruby.