Search code examples
amazon-swf

Amazon SWF queries


Over the last couple of years, I have done a fair amount of work on Amazon SWF, but the following points are still unclear to me and I am not able to find any straight forward answers on any forums yet.

These are pretty basic requirements I suppose, sure others might have come across too. Would be great if someone can clarify these.

  1. Is there a simple way to return a workflow execution result (maybe just something as simple as boolean) back to workflow starter?
  2. Is there a way to catch Activity timeout exception, so that we can do run customised actions in such scenarios?
  3. Why doesn't WorkflowExecutionHistory contains Activities, why just Events?
  4. Why there is no simple way of restarting a workflow from the point it failed?

I am considering to use SWF for more business processes at my workplace, but these limitations/doubts are holding me back!

FINAL WORKING SOLUTION

public class ReturnResultActivityImpl implements ReturnResultActivity {

    SettableFuture future;

    public ReturnResultActivityImpl() {
    }

    public ReturnResultActivityImpl(SettableFuture future) {

        this.future = future;
    }

    public void returnResult(WorkflowResult workflowResult) {

        System.out.print("Marking future as Completed");
        future.set(workflowResult);
    }

}

public class WorkflowResult {

    public WorkflowResult(boolean s, String n) {
        this.success = s;
        this.note = n;
    }

    private boolean success;
    private String note;
}

public class WorkflowStarter {

    @Autowired
    ReturnResultActivityClient returnResultActivityClient;

    @Autowired
    DummyWorkflowClientExternalFactory dummyWorkflowClientExternalFactory;

    @Autowired
    AmazonSimpleWorkflowClient swfClient;

    String domain = "test-domain;
    boolean isRegister = true;
    int days = 7;
    int terminationTimeoutSeconds = 5000;
    int threadPollCount = 2;
    int taskExecutorThreadCount = 4;

    public String testWorkflow() throws Exception {

        SettableFuture<WorkflowResult> workflowResultFuture = SettableFuture.create();
        String taskListName = "testTaskList-" + RandomStringUtils.randomAlphabetic(8);

        ReturnResultActivity activity = new ReturnResultActivityImpl(workflowResultFuture);
        SpringActivityWorker activityWorker = buildReturnResultActivityWorker(taskListName, Arrays.asList(activity));

        DummyWorkflowClientExternalFactory factory = new DummyWorkflowClientExternalFactoryImpl(swfClient, domain);
        factory.getClient().doSomething(taskListName)

        WorkflowResult result = workflowResultSettableFuture.get(20, TimeUnit.SECONDS);
        return "Call result note - " + result.getNote();
    }

    public SpringActivityWorker buildReturnResultActivityWorker(String taskListName, List activityImplementations)

            throws Exception {

        return setupActivityWorker(swfClient, domain, taskListName, isRegister, days, activityImplementations,
                terminationTimeoutSeconds, threadPollCount, taskExecutorThreadCount);
    }
}

public class Workflow {

    @Autowired
    private DummyActivityClient dummyActivityClient;

    @Autowired
    private ReturnResultActivityClient returnResultActivityClient;

    @Override
    public void doSomething(final String resultActivityTaskListName) {

        Promise<Void> activityPromise = dummyActivityClient.dummyActivity();
        returnResult(resultActivityTaskListName, activityPromise);
    }

    @Asynchronous
    private void returnResult(final String taskListname, Promise waitFor) {

        ActivitySchedulingOptions schedulingOptions = new ActivitySchedulingOptions();

        schedulingOptions.setTaskList(taskListname);
        WorkflowResult result = new WorkflowResult(true,"All successful");

        returnResultActivityClient.returnResult(result, schedulingOptions);
    }
}

Solution

    1. The standard pattern is to host a special activity in the workflow starter process that is used to deliver the result. Use a process specific task list to make sure that it is routed to a correct instance of the starter. Here are the steps to implement it:

      • Define an activity to receive the result. For example "returnResultActivity". Make this activity implementation to complete the Future passed to its constructor upon execution.
      • When the workflow is started it receives "resultActivityTaskList" as an input argument. At the end the workflow calls this activity with a workflow result. The activity is scheduled on the passed task list.
      • The workflow starter creates an ActivityWorker and an instance of a Future. Then it creates an instance of "returnResultActivity" with that future as a constructor parameter.
      • Then it registers the activity instance with the activity worker and configures it to poll on a randomly generated task list name. Then it calls "start workflow execution" passing the generated task list name as an input argument.
      • Then it wait on the Future to complete. The future.get() is going to return the workflow result.
    2. Yes, if you are using the AWS Flow Framework a timeout exception is thrown when activity is timed out. If you are not using the Flow framework than you are making your life 100 times harder. BTW the workflow timeout is thrown into a parent workflow as a timeout exception as well. It is not possible to catch a workflow timeout exception from within the timing out instance itself. In this case it is recommended to not rely on workflow timeout, but just create a timer that would fire and notify workflow logic that some business event has timed out.

    3. Because a single activity execution has multiple events associated to it. It should be pretty easy to write code that converts history to whatever representation of activities you like. Such code would just match the events that relate to each activities. Each event always has a reference to the related events, so it is easy to roll them up into higher level representation.

    4. Unfortunately there is no easy answer to this one. Ideally SWF would support restarting workflow by copying its history up to the failure point. But it is not supported. I personally believe that workflow should be written in a way that it never fails but always deals with failures without failing. Obviously it doesn't work in case of failures due to unexpected conditions. In this case writing workflow in a way that it can be restarted from the beginning is the simplest approach.