web-dev-qa-db-ja.com

AWS Step Function-イベントまで待つ

ファイルがS3にアップロードされたときにトリガーされるAWS Step関数があるユースケースがあり、そこから最初のステップはffprobeを実行して、出力が書き込まれるtransloaditなどの外部サービスからファイルの期間を取得しますS3に戻ります。

そのイベントから新しいステップ関数を作成できますが、元のステップ関数内にAwaitプロミスを設定し、次のステップに進むことが可能かどうか迷っていました-ffprobeが復帰するのに時間がかかる可能性があることを考慮して.

これに取り組む方法についてのアドバイスは大歓迎です。

17
khinester

AWS Step Functionsは、ファーストクラスとして長時間実行されるステップの非同期コールバックをサポートするようになりました。

これは、上記の@mixjaの回答に似ていますが、単純化されています。ワークフロー内の単一の状態は、Lambda、SNS、SQS、またはECSを直接呼び出し、SendTaskSuccessへの呼び出しを待機できます。

良い例 SQSについて文書化されており、ステップ関数がメッセージを送信し、何かがコールバックを提供するまでワークフローの実行を一時停止します。 Lambdaは同等です(transloaditなどのメイン処理がLambda自体の外部で行われると仮定)

ステップ関数の定義は次のようになります

"Invoke transloadit": {
  "Type": "Task",
  "Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken",
  "Parameters": {
    "FunctionName": "InvokeTransloadit",
    "Payload": {
        "some_other_param": "...",
        "token.$": "$$.Task.Token"
     }
  },
  "Next": "NEXT_STATE"
}

次に、ラムダで次のようなことをします

def lambda_handler(event, context):
    token = event['token']

    # invoke transloadit via SSM, ECS, passing token along

次に、メインの長時間実行プロセスで、シェルスクリプト/ CLIからのaws stepfunctions send-task-success --task-token $tokenなどのトークン、またはAPI呼び出しと同様のトークンを使用してコールバックを発行します。

6
wrschneider

Transloaditにリクエストを送信するとき、アップロードされたファイルキーに基づいて予測可能なキーでs3のステップのtaskTokenを保存します。たとえば、メディアファイルが 's3://my-media-bucket/foobar/media-001.mp3'にある場合、現在のステップのタスクトークンを含むJSONファイルを作成し、同じキーで保存できます。たとえば、「s3://ffprobe-tasks/foobar/media-001.mp3.json」などの別のバケット内。メディアをtransloaditに送信するステップの最後にdo notステップの呼び出しの成功または失敗-実行したままにします。

次に、transloaditの結果の準備ができたというs3通知を受け取ったら、s3キーを決定してタスクトークン( 's3://ffprobe-tasks/foobar/media-001.mp3')を取得し、JSONをロードします(そして削除します) s3)から取得し、そのタスクの成功を送信します。ステップ関数は、実行の次の状態に進みます。

2
ivo

簡単な解決策を提案することはできません。探索する方向はわずかです。

まず、ステップ関数には、長時間実行されるバックグラウンド作業を処理する特定の方法があります:アクティビティ。 https://docs.aws.Amazon.com/step-functions/latest/dg/concepts-activities.html これは基本的にキューです。

100%サーバーレスにしたい場合、これは複雑またはugいものになります。

  • あなたが言ったように、各ファイルに新しいステップ関数を作成するか、
  • または、カスタムエラーコードとRetry句を使用したステートマシンでのS3ポーリングループ

バックグラウンドワーカーに「1/8 micro」インスタンスを割り当てることができれば、エレガントではありませんが簡単で、即座に対応できます。ハードウェア要件が低いということは、同期のためだけにマシンを使用することを示唆しています。

たとえばvideo-durationという名前のStepFunctionアクティビティを定義します。インスタントリアクションのSQSキューを定義するか、継続結果のS3をポーリングします。

状態関数の擬似コード:

{
  StartAt: ffprobe
  ffprobe: {
    Type: Task
    Resource: arn:...lambda:launch-ffprobe
    Next: wait-duration
  }
  wait-duration: {
    Type: Task
    Resource: arn...activity:video-duration
    End: true
  }
}

バックグラウンドワーカーの擬似コード:

statemap = dict/map filename to result

thread1:
  loop:
    taskToken, input = SF.GetActivityTask('video-duration')  # long poll
    sync(key=input.filename, waiter=taskToken)
thread2:
  loop:
    msg = SQS.ReceiveMessage(...)  # or poll S3
    sync(key=msg.filename, duration=msg.result)

function sync(key, waiter, duration):
  state = statemap[key]
  if waiter:
    state.waiter = waiter
  if duration:
    state.duration = duration
  if state.waiter and state.duration:
    SF.SendTaskSuccess(state.waiter, state.duration)

S3トリガー擬似コード:

if filename is video:
  SF.StartExecution(...)
else if filename is duration:
  content = S3.GetObject(filename)
  SQS.SendMessage(queue, content)
1
temoto

また、SFNを組み合わせてAWS Batchジョブをオーケストレーションしようとしたときに、この問題に進みます。 taskTokenを渡す必要があるため、上記のプラクティスには問題があります。したがって、ラムダinsideステートマシンから、QueueからTaskTokenをポーリングし、それをS3またはどこかに渡す必要があります。別のラムダがアクティビティステータスを送信します。

問題は、taskTokenをポーリングするときに、それが自分のステートマシンインスタンスに属しているかどうかを知ることができないことです。代わりに同じsate-machineの別のインスタンスでトークンを取得できます。個人的には、AWSがこの機能をサポートしてくれるといいと思います。

0
RELW

まあ、私は https://aws.Amazon.com/blogs/compute/implementing-serverless-manual-approval-steps-in-aws-step-functions-and-Amazon-api-gatewayからインスピレーションを得ます/

これでAPI Gatewayを、たとえばS3イベントによってトリガーされるAWS Lambda関数で置き換えることができます(ドキュメント: http://docs.aws.Amazon.com/lambda/latest/dg/with-s3。 html )。タスクに適切なタイムアウトがあることを確認してください。

0
ElFitz

通常、非同期タスクをステップ関数アクティビティとして開始します。ここでのキーワードはinitiateです。つまり、アクティビティに保留中のアクションがあると、非同期アクションをトリガーします。その理由は、保留中のアクティビティに関連付けられたタスクトークンが必要なためです。「将来」に何らかの方法でこのトークンを含めることができる限り(たとえば、参照またはリクエストIDとして設定できる場合)、「完了」することができます SendTaskSuccess または SendTaskFailure 呼び出しを使用した成功または失敗のアクティビティ。

タスクを開始するには、2つのアプローチがあります。

  1. 新しいアクティビティをポーリングします。 CloudWatchのスケジュールされたイベントをセットアップして、n分ごとに GetActivityTask を呼び出します。

  2. Step関数内のアクティビティと並行して、新しい「イニシエーター」タスクを起動します。このイニシエーターは#1と同じように実行され、GetActivityTask呼び出しを行います。唯一の違いは、すぐにトリガーされ、ポーリングメカニズムが不要であることです。 GetActivityTask呼び出しは、新しいアクティビティタスクが利用可能になるまでブロックするため、競合状態に問題はありません。別の実行からアクティビティを取得する可能性があるため、このイニシエーターはアクティビティの入力のみを考慮すればよく、イニシエーター自体が受け取る入力は考慮しないでください。

ステップ関数では、#2は次のようになります。

Initiating an activity

InitiateManualApprovalActivityタスクに関連付けられた基本的なコード例:

import boto3
import time

client = boto3.client('stepfunctions')
activity = "arn:aws:states:us-east-1:123456789012:activity:ManualStep"

def lambda_handler(event, context):
    print(event)
    # This will block until an activity task becomes available
    task = client.get_activity_task(activityArn=activity, workerName="test")
    print(task)
    # Perform your task here
    # In this example we continue on in the same function,
    # but the continuation could be a separate event, 
    # just as long as you can retrieve the task token
    time.sleep(60)
    response = client.send_task_success(taskToken=task['taskToken'], output=task['input'])
    print(response)
    return "done"
0
mixja