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.
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.
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)
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.
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 ...
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é.
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:
Sondage pour une nouvelle activité. Vous devez configurer un événement planifié CloudWatch pour émettre le GetActivityTask call toutes les n minutes.
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:
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"