web-dev-qa-db-fra.com

AWS Step Function - Attendre qu'un événement se produise

J'ai un cas d'utilisation où j'ai une fonction AWS Step qui est déclenchée lorsqu'un fichier est chargé sur S3. À partir de là, la première étape exécute une analyse ffprobe pour obtenir la durée du fichier auprès d'un service externe tel que transloadit où la sortie est écrite. Retour à S3.

Je peux créer une nouvelle fonction d'étape à partir de cet événement, mais je me demandais s'il était possible d'insérer une promesse Await dans la fonction d'étape d'origine, puis de passer à la suivante, en tenant compte du fait que le retour de la sonde pouvait prendre plus de temps.

Tout conseil est très apprécié sur la façon de s'y attaquer.

9
khinester

Impossible de proposer une solution simple, seulement quelques directions à explorer.

Tout d’abord, les fonctions d’étape disposent d’un moyen spécifique pour gérer le travail en arrière-plan de longue durée: les activités. https://docs.aws.Amazon.com/step-functions/latest/dg/concepts-activities.html il s’agit d’une file d’attente.

Si vous voulez 100% sans serveur, cela va être compliqué ou moche.

  • soit, comme vous l'avez dit, créer une nouvelle fonction d'étape pour chaque fichier
  • ou, boucle d'interrogation S3 dans la machine à états à l'aide du code d'erreur personnalisé et de la clause Retry

Si vous pouvez allouer une instance "1/8 micro" à un employé d'arrière-plan, ce n'est pas élégant, mais facile et peut être mis en œuvre avec une réaction instantanée. La configuration matérielle requise laisse à penser que nous n'utiliserons la machine que pour la synchronisation.

Définissez l'activité StepFunction, nommée par exemple video-duration. Définissez la file d'attente SQS pour une réaction instantanée ou interrogez S3 pour obtenir des résultats de durée.

Pseudocode de fonction d'état:

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

Pseudocode de l’agent d’arrière-plan:

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)

Pseudocode déclencheur S3:

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

Lorsque vous envoyez la demande à transloadit, enregistrez le taskToken pour l'étape dans s3 sous une clé prévisible en fonction de la clé du fichier téléchargé. Par exemple, si le fichier multimédia se trouve dans 's3: //my-media-bucket/foobar/media-001.mp3', vous pouvez créer un fichier JSON contenant le jeton de tâche de l'étape en cours et le stocker avec la même clé. dans un compartiment différent, par exemple 's3: //ffprobe-tasks/foobar/media-001.mp3.json'. À la fin de votre étape, le média est envoyé à transloadit ne pas appelez le succès ou l'échec de l'étape - laissez-le en marche.

Ensuite, lorsque vous recevez une notification s3 indiquant que le résultat transloadit est prêt, vous pouvez déterminer la clé s3 pour obtenir le jeton de tâche ('s3: //ffprobe-tasks/foobar/media-001.mp3'), charger le de s3) et envoyer le succès pour cette tâche. La fonction step continuera à l'état suivant de l'exécution.

1
ivo

je vais aussi à ce problème, lorsque j'ai essayé de combiner SFN pour orchestrer des travaux AWS Batch. Les pratiques suggérées ci-dessus sont problématiques, car vous devez réussir la tâche Taskoken, donc vous avez besoin d'un lambda inside la machine à états, pour extraire TaskToken de la file d'attente et la transmettre à S3 ou ailleurs, qu'un autre lambda soumettra le statut d'activité.

le problème est le suivant: lorsque vous interrogez le taskToken, vous ne pouvez pas savoir s'il appartient à votre instance d'état-machine. vous pouvez obtenir un jeton sur une autre instance de la même machine-état. personnellement Je pense que ce serait formidable si AWS prend en charge cette fonctionnalité, ce qu’ils peuvent facilement faire ...

0
RELW

Eh bien, je m'inspirerais de https://aws.Amazon.com/blogs/compute/implementing-serverless-manual-approval-steps-in-aws-functions-and-Amazon-api-gateway/

Vous pouvez remplacer la passerelle API par une fonction AWS Lambda, déclenchée par exemple par un événement S3 (Documentation: http://docs.aws.Amazon.com/lambda/latest/dg/with-s3.html ). Assurez-vous simplement que votre tâche a un délai d'attente approprié.

0
ElFitz

Vous souhaitez généralement lancer la tâche asynchrone en tant qu'activité de fonction étape. Le mot clé ici est initier - en d'autres termes, une fois que votre activité a une action en attente, vous déclenchez votre action asynchrone. La raison en est que vous avez besoin du jeton de tâche associé à l'activité en attente - aussi longtemps que votre "futur" peut inclure ce jeton d'une manière ou d'une autre (par exemple, vous pouvez le définir comme identifiant de référence ou de demande), vous pouvez alors "terminer" l'activité ayant un succès ou un échec à l'aide de l'appel SendTaskSuccess ou SendTaskFailure .

Il existe deux approches pour lancer la tâche:

  1. Sondage pour une nouvelle activité. Vous devez configurer un événement planifié CloudWatch pour émettre le GetActivityTask call toutes les n minutes.

  2. Lancez une nouvelle tâche "initiateur" en parallèle de votre activité dans la fonction d'étape. Cet initiateur exécute la même chose que # 1 et effectue l'appel GetActivityTask. La seule différence est qu'il est déclenché immédiatement et ne nécessite pas de mécanisme de scrutation. L'appel GetActivityTask bloque jusqu'à ce qu'une nouvelle tâche d'activité devienne disponible. Il n'y a donc pas de problème de conditions de concurrence. Notez qu'il est possible que vous choisissiez une activité d'une autre exécution. Cet initiateur doit donc uniquement prendre en compte l'entrée de l'activité et non l'entrée que l'initiateur reçoit lui-même.

Voici ce que # 2 ressemble à une fonction Step:

 Initiating an activity

Et exemple de code de base associé à la tâche 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