SDK d'exécution durable - AWS Lambda

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

SDK d'exécution durable

Le SDK d'exécution durable est la base de la création de fonctions durables. Il fournit les primitives dont vous avez besoin pour contrôler la progression, gérer les nouvelles tentatives et gérer le flux d'exécution. Le SDK élimine la complexité de la gestion et de la rediffusion des points de contrôle, en vous permettant d'écrire du code séquentiel qui devient automatiquement tolérant aux pannes.

Le SDK est disponible pour JavaScript, TypeScript, et Python. Pour obtenir une documentation complète sur l'API et des exemples, consultez le TypeScript SDKJavaScript/et le SDK Python sur. GitHub

DurableContext

Le SDK fournit à votre fonction un DurableContext objet qui expose toutes les opérations durables. Ce contexte remplace le contexte Lambda standard et fournit des méthodes pour créer des points de contrôle, gérer le flux d'exécution et coordonner avec des systèmes externes.

Pour utiliser le SDK, enveloppez votre gestionnaire Lambda avec le wrapper d'exécution durable :

TypeScript
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js'; export const handler = withDurableExecution( async (event: any, context: DurableContext) => { // Your function receives DurableContext instead of Lambda context // Use context.step(), context.wait(), etc. return result; } );
Python
from aws_durable_execution_sdk_python import durable_execution, DurableContext @durable_execution def handler(event: dict, context: DurableContext): # Your function receives DurableContext # Use context.step(), context.wait(), etc. return result

Le wrapper intercepte l'invocation de votre fonction, charge tout journal de points de contrôle existant et fournit le journal DurableContext qui gère les rediffusions et les points de contrôle.

Ce que fait le SDK

Le SDK assume trois responsabilités essentielles qui permettent une exécution durable :

Gestion des points de contrôle : le SDK crée automatiquement des points de contrôle lorsque votre fonction exécute des opérations durables. Chaque point de contrôle enregistre le type d'opération, les entrées et les résultats. Lorsque votre fonction termine une étape, le SDK maintient le point de contrôle avant de continuer. Cela garantit que votre fonction peut reprendre après toute opération terminée en cas d'interruption.

Coordination des rediffusions : lorsque votre fonction reprend après une pause ou une interruption, le SDK effectue une rediffusion. Il exécute votre code depuis le début mais ignore les opérations terminées, en utilisant les résultats des points de contrôle stockés au lieu de les réexécuter. Le SDK garantit que la rediffusion est déterministe : avec les mêmes entrées et le même journal de points de contrôle, votre fonction produit les mêmes résultats.

Isolation des états : le SDK conserve l'état d'exécution séparément de votre logique métier. Chaque exécution durable possède son propre journal de points de contrôle auquel les autres exécutions ne peuvent pas accéder. Le SDK chiffre les données des points de contrôle au repos et garantit la cohérence de l'état entre les rediffusions.

Comment fonctionne le point de contrôle

Lorsque vous appelez une opération durable, le SDK suit cette séquence :

  1. Vérifier l'existence d'un point de contrôle : le SDK vérifie si cette opération a déjà été effectuée lors d'un appel précédent. S'il existe un point de contrôle, le SDK renvoie le résultat enregistré sans réexécuter l'opération.

  2. Exécuter l'opération : s'il n'existe aucun point de contrôle, le SDK exécute votre code d'opération. Pour les étapes, cela signifie appeler votre fonction. Pour les temps d'attente, cela signifie planifier la reprise.

  3. Créer un point de contrôle : une fois l'opération terminée, le SDK sérialise le résultat et crée un point de contrôle. Le point de contrôle inclut le type d'opération, le nom, les entrées, le résultat et l'horodatage.

  4. Point de contrôle persistant : le SDK appelle l'API du point de contrôle Lambda pour conserver le point de contrôle. Cela garantit la durabilité du point de contrôle avant de poursuivre l'exécution.

  5. Renvoyer le résultat : le SDK renvoie le résultat de l'opération à votre code, qui passe à l'opération suivante.

Cette séquence garantit qu'une fois l'opération terminée, son résultat est stocké en toute sécurité. Si votre fonction est interrompue à tout moment, le SDK peut être rejoué jusqu'au dernier point de contrôle terminé.

Comportement de rediffusion

Lorsque votre fonction reprend après une pause ou une interruption, le SDK effectue une rediffusion :

  1. Charger le journal des points de contrôle : le SDK extrait le journal des points de contrôle pour cette exécution à partir de Lambda.

  2. Exécuter depuis le début : le SDK appelle votre fonction de gestionnaire dès le début, et non depuis l'endroit où elle s'est interrompue.

  3. Ignorer les opérations durables terminées : lorsque votre code appelle des opérations durables, le SDK compare chacune d'entre elles au journal des points de contrôle. Pour les opérations durables terminées, le SDK renvoie le résultat stocké sans exécuter le code d'opération.

    Note

    Si le résultat d'un contexte enfant est supérieur à la taille maximale du point de contrôle (256 Ko), le code du contexte est à nouveau exécuté pendant la rediffusion. Cela vous permet de générer des résultats volumineux à partir des opérations durables exécutées dans le contexte, qui seront consultés dans le journal des points de contrôle. Il est donc impératif de n'exécuter le code déterministe que dans le contexte lui-même. Lorsque vous utilisez des contextes enfants avec des résultats importants, il est recommandé d'effectuer un travail de longue durée ou non déterministe à l'intérieur des étapes et de n'effectuer que des tâches de courte durée qui combinent les résultats dans le contexte lui-même.

  4. Reprise au point d'interruption : lorsque le SDK atteint une opération sans point de contrôle, il s'exécute normalement et crée de nouveaux points de contrôle au fur et à mesure que les opérations durables sont terminées.

Ce mécanisme de rediffusion nécessite que votre code soit déterministe. Avec les mêmes entrées et le même journal de points de contrôle, votre fonction doit effectuer la même séquence d'appels d'opérations durables. Le SDK applique cela en validant que les noms et types d'opérations correspondent au journal des points de contrôle lors de la rediffusion.

Opérations durables disponibles

DurableContextIl fournit des opérations pour différents modèles de coordination. Chaque opération durable crée automatiquement des points de contrôle, garantissant ainsi que votre fonction peut reprendre à tout moment.

Étapes

Exécute la logique métier avec pointage automatique des points de contrôle et réessais. Utilisez des étapes pour les opérations qui appellent des services externes, effectuent des calculs ou exécutent toute logique qui doit être vérifiée. Le SDK crée un point de contrôle avant et après l'étape, stockant le résultat pour le rejouer.

TypeScript
const result = await context.step('process-payment', async () => { return await paymentService.charge(amount); });
Python
result = context.step( lambda _: payment_service.charge(amount), name='process-payment' )

Les étapes prennent en charge les stratégies de nouvelle tentative configurables, la sémantique d'exécution (at-most-once ou at-least-once) et la sérialisation personnalisée.

Éléments d’attente

Suspend l'exécution pendant une durée spécifiée sans consommer de ressources informatiques. Le SDK crée un point de contrôle, met fin à l'invocation de la fonction et planifie la reprise. Lorsque l'attente est terminée, Lambda invoque à nouveau votre fonction et le SDK rejoue jusqu'au point d'attente avant de continuer.

TypeScript
// Wait 1 hour without charges await context.wait({ seconds: 3600 });
Python
# Wait 1 hour without charges context.wait(3600)

Rappels

Les rappels permettent à votre fonction de faire une pause et d'attendre que des systèmes externes fournissent une entrée. Lorsque vous créez un rappel, le SDK génère un identifiant de rappel unique et crée un point de contrôle. Votre fonction est ensuite suspendue (met fin à l'invocation) sans frais de calcul. Les systèmes externes soumettent les résultats de rappel à l'aide du SendDurableExecutionCallbackSuccess ou SendDurableExecutionCallbackFailure Lambda APIs. Lorsqu'un rappel est soumis, Lambda invoque à nouveau votre fonction, le SDK rejoue jusqu'au point de rappel et votre fonction continue avec le résultat du rappel.

Le SDK propose deux méthodes pour utiliser les rappels :

CreateCallback : crée un rappel et renvoie à la fois une promesse et un identifiant de rappel. Vous envoyez l'ID de rappel à un système externe, qui soumet le résultat à l'aide de l'API Lambda.

TypeScript
const [promise, callbackId] = await context.createCallback('approval', { timeout: { hours: 24 } }); await sendApprovalRequest(callbackId, requestData); const approval = await promise;
Python
callback = context.create_callback( name='approval', config=CallbackConfig(timeout_seconds=86400) ) context.step( lambda _: send_approval_request(callback.callback_id), name='send_request' ) approval = callback.result()

waitForCallback: simplifie la gestion des rappels en combinant la création et la soumission des rappels en une seule opération. Le SDK crée le rappel, exécute votre fonction de soumission avec l'ID de rappel et attend le résultat.

TypeScript
const result = await context.waitForCallback( 'external-api', async (callbackId, ctx) => { await submitToExternalAPI(callbackId, requestData); }, { timeout: { minutes: 30 } } );
Python
result = context.wait_for_callback( lambda callback_id: submit_to_external_api(callback_id, request_data), name='external-api', config=WaitForCallbackConfig(timeout_seconds=1800) )

Configurez les délais d'attente pour empêcher les fonctions d'attendre indéfiniment. Si un rappel expire, le SDK renvoie un CallbackError et votre fonction peut gérer le cas de délai d'expiration. Utilisez les délais d'expiration du rythme cardiaque pour les rappels de longue durée afin de détecter les cas où les systèmes externes cessent de répondre.

Utilisez des rappels pour les human-in-the-loop flux de travail, l'intégration de systèmes externes, les réponses Webhook ou tout autre scénario dans lequel l'exécution doit être interrompue pour une entrée externe.

Exécution en parallèle

Exécute plusieurs opérations simultanément avec un contrôle de simultanéité optionnel. Le SDK gère l'exécution parallèle, crée des points de contrôle pour chaque opération et gère les défaillances conformément à votre politique d'achèvement.

TypeScript
const results = await context.parallel([ async (ctx) => ctx.step('task1', async () => processTask1()), async (ctx) => ctx.step('task2', async () => processTask2()), async (ctx) => ctx.step('task3', async () => processTask3()) ]);
Python
results = context.parallel( lambda ctx: ctx.step(lambda _: process_task1(), name='task1'), lambda ctx: ctx.step(lambda _: process_task2(), name='task2'), lambda ctx: ctx.step(lambda _: process_task3(), name='task3') )

parallelÀ utiliser pour exécuter simultanément des opérations indépendantes.

Map

Exécutez simultanément une opération sur chaque élément d'un tableau avec un contrôle de simultanéité optionnel. Le SDK gère les exécutions simultanées, crée des points de contrôle pour chaque opération et gère les défaillances conformément à votre politique d'achèvement.

TypeScript
const results = await context.map(itemArray, async (ctx, item, index) => ctx.step('task', async () => processItem(item, index)) );
Python
results = context.map( item_array, lambda ctx, item, index: ctx.step( lambda _: process_item(item, index), name='task' ) )

mapÀ utiliser pour traiter des tableaux avec contrôle de simultanéité.

Contextes relatifs aux enfants

Crée un contexte d'exécution isolé pour les opérations de regroupement. Les contextes enfants possèdent leur propre journal des points de contrôle et peuvent contenir plusieurs étapes, temps d'attente et autres opérations. Le SDK traite l'ensemble du contexte de l'enfant comme une unité unique pour les nouvelles tentatives et la restauration.

Utilisez des contextes enfants pour organiser des flux de travail complexes, implémenter des sous-flux de travail ou isoler les opérations qui doivent être réessayées ensemble.

TypeScript
const result = await context.runInChildContext( 'batch-processing', async (childCtx) => { return await processBatch(childCtx, items); } );
Python
result = context.run_in_child_context( lambda child_ctx: process_batch(child_ctx, items), name='batch-processing' )

Le mécanisme de rediffusion exige que les opérations durables soient effectuées dans un ordre déterministe. En utilisant plusieurs contextes enfants, plusieurs flux de travail peuvent être exécutés simultanément, et le déterminisme s'applique séparément dans chaque contexte. Cela vous permet de créer des fonctions hautes performances qui utilisent efficacement plusieurs cœurs de processeur.

Par exemple, imaginons que nous démarrions deux contextes enfants, A et B. Lors de l'invocation initiale, les étapes des contextes étaient exécutées dans cet ordre, les étapes « A » s'exécutant simultanément avec les étapes « B » : A1, B1, B2, A2, A3. Lors de la rediffusion, le chronométrage est beaucoup plus rapide car les résultats sont extraits du journal des points de contrôle et les étapes sont parcourues dans un ordre différent : B1, A1, A2, B2, A3. Comme les étapes « A » ont été rencontrées dans le bon ordre (A1, A2, A3) et que les étapes « B » ont été rencontrées dans le bon ordre (B1, B2), le besoin de déterminisme a été correctement satisfait.

Attentes conditionnelles

Recherche une condition avec pointage automatique entre les tentatives. Le SDK exécute votre fonction de vérification, crée un point de contrôle avec le résultat, attend selon votre stratégie et répète jusqu'à ce que la condition soit remplie.

TypeScript
const result = await context.waitForCondition( async (state, ctx) => { const status = await checkJobStatus(state.jobId); return { ...state, status }; }, { initialState: { jobId: 'job-123', status: 'pending' }, waitStrategy: (state) => state.status === 'completed' ? { shouldContinue: false } : { shouldContinue: true, delay: { seconds: 30 } } } );
Python
result = context.wait_for_condition( lambda state, ctx: check_job_status(state['jobId']), config=WaitForConditionConfig( initial_state={'jobId': 'job-123', 'status': 'pending'}, wait_strategy=lambda state, attempt: {'should_continue': False} if state['status'] == 'completed' else {'should_continue': True, 'delay': 30} ) )

waitForConditionÀ utiliser pour interroger des systèmes externes, attendre que les ressources soient prêtes ou implémenter une nouvelle tentative avec interruption.

Invocation de fonctions

Invoque une autre fonction Lambda et attend son résultat. Le SDK crée un point de contrôle, invoque la fonction cible et reprend votre fonction une fois l'invocation terminée. Cela permet la composition des fonctions et la décomposition du flux de travail.

TypeScript
const result = await context.invoke( 'invoke-processor', 'arn:aws:lambda:us-east-1:123456789012:function:processor', { data: inputData } );
Python
result = context.invoke( 'arn:aws:lambda:us-east-1:123456789012:function:processor', {'data': input_data}, name='invoke-processor' )

Comment mesure-t-on la durabilité des opérations

Chaque opération durable que vous effectuez DurableContext crée des points de contrôle pour suivre la progression de l'exécution et stocker les données d'état. Ces opérations sont facturées en fonction de leur utilisation, et les points de contrôle peuvent contenir des données qui contribuent à vos coûts d'écriture et de conservation des données. Les données stockées incluent les données relatives aux événements d'invocation, les charges utiles renvoyées par les étapes et les données transmises lors de l'exécution des rappels. Comprendre comment la durabilité des opérations est mesurée vous permet d'estimer les coûts d'exécution et d'optimiser vos flux de travail. Pour plus de détails sur la tarification, consultez la page de tarification de Lambda.

La taille de la charge utile fait référence à la taille des données sérialisées qu'une opération durable persiste. Les données sont mesurées en octets et leur taille peut varier en fonction du sérialiseur utilisé par l'opération. La charge utile d'une opération peut être le résultat lui-même en cas d'achèvement réussi, ou l'objet d'erreur sérialisé en cas d'échec de l'opération.

Opérations de base

Les opérations de base sont les éléments fondamentaux des fonctions durables :

Opération Chronométrage des points de contrôle Nombre d'opérations Les données ont persisté
Exécution Démarré(e) 1 Taille de la charge utile d'entrée
Exécution Terminé (Succeeded/Failed/Stopped) 0 Taille de la charge utile de sortie
Step (Étape) Retry/Succeeded/Failed 10 + Aucune nouvelle tentative Taille de la charge utile renvoyée à chaque tentative
Attente Démarré(e) 1 N/A
WaitForCondition Chaque tentative de sondage + 1 x N sondages Taille de la charge utile renvoyée à chaque tentative de sondage
Nouvelle tentative au niveau de l'invocation Démarré(e) 1 Charge utile pour l'objet d'erreur

Opérations de rappel

Les opérations de rappel permettent à votre fonction de faire une pause et d'attendre que des systèmes externes fournissent une entrée. Ces opérations créent des points de contrôle lorsque le rappel est créé et lorsqu'il est terminé :

Opération Chronométrage des points de contrôle Nombre d'opérations Les données ont persisté
CreateCallback Démarré(e) 1 N/A
Exécution du rappel via un appel d'API Terminé 0 Charge utile de rappel
WaitForCallback Démarré(e) 3 + N tentatives (contexte + rappel + étape) Charges utiles renvoyées par les tentatives d'étape de l'expéditeur, plus deux copies de la charge utile de rappel

Opérations composées

Les opérations composées combinent plusieurs opérations durables pour gérer des modèles de coordination complexes tels que l'exécution parallèle, le traitement de tableaux et les contextes imbriqués :

Opération Chronométrage des points de contrôle Nombre d'opérations Les données ont persisté
Parallèle Démarré(e) 1 + N branches (1 contexte parent + N contextes enfants) Jusqu'à deux copies de la taille de charge utile renvoyée par chaque branche, plus les statuts de chaque branche
Map Démarré(e) 1 + N branches (1 contexte parent + N contextes enfants) Jusqu'à deux copies de la taille de charge utile renvoyée par itération, plus les statuts de chaque itération
Des aides à la promesse Terminé 1 Taille de la charge utile renvoyée par la promesse
RunInChildContext Succès/Echec 1 Taille de la charge utile renvoyée par le contexte enfant

Pour les contextes, tels que ceux issus runInChildContext ou utilisés en interne par des opérations composées, les résultats inférieurs à 256 Ko sont directement contrôlés. Les résultats les plus importants ne sont pas stockés ; ils sont reconstruits lors de la rediffusion en retraitant les opérations du contexte.