Amazon Simple Workflow ServicesをAWSCLIで使ってみて理解する

ちょうどAWS上でワークフローが必要になるような業務を実装することになったので、アーキテクチャを考えてみました。
もちろん、それらのワークフローの実施が冗長化されており、同時にスケールすることも前提で。

…と、意気込んで以下のコンセプトを元にしたアーキテクチャ図を考えていました。

  • 1つの処理の流れの状態を常に持っている (どこまで進んだか、それぞれ中間でどのような結果となったか、など)
  • それぞれの処理はLambdaで実装し、上記で示した状態を受け取る
  • Lambdaも処理全体をコントロールするControllerと、具体的なサブタスクを実行するSubfunctionsに分ける
  • 各Lambda Function自体は入力のみに依存した処理を実行できるのでステートレスを保てる
  • Lambda Functionの終了時に後続作業がある場合は、入力を変更したメッセージを用意したSQSに投げる。 この時、必要であればDelayオプションを使って実行を遅延させる
  • 処理途中のSQSのメッセージキューはポーリングされており、適切なタイミングで拾われ、再度Lambdaを実行させる (Lambda側では状態に基づいた処理を行い、何も無くなったら処理を終わらせる)

理論的には実装できなくもないですが、問題も数多く有ります。
例えば、次のようなものです。

  • SQSのメッセージは2度以上取得される可能性があるため、実行を1度のみに保証する場合の作りこみが大変
  • SQSの順序保証が無いため、実行順序が重要な場合は使いづらい
  • 1つの処理の流れが何処まで進んだかの可視化が難しい

どうしたものか…と悩んでいたのですが、前々からやけに概念を理解するのが難しそうで避けていたAmazon Simple Workflow Services (SWF) とほとんどそっくりな考え方であったのと、SQSの性質に基づく問題が解決できるということもあったので、本腰を入れてSWFを調べてみることにしました。

日本語の資料が少ないのもあれですが、SWFを紹介している資料は多くの場合はAWS Flow FrameworkというSWFを実装するためのライブラリを利用してSWFを紹介しているものが多かったです。
ただ、これだと個人的には非常に理解しづらかったので、AWSCLIから(ほぼ)APIレベルで機能を使ってみつつ、SWFの仕組みの理解をしようと試みました。
結果としては、APIだけを使ってみると非常にシンプルなできになっている上に、非常に汎用的で使い道も数多くありそう、という結論が得られました。

Amazon Simple Workflow Services (SWF) とは

概念を理解するのであれば、以下の記事が一番分かりやすかったです。

http://dev.classmethod.jp/cloud/aws/introduction-to-amazon-simple-workflow-service/

上記ページの登場人物として、starter, decider, workerなどが出てきますが、
今回はこれら全てを1つのクライアントのAWSCLIを用いて実行し、どのような挙動になっているのかを確認してみることが目的です。

SWFサービスの構成要素

SWFサービスの中にはDomain, Workflow, Activityという概念があります。
が、自分はこの理解に非常に時間がかかりました。
なぜかというと、SWFの上でWorkflow/Activityを作った時には、decider/workerが全く出てこなかったためです。
実際にAWS Management ConsoleからSWFのサービスを開いてみると、Domain, Workflow, Activityを作ることはできますが、これらとdecider/workerを結びつける設定は全くありません。
AWS Flow Frameworkではこれらの概念を紐付けてプログラミングできるようにしてくれるため強く紐付いているのですが、生でAPIを叩く場合はこれらをdecider/workerのプログラムの中で紐付けてやる必要があります。

実際に実行されたWorkflowはDecision TaskListと呼ばれるキューの中に格納されます。 deciderはこのキューをポーリングし、内容を見て次の作業を決定します。
また、deciderは実際に実行する処理を決定してSWFに通知します。 この処理はActivity TaskListと呼ばれるキューの中に格納され、workerはこのキューをポーリングして具体的な作業を実行します。

この流れを見たとき、私は最初 "deciderはActivity TaskListに対して次のコマンドを登録する" ものだと思っていました。
しかし、これは誤りであり、"deciderはDecision TaskListに対して、Activity Taskを生成するためのスケジュールを登録する" のが正しい動きになります。
これは、SWFサービスの中にAPIコールを受けてキューへ処理を振り分けるためのサービスが存在していることを示しています (このサービスのことをこの記事ではスケジューラと呼ぶことにします)。

AWSCLIを使ってサービスの流れを確認する

以上の前置きを置いた上で、実際にワークフローの流れを確認してみます。

前提条件

以下の内容のものをAWS Management Consoleから作成しました。

  • Domain: sample
  • Workflow: HelloWorld (version=1.0, TaskList=default)
  • Activity: Echo (version=1.0, TaskList=default)
# Workflowの開始 (starter)
aws swf start-workflow-execution --domain sample --workflow-id test --workflow-type name=HelloWorld,version=1.0

# Decision TaskのPollingをしてタスクを取得する (decider)
aws swf poll-for-decision-task --domain sample --task-list name=default
# Activity Taskをスケジュールする (decider)
aws swf respond-decision-task-completed --task-token ${POLLING_RESULTS_INCLUDES_TOKEN} --decisions "decisionType=ScheduleActivityTask,scheduleActivityTaskDecisionAttributes={activityType={name=Echo,version=1.0},activityId=test2,input=${TASK_INPUT},scheduleToCloseTimeout=300,heartbeatTimeout=300,taskList={name=default}}"

# Activity TaskのPollingをしてタスクを取得する (worker)
aws swf poll-for-activity-task --domain sample --task-list default
# Activity Taskを処理して、resultを返す (worker)
aws swf respond-activity-task-completed --task-token ${POLLING_RESULTS_INCLUDES_TOKEN} --result "Hello, World"

# Activity Taskが終了すると、Decision TaskがPollingできるようになる (スケジューラがそうしてくれる) (decider)
aws swf poll-for-decision-task --domain sample --task-list name=default
# Workflowの終了をスケジュールする (decider)
aws swf respond-decision-task-completed --task-token ${POLLING_RESULTS_INCLUDES_TOKEN} --decisions decisionType=CompleteWorkflowExecution

# Workflow処理がCompletedになって正常終了する

実際に流れを図示してみると以下の通りです。

ここではタスクがキューに積まれ次第ポーリングしているので、結果をすぐに得ることができますが、キューにない場合はLong Pollingとなってデータを取得するまで待機状態になります。 なお、一度TaskListからデータを取得した場合、一貫性を保持しているため再度その値を取得することはできません。また、deciderからworkerに対して入力(input)を与えることができたり、workerの処理結果を出力(output)ができることが上記の例から分かります。

deciderがポーリングした結果にはこれまでのワークフローの全ての履歴や、Activityが返した結果が格納されています。
これらの履歴を元に、deciderは次の処理を決めていくことになります。

SWFの内部にスケジューラというサービスがAPIを受け取り、TaskListの状態を良しなにしてくれることさえ理解できれば、上記の動きは理解できると思います。同時に、SWF APIを利用できるのであれば、これらのstarter/decider/workerを自由な方法で実装できることも分かります。

まとめ

AWS Flow Frameworkを使わずに直接Amazon SWFを利用することで、その大まかな仕組みを理解することができました。
改めてみてみると、かなり汎用的で使いやすい開発サービスになっているように思います。

また、ActivityTaskにLambdaを利用できるので、実際に作ってみてからその使い方を書いてみる予定です。