

# Invocación de funciones duraderas de Lambda
<a name="durable-invoking"></a>

Las funciones duraderas de Lambda admiten los mismos métodos de invocación que las funciones estándar de Lambda. Puede invocar funciones duraderas de forma sincrónica, asíncrona o mediante asignaciones de orígenes de eventos. El proceso de invocación es idéntico al de las funciones estándar, pero las funciones duraderas proporcionan capacidades adicionales para las ejecuciones de larga duración y la administración automática del estado.

## Métodos de invocación
<a name="durable-invoking-methods"></a>

**Invocación sincrónica:** invoca una función duradera y espera la respuesta. Lambda limita las invocaciones sincrónicas a 15 minutos (o menos, según la función configurada y el tiempo de espera de ejecución). Utilice la invocación sincrónica cuando necesite resultados inmediatos o cuando se integre con API y servicios que esperan una respuesta. Puede utilizar las operaciones de espera para un cómputo eficiente sin interrumpir a la persona que llama; la invocación espera a que se complete toda la ejecución duradera. Para que se inicie una ejecución idempotente, utilice el parámetro del nombre de la ejecución, como se describe en [Idempotencia](durable-execution-idempotency.md).

```
aws lambda invoke \
  --function-name my-durable-function:1 \
  --cli-binary-format raw-in-base64-out \
  --payload '{"orderId": "12345"}' \
  response.json
```

**Invocación asíncrona:** coloca un evento en cola para su procesamiento sin esperar una respuesta. Lambda coloca el evento en una cola y lo devuelve de inmediato. Las invocaciones asíncronas admiten duraciones de ejecución de hasta 1 año como máximo. Utilice la invocación asíncrona en situaciones de tipo «disparar y olvidar», o cuando el procesamiento pueda realizarse en segundo plano. Para que se inicie una ejecución idempotente, utilice el parámetro del nombre de la ejecución, como se describe en [Idempotencia](durable-execution-idempotency.md).

```
aws lambda invoke \
  --function-name my-durable-function:1 \
  --invocation-type Event \
  --cli-binary-format raw-in-base64-out \
  --payload '{"orderId": "12345"}' \
  response.json
```

**Asignaciones de orígenes de eventos:** configure Lambda para que invoque automáticamente la función duradera cuando haya registros disponibles en servicios basados en colas o flujos, como Amazon SQS, Kinesis o DynamoDB. Las asignaciones de orígenes de eventos sondean el origen de eventos e invocan su función con lotes de registros. Para obtener más información sobre el uso de las asignaciones de orígenes de eventos con funciones duraderas, incluidos los límites de duración de la ejecución, consulte [Asignaciones de orígenes de eventos con funciones duraderas](durable-invoking-esm.md).

Para obtener información completa sobre cada método de invocación, consulte [invocación sincrónica](invocation-sync.md) e [invocación asíncrona](invocation-async.md).

**nota**  
Las funciones duraderas admiten colas de mensajes fallidos (DLQ) para la gestión de errores, pero no admiten destinos de Lambda. Configure una DLQ para capturar los registros de las invocaciones fallidas.

## Requerimiento de ARN calificados
<a name="durable-invoking-qualified-arns"></a>

Las funciones duraderas requieren identificadores calificados para la invocación. Debe invocar funciones duraderas con un número de versión, un alias o `$LATEST`. Puede utilizar un ARN calificado completo o un nombre de función con el sufijo de versión/alias. No puede utilizar un identificador no calificado (sin un sufijo de versión o alias).

**Invocaciones válidas:**

```
# Using full ARN with version number
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1

# Using full ARN with alias
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:prod

# Using full ARN with $LATEST
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:$LATEST

# Using function name with version number
my-durable-function:1

# Using function name with alias
my-durable-function:prod
```

**Invocaciones inválidas:**

```
# Unqualified ARN (not allowed)
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function

# Unqualified function name (not allowed)
my-durable-function
```

Este requisito garantiza que las ejecuciones duraderas se mantengan consistentes durante todo su ciclo de vida. Cuando se inicia una ejecución duradera, se fija en la versión de la función específica. Si la función se detiene y se reanuda horas o días después, Lambda invoca la misma versión que inició la ejecución, lo que garantiza la coherencia del código en todo el flujo de trabajo.

**Práctica recomendada**  
En lugar de utilizar `$LATEST`, utilice versiones numeradas o alias para las funciones duraderas de producción. Las versiones numeradas son inmutables y admiten una reproducción determinista. Opcionalmente, los alias proporcionan una referencia estable que se puede actualizar para que apunte a nuevas versiones sin cambiar el código de invocación. Cuando actualiza un alias, las nuevas ejecuciones utilizan la nueva versión, mientras que las ejecuciones en curso continúan con su versión original. Puede utilizar `$LATEST` para crear prototipos o acortar los tiempos de implementación durante el desarrollo; debe tener en cuenta que las ejecuciones podrían no reproducirse correctamente (o incluso fallar) si el código subyacente cambia durante las ejecuciones en ejecución.

## Descripción del ciclo de vida de una ejecución
<a name="durable-invoking-execution-lifecycle"></a>

Cuando invoca una función duradera, Lambda crea una ejecución duradera que puede abarcar múltiples invocaciones de funciones:

1. **Invocación inicial:** su solicitud de invocación crea una nueva ejecución duradera. Lambda asigna un identificador de ejecución único e inicia el procesamiento.

1. **Ejecución y puntos de control:** a medida que su función ejecuta operaciones duraderas, el SDK crea puntos de control para seguir el progreso.

1. **Suspensión (si es necesaria):** si la función utiliza esperas duraderas, como `wait` o `waitForCallback`, o reintentos automáticos por pasos, Lambda suspende la ejecución y deja de cobrar por el tiempo de cómputo.

1. **Reanudación:** cuando llega el momento de la reanudación (incluso después de los reintentos), Lambda vuelve a invocar la función. El SDK reproduce el registro de puntos de control y continúa desde donde se detuvo la ejecución.

1. **Finalización:** cuando la función devuelve un resultado final o arroja un error no controlado, se completa la ejecución duradera.

Para las invocaciones sincrónicas, la persona que llama espera a que se complete toda la ejecución duradera, incluidas las operaciones de espera. Si la ejecución supera el tiempo de espera de la invocación (15 minutos o menos), se agota el tiempo de espera de la invocación. Para las invocaciones asíncronas, Lambda regresa de inmediato, y la ejecución continúa de forma independiente. Utilice las API de ejecución duradera para realizar un seguimiento del estado de la ejecución y recuperar los resultados finales.

## Invocación desde el código de la aplicación
<a name="durable-invoking-with-sdk"></a>

Utilice los AWS SDK para invocar funciones duraderas desde el código de la aplicación. El proceso de invocación es idéntico al de las funciones estándar:

------
#### [ TypeScript ]

```
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';

const client = new LambdaClient({});

// Synchronous invocation
const response = await client.send(new InvokeCommand({
  FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
  Payload: JSON.stringify({ orderId: '12345' })
}));

const result = JSON.parse(Buffer.from(response.Payload!).toString());

// Asynchronous invocation
await client.send(new InvokeCommand({
  FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
  InvocationType: 'Event',
  Payload: JSON.stringify({ orderId: '12345' })
}));
```

------
#### [ Python ]

```
import boto3
import json

client = boto3.client('lambda')

# Synchronous invocation
response = client.invoke(
    FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    Payload=json.dumps({'orderId': '12345'})
)

result = json.loads(response['Payload'].read())

# Asynchronous invocation
client.invoke(
    FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    InvocationType='Event',
    Payload=json.dumps({'orderId': '12345'})
)
```

------

## Invocaciones encadenadas
<a name="durable-invoking-chained"></a>

Las funciones duraderas pueden invocar otras funciones duraderas y no duraderas mediante la operación `invoke` desde `DurableContext`. Esto crea una invocación encadenada en la que la función de llamada espera (suspende) a que se complete la función invocada:

------
#### [ TypeScript ]

```
export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Invoke another durable function and wait for result
    const result = await context.invoke(
      'process-order',
      'arn:aws:lambda:us-east-1:123456789012:function:order-processor:1',
      { orderId: event.orderId }
    );
    
    return { statusCode: 200, body: JSON.stringify(result) };
  }
);
```

------
#### [ Python ]

```
@durable_execution
def handler(event, context: DurableContext):
    # Invoke another durable function and wait for result
    result = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:order-processor:1',
        {'orderId': event['orderId']},
        name='process-order'
    )
    
    return {'statusCode': 200, 'body': json.dumps(result)}
```

------

Las invocaciones encadenadas crean un punto de control en la función de llamada. Si la función de llamada se interrumpe, se reanuda desde el punto de control con el resultado de la función invocada, sin volver a invocarla.

**nota**  
No se admiten invocaciones encadenadas entre cuentas. La función invocada debe estar en la misma cuenta de AWS que la función de llamada.

# Asignaciones de orígenes de eventos con funciones duraderas
<a name="durable-invoking-esm"></a>

Las funciones duraderas funcionan con todas las asignaciones de orígenes de eventos de Lambda. Configure las asignaciones de orígenes de eventos para funciones duraderas del mismo modo que las configura para funciones estándar. Las asignaciones de orígenes de eventos sondean automáticamente los orígenes de eventos, como Amazon SQS, Kinesis y DynamoDB Streams, e invocan su función con lotes de registros.

Las asignaciones de orígenes de eventos son útiles para funciones duraderas que procesan flujos o colas con flujos de trabajo complejos de varios pasos. Por ejemplo, puede crear una función duradera que procese los mensajes de Amazon SQS con reintentos, llamadas a la API externas y aprobaciones humanas.

## Cómo invocar asignaciones de orígenes de eventos con funciones duraderas
<a name="durable-esm-invocation-behavior"></a>

Las asignaciones de orígenes de eventos invocan funciones duraderas de forma sincrónica; por ello, esperan a que finalice la ejecución duradera completa antes de procesar el siguiente lote o marcar los registros como procesados. Si el tiempo total de la ejecución duradera supera los 15 minutos, se agota el tiempo de espera de la ejecución y se produce un error. La asignación de orígenes de eventos recibe una excepción de tiempo de espera y la gestiona de acuerdo con su configuración de reintentos.

## Límite de ejecución de 15 minutos
<a name="durable-esm-duration-limit"></a>

Cuando las asignaciones de orígenes de eventos invocan funciones duraderas, la duración total de la ejecución duradera no puede superar los 15 minutos. Este límite se aplica a toda la ejecución duradera, desde el principio hasta la finalización, no solo a las invocaciones de funciones individuales.

Este límite de 15 minutos es independiente del tiempo de espera de la función de Lambda (también 15 minutos como máximo). El tiempo de espera de la función controla cuánto tiempo puede ejecutarse cada invocación individual, mientras que el tiempo de espera de la ejecución duradera controla el tiempo total transcurrido desde el inicio de la ejecución hasta su finalización.

**Ejemplos de escenarios:**
+ **Válido:** una función duradera procesa un mensaje de Amazon SQS en tres pasos, cada uno de los cuales dura 2 minutos, y luego espera 5 minutos antes de completar el último paso. Tiempo total de ejecución: 11 minutos. Esto funciona porque el total es inferior a 15 minutos.
+ **Inválido:** una función duradera procesa un mensaje de Amazon SQS, completa el procesamiento inicial en 2 minutos y luego espera 20 minutos por una devolución de llamada externa antes de completarlo. Tiempo total de ejecución: 22 minutos. Si se supera el límite de 15 minutos, se producirá un error.
+ **Inválido:** una función duradera procesa un registro de Kinesis con varias operaciones de espera que suman un total de 30 minutos entre los pasos. Aunque cada invocación individual se completa rápidamente, el tiempo total de ejecución supera los 15 minutos.

**importante**  
Configure el tiempo de espera de la ejecución duradera en 15 minutos o menos cuando utilice asignaciones de orígenes de eventos; de lo contrario, la creación de la asignación de orígenes de eventos fallará. Si su flujo de trabajo requiere tiempos de ejecución más prolongados, utilice el patrón de funciones intermedias que se describe a continuación.

## Configuración de asignaciones de orígenes de eventos
<a name="durable-esm-configuration"></a>

Configure las asignaciones de orígenes de eventos para funciones duraderas mediante la consola de Lambda, la AWS CLI, o los AWS SDK. Todas las propiedades de la asignación de orígenes de eventos estándar se aplican a las funciones duraderas:

```
aws lambda create-event-source-mapping \
  --function-name arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1 \
  --event-source-arn arn:aws:sqs:us-east-1:123456789012:my-queue \
  --batch-size 10 \
  --maximum-batching-window-in-seconds 5
```

Recuerde utilizar un ARN calificado (con número de versión o alias) cuando configure las asignaciones de orígenes de eventos para funciones duraderas.

## Gestión de errores con asignaciones de orígenes de eventos
<a name="durable-esm-error-handling"></a>

Las asignaciones de orígenes de eventos proporcionan una gestión de errores integrada compatible con funciones duraderas:
+ **Comportamiento de reintento:** si la invocación inicial falla, la asignación de orígenes de eventos hace un reintento de acuerdo con su configuración de reintentos. Configure el número máximo de reintentos y los intervalos de reintento en función de sus requisitos.
+ **Colas de mensajes fallidos:** configure una cola de mensajes fallidos para capturar los registros que fallan después de todos los reintentos. Esto evita la pérdida de mensajes y permite la inspección manual de los registros fallidos.
+ **Errores de lotes parciales:** en el caso de Amazon SQS y Kinesis, utilice los informes de errores de lotes parciales para procesar los registros de forma individual y reintentar solo los registros fallidos.
+ **Segmentado en caso de error:** en el caso de Kinesis y DynamoDB Streams, habilite el segmentado en caso de error para dividir los lotes fallidos y aislar los registros problemáticos.

**nota**  
Las funciones duraderas admiten colas de mensajes fallidos (DLQ) para la gestión de errores, pero no admiten destinos de Lambda. Configure una DLQ para capturar los registros de las invocaciones fallidas.

Para obtener información completa sobre la gestión de errores en la asignación de orígenes de eventos, consulte la [asignación de orígenes de eventos](invocation-eventsourcemapping.md).

## Uso de una función intermedia para flujos de trabajo de larga duración
<a name="durable-esm-intermediary-function"></a>

Si el flujo de trabajo requiere más de 15 minutos para completarse, utilice una función estándar intermedia de Lambda entre la asignación de orígenes de eventos y su función duradera. La función intermedia recibe los eventos de la asignación de orígenes de eventos e invoca la función duradera de forma asíncrona, lo que elimina el límite de ejecución de 15 minutos.

Este patrón desacopla el modelo de invocación sincrónica de la asignación de orígenes de eventos del modelo de ejecución de larga duración de la función duradera. La asignación de orígenes de eventos invoca la función intermedia, que regresa rápidamente después de iniciar la ejecución duradera. A continuación, la función duradera se ejecuta de forma independiente durante el tiempo que sea necesario (hasta 1 año).

### Arquitectura
<a name="durable-esm-intermediary-architecture"></a>

El patrón de funciones intermedias utiliza tres componentes:

1. **Asignación de orígenes de eventos:** sondea el origen de eventos (Amazon SQS, Kinesis, DynamoDB Streams) e invoca la función intermedia de forma sincrónica con lotes de registros.

1. **Función intermedia: ** función estándar de Lambda que recibe eventos de la asignación de orígenes de eventos, valida y transforma los datos (si es necesario) e invoca la función duradera de forma asíncrona. Esta función se completa rápidamente (en general en menos de 1 segundo) y devuelve el control a la asignación de orígenes de eventos.

1. **Función duradera:** procesa el evento con una lógica compleja de varios pasos que puede ejecutarse durante períodos prolongados. Se invoca de forma asíncrona, por lo que no está limitada por el límite de 15 minutos.

### Implementación
<a name="durable-esm-intermediary-implementation"></a>

La función intermedia recibe el evento completo de la asignación de orígenes de eventos e invoca la función duradera de forma asíncrona. Utilice el parámetro del nombre de la ejecución para garantizar que se inicie una ejecución idempotente, lo que evitará que se duplique el procesamiento si la asignación de orígenes de eventos hace un reintento:

------
#### [ TypeScript ]

```
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
import { SQSEvent } from 'aws-lambda';
import { createHash } from 'crypto';

const lambda = new LambdaClient({});

export const handler = async (event: SQSEvent) => {
  // Invoke durable function asynchronously with execution name
  await lambda.send(new InvokeCommand({
    FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    InvocationType: 'Event',
    Payload: JSON.stringify({
      executionName: event.Name,
      event: event
    })
  }));
  
  return { statusCode: 200 };
};
```

------
#### [ Python ]

```
import boto3
import json
import hashlib

lambda_client = boto3.client('lambda')

def handler(event, context):  
    # Invoke durable function asynchronously with execution name
    lambda_client.invoke(
        FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
        InvocationType='Event',
        Payload=json.dumps({
            'executionName': execution_name,
            'event': event["name"]
        })
    )
    
    return {'statusCode': 200}
```

------

En el caso de la idempotencia en la propia función intermedia, utilice [Powertools para AWS Lambda](https://docs.aws.amazon.com//powertools/) para evitar que se dupliquen las invocaciones de la función duradera si la asignación de orígenes de eventos reintenta la función intermedia.

La función duradera recibe la carga útil con el nombre de la ejecución y procesa todos los registros con una lógica de larga duración:

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (payload: any, context: DurableContext) => {
    const sqsEvent = payload.event;
    
    // Process each record with complex, multi-step logic
    const results = await context.map(
      sqsEvent.Records,
      async (ctx, record) => {
        const validated = await ctx.step('validate', async () => {
          return validateOrder(JSON.parse(record.body));
        });
        
        // Wait for external approval (could take hours or days)
        const approval = await ctx.waitForCallback(
          'approval',
          async (callbackId) => {
            await requestApproval(callbackId, validated);
          },
          { timeout: { hours: 48 } }
        );
        
        // Complete processing
        return await ctx.step('complete', async () => {
          return completeOrder(validated, approval);
        });
      }
    );
    
    return { statusCode: 200, processed: results.getResults().length };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
from aws_durable_execution_sdk_python.config import Duration, WaitForCallbackConfig
from collections.abc import Sequence
import json

def validate_order(order_data: dict) -> dict:
    """Validate order data - always passes."""
    return order_data

def request_approval(callback_id: str, validated_order: dict) -> None:
    """Request approval for the order - always passes."""
    pass

def complete_order(validated_order: dict, approval_result: str) -> dict:
    """Complete the order processing - always passes."""
    return validated_order

@durable_execution
def lambda_handler(payload, context: DurableContext):
    sqs_event = payload['event']

    def process_record(
        ctx: DurableContext, 
        record: dict, 
        index: int, 
        items: Sequence[dict]
    ) -> dict:
        validated = ctx.step(
            lambda _: validate_order(json.loads(record['body'])),
            name=f'validate-{index}'
        )

        approval = ctx.wait_for_callback(
            submitter=lambda callback_id, wait_ctx: request_approval(callback_id, validated),
            name=f'approval-{index}',
            config=WaitForCallbackConfig(timeout=Duration.from_seconds(172800))
        )

        return ctx.step(
            lambda _: complete_order(validated, approval),
            name=f'complete-{index}'
        )

    results = context.map(
        inputs=sqs_event['Records'],
        func=process_record,
        name='process-records'
    )

    return {
        'statusCode': 200, 
        'started': results.started_count,
        'completed': results.success_count,
        'failed': results.failure_count,
        'total': results.total_count
    }
```

------

### Consideraciones clave
<a name="durable-esm-intermediary-tradeoffs"></a>

Este patrón elimina el límite de ejecución de 15 minutos al desvincular la asignación de orígenes de eventos de la ejecución duradera. La función intermedia regresa inmediatamente después de iniciar la ejecución duradera, lo que permite que la asignación de orígenes de eventos continúe procesándose. A continuación, la función duradera se ejecuta de forma independiente durante el tiempo que sea necesario.

La función intermedia tiene éxito cuando invoca la función duradera, no cuando se completa la ejecución duradera. Si la ejecución duradera falla más adelante, la asignación de orígenes de eventos no hará un reintento porque ya procesó el lote correctamente. Implemente la gestión de errores en la función duradera y configure las colas de mensajes fallidos para las ejecuciones fallidas.

Utilice el parámetro del nombre de la ejecución para garantizar que se inicie una ejecución idempotente. Si la asignación de orígenes de eventos reintenta la función intermedia, la función duradera no iniciará una ejecución duplicada porque el nombre de la ejecución ya existe.

## Orígenes de eventos compatibles
<a name="durable-esm-supported-sources"></a>

Las funciones duraderas admiten todos los orígenes de eventos de Lambda que utilizan asignaciones de orígenes de eventos:
+ Colas de Amazon SQS (estándar y FIFO)
+ Flujos de Kinesis
+ DynamoDB Streams
+ Amazon Managed Streaming for Apache Kafka (Amazon MSK)
+ Apache Kafka autoadministrado
+ Amazon MQ (ActiveMQ y RabbitMQ)
+ Flujos de cambios de Amazon DocumentDB

Todos los tipos de orígenes de eventos están sujetos al límite de ejecución duradera de 15 minutos cuando se invocan funciones duraderas.

# Reintentos de funciones duraderas de Lambda
<a name="durable-execution-sdk-retries"></a>

Las funciones duraderas proporcionan capacidades de reintento automático que hacen que sus aplicaciones sean resistentes a los errores transitorios. El SDK gestiona los reintentos en dos niveles: los reintentos escalonados en caso de errores de lógica empresarial y los reintentos de backend en caso de errores de infraestructura.

## Reintentos escalonados
<a name="durable-step-retries"></a>

Cuando se produce una excepción no detectada en un paso, el SDK reintenta el paso automáticamente en función de la estrategia de reintento configurada. Los reintentos escalonados son operaciones de punto de control que permiten que el SDK suspenda la ejecución y la reanude más adelante sin perder el progreso.

### Comportamiento de reintentos escalonados
<a name="durable-step-retry-behavior"></a>

En la siguiente tabla, se describe cómo el SDK gestiona las excepciones dentro de los pasos:


| Escenario | ¿Qué sucede? | Medición del impacto | 
| --- | --- | --- | 
| Excepción en el paso con intentos pendientes de reintento | El SDK crea un punto de control para el reintento y suspende la función. En la siguiente invocación, el paso hace un reintento con el retraso del retroceso configurado. | 1 operación \$1 tamaño de carga útil del error | 
| Excepción en el paso y no quedan reintentos | El paso falla e inicia una excepción. Si el código del controlador no detecta esta excepción, se produce un error en toda la ejecución. | 1 operación \$1 tamaño de carga útil del error | 

Cuando es necesario reintentar un paso, el SDK comprueba el estado del reintento y sale de la invocación a Lambda si no hay ningún otro trabajo en ejecución. Esto permite que el SDK implemente los retrasos de retroceso sin consumir recursos de cómputo. La función se reanuda automáticamente tras el período de retroceso.

### Configuración de estrategias de reintentos escalonados
<a name="durable-step-retry-configuration"></a>

Configure estrategias de reintentos para controlar la forma en que los pasos gestionan los errores. Puede especificar el número máximo de intentos, los intervalos de retroceso y las condiciones de reintento.

**Retroceso exponencial con un número máximo de intentos:**

------
#### [ TypeScript ]

```
const result = await context.step('call-api', async () => {
  const response = await fetch('https://api.example.com/data');
  if (!response.ok) throw new Error(`API error: ${response.status}`);
  return await response.json();
}, {
  retryStrategy: (error, attemptCount) => {
    if (attemptCount >= 5) {
      return { shouldRetry: false };
    }
    // Exponential backoff: 2s, 4s, 8s, 16s, 32s (capped at 300s)
    const delay = Math.min(2 * Math.pow(2, attemptCount - 1), 300);
    return { shouldRetry: true, delay: { seconds: delay } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    if attempt_count >= 5:
        return {'should_retry': False}
    # Exponential backoff: 2s, 4s, 8s, 16s, 32s (capped at 300s)
    delay = min(2 * (2 ** (attempt_count - 1)), 300)
    return {'should_retry': True, 'delay': delay}

result = context.step(
    lambda _: call_external_api(),
    name='call-api',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**Retroceso de intervalo fijo:**

------
#### [ TypeScript ]

```
const orders = await context.step('query-orders', async () => {
  return await queryDatabase(event.userId);
}, {
  retryStrategy: (error, attemptCount) => {
    if (attemptCount >= 3) {
      return { shouldRetry: false };
    }
    return { shouldRetry: true, delay: { seconds: 5 } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    if attempt_count >= 3:
        return {'should_retry': False}
    return {'should_retry': True, 'delay': 5}

orders = context.step(
    lambda _: query_database(event['userId']),
    name='query-orders',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**Reintento condicional (reintenta solo errores específicos):**

------
#### [ TypeScript ]

```
const result = await context.step('call-rate-limited-api', async () => {
  const response = await fetch('https://api.example.com/data');
  
  if (response.status === 429) throw new Error('RATE_LIMIT');
  if (response.status === 504) throw new Error('TIMEOUT');
  if (!response.ok) throw new Error(`API_ERROR_${response.status}`);
  
  return await response.json();
}, {
  retryStrategy: (error, attemptCount) => {
    // Only retry rate limits and timeouts
    const isRetryable = error.message === 'RATE_LIMIT' || error.message === 'TIMEOUT';
    
    if (!isRetryable || attemptCount >= 3) {
      return { shouldRetry: false };
    }
    
    // Exponential backoff: 1s, 2s, 4s (capped at 30s)
    const delay = Math.min(Math.pow(2, attemptCount - 1), 30);
    return { shouldRetry: true, delay: { seconds: delay } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    # Only retry rate limits and timeouts
    is_retryable = str(error) in ['RATE_LIMIT', 'TIMEOUT']
    
    if not is_retryable or attempt_count >= 3:
        return {'should_retry': False}
    
    # Exponential backoff: 1s, 2s, 4s (capped at 30s)
    delay = min(2 ** (attempt_count - 1), 30)
    return {'should_retry': True, 'delay': delay}

result = context.step(
    lambda _: call_rate_limited_api(),
    name='call-rate-limited-api',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**Deshabilite los reintentos:**

------
#### [ TypeScript ]

```
const isDuplicate = await context.step('check-duplicate', async () => {
  return await checkIfOrderExists(event.orderId);
}, {
  retryStrategy: () => ({ shouldRetry: false })
});
```

------
#### [ Python ]

```
is_duplicate = context.step(
    lambda _: check_if_order_exists(event['orderId']),
    name='check-duplicate',
    config=StepConfig(
        retry_strategy=lambda error, attempt: {'should_retry': False}
    )
)
```

------

Cuando la estrategia de reintentos devuelve `shouldRetry: false`, el paso falla inmediatamente sin reintentos. Puede usar esto para operaciones que no deben reintentarse, como las comprobaciones de idempotencia o las operaciones con efectos secundarios que no pueden repetirse de forma segura.

## Excepciones fuera de los pasos
<a name="durable-handler-exceptions"></a>

Cuando se produce una excepción no detectada en el código del controlador, pero fuera de cualquier paso, el SDK marca la ejecución como fallida. Esto garantiza que los errores en la lógica de la aplicación se capturen y notifiquen correctamente.


| Escenario | ¿Qué sucede? | Medición del impacto | 
| --- | --- | --- | 
| Excepción en el código del controlador fuera de cualquier paso | El SDK marca la ejecución como FALLIDA y devuelve el error. La excepción no se reintenta automáticamente. | Tamaño de carga útil del error | 

Para habilitar el reintento automático en el código propenso a errores, encapsúlelo en un paso con una estrategia de reintento. Los pasos permiten reintentos automáticos con un tiempo de retroceso configurable, mientras que el código fuera de los pasos produce un error inmediato.

## Reintentos en backend
<a name="durable-backend-retries"></a>

Los reintentos del backend se producen cuando Lambda encuentra errores en la infraestructura, errores de tiempo de ejecución o cuando el SDK no puede comunicarse con el servicio de ejecución duradera. Lambda vuelve a intentar automáticamente estos errores para ayudar a que las funciones duraderas se recuperen de problemas transitorios de infraestructura.

### Escenarios de reintentos en backend
<a name="durable-backend-retry-scenarios"></a>

Lambda reintenta la función automáticamente cuando se encuentra en los siguientes escenarios:
+ **Errores de servicio interno**: cuando Lambda o el servicio de ejecución duradera devuelven un error 5xx, que indica un problema de servicio temporal.
+ **Limitación:** cuando su función es restringida debido a límites de concurrencia o Service Quotas.
+ **Tiempos de espera:** cuando el SDK no puede acceder al servicio de ejecución duradera dentro del período de espera.
+ **Errores de inicialización del entorno de pruebas**: cuando Lambda no puede inicializar el entorno de ejecución.
+ **Errores de tiempo de ejecución**: cuando el tiempo de ejecución de Lambda encuentra errores fuera del código de la función, como errores de memoria insuficiente o bloqueos del proceso.
+ **Errores de token de punto de control inválido**: cuando el token del punto de control ya no es válido, normalmente debido a cambios de estado del lado del servicio.

En la siguiente tabla, se describe cómo el SDK gestiona estos escenarios.


| Escenario | ¿Qué sucede? | Medición del impacto | 
| --- | --- | --- | 
| Error de tiempo de ejecución ajeno al controlador duradero (OOM, tiempo de espera, bloqueo) | Lambda reintenta la invocación automáticamente. El SDK reproduce desde el último punto de control y omite los pasos completados. | Tamaño de carga útil del error \$1 1 operación por reintento | 
| Error de servicio (5xx) o tiempo de espera cuando se llama a las API de CheckpointDurableExecution y GetDurableExecutionState. | Lambda reintenta la invocación automáticamente. El SDK reproduce desde el último punto de control. | Tamaño de carga útil del error \$1 1 operación por reintento | 
| Limitación (429) o token de punto de control inválido cuando se llama a las API de CheckpointDurableExecution y GetDurableExecutionState. | Lambda reintenta la invocación automáticamente con un retroceso exponencial. El SDK reproduce desde el último punto de control. | Tamaño de carga útil del error \$1 1 operación por reintento | 
| Error del cliente (4xx, excepto 429 y token inválido) cuando se llama a las API de CheckpointDurableExecution y GetDurableExecutionState. | El SDK marca la ejecución como FALLIDA. No se produce ningún reintento automático porque el error indica un problema permanente. | Tamaño de carga útil del error | 

Los reintentos de backend utilizan un retroceso exponencial y continúan hasta que la función se ejecute correctamente o se agote el tiempo de espera de la ejecución. Durante la reproducción, el SDK omite los puntos de control completados y continúa con la ejecución desde la última operación satisfactoria, lo que garantiza que la función no vuelva a ejecutar el trabajo completado.

## Prácticas recomendadas para los reintentos
<a name="durable-retry-best-practices"></a>

Siga estas prácticas recomendadas cuando configure estrategias de reintentos:
+ **Configure estrategias de reintentos explícitas**: no confíe en el comportamiento de reintento predeterminado en producción. Configure estrategias de reintento explícitas con el máximo de intentos y los intervalos de retroceso adecuados para su caso de uso.
+ **Utilice reintentos condicionales**: implemente la lógica de `shouldRetry` para reintentar solo los errores transitorios (límites de tasa, tiempos de espera) y responder rápido a los errores permanentes (errores de validación, no encontrados).
+ **Establezca el máximo de intentos adecuado**: equilibre la resiliencia y el tiempo de ejecución. Demasiados reintentos pueden retrasar la detección de errores, mientras que muy pocos pueden provocar errores innecesarios.
+ **Utilice un retroceso exponencial** el retroceso exponencial reduce la carga de los servicios posteriores y aumenta la probabilidad de recuperación en caso de errores transitorios.
+ **Encapsule el código propenso a errores en pasos:** no es posible hacer un reintento automático con el código fuera de los pasos. Combine las llamadas a las API externas, las consultas a la base de datos y otras operaciones propensas a errores en pasos con estrategias de reintento.
+ **Supervise las métricas de reintentos**: realice un seguimiento de las operaciones de reintentos escalonados y de los errores de ejecución en Amazon CloudWatch para identificar patrones y optimizar las estrategias de reintentos.

# Idempotencia
<a name="durable-execution-idempotency"></a>

Las funciones duraderas proporcionan una idempotencia integrada para los inicios de la ejecución a través de los nombres de ejecución. Cuando proporciona un nombre de ejecución, Lambda lo usa para evitar ejecuciones duplicadas y habilitar reintentos seguros de las solicitudes de invocación. De forma predeterminada, los pasos tienen una semántica de ejecución de como mínimo una vez: durante la reproducción, el SDK devuelve los resultados comprobados sin volver a ejecutar los pasos completados, pero su lógica empresarial debe ser idempotente para gestionar los posibles reintentos antes de que se completen.

**nota**  
Las asignaciones de orígenes de eventos (ESM) de Lambda no admiten la idempotencia en el momento del lanzamiento. Por lo tanto, cada invocación (incluidos los reintentos) inicia una nueva ejecución duradera. Para garantizar una ejecución idempotente con asignaciones de orígenes de eventos, implemente la lógica de idempotencia en el código de su función, como con [Powertools para AWS Lambda](https://docs.aws.amazon.com//powertools/) o utilice una función de Lambda normal como proxy (despachador) para invocar una función duradera con una clave de idempotencia (parámetro del nombre de la ejecución).

## Nombres de ejecución
<a name="durable-idempotency-execution-names"></a>

Puede proporcionar un nombre de ejecución cuando invoca una función duradera. El nombre de la ejecución actúa como clave de idempotencia, lo que permite reintentar las solicitudes de invocación de forma segura sin crear ejecuciones duplicadas. Si no proporciona un nombre, Lambda genera automáticamente un identificador de ejecución único.

Los nombres de ejecución deben ser únicos dentro de su cuenta y región. Cuando invoca una función con un nombre de ejecución que ya existe, el comportamiento de Lambda depende del estado de la ejecución existente y de si la carga útil coincide.

## Comportamiento de idempotencia
<a name="durable-idempotency-behavior"></a>

En la siguiente tabla, se describe cómo Lambda gestiona las solicitudes de invocación en función de si proporciona un nombre de ejecución, el estado de ejecución existente y si la carga útil coincide:


| Escenario | ¿Se proporcionó un nombre? | Estado de ejecución existente | ¿Carga útil idéntica? | Comportamiento | 
| --- | --- | --- | --- | --- | 
| 1 | No | N/A | N/A | Nueva ejecución iniciada: Lambda genera un identificador de ejecución único e inicia una nueva ejecución. | 
| 2 | Sí | Nunca existió o la retención caducó. | N/A | Nueva ejecución iniciada: Lambda inicia una nueva ejecución con el nombre proporcionado. | 
| 3 | Sí | Ejecutar | Sí | Inicio idempotente: Lambda devuelve la información de ejecución existente sin iniciar un duplicado. En el caso de las invocaciones sincrónicas, esto actúa como una reconexión a la ejecución en ejecución. | 
| 4 | Sí | Ejecutar | No | Error: Lambda devuelve un error de DurableExecutionAlreadyExists porque ya se está ejecutando una ejecución con este nombre con una carga útil diferente. | 
| 5 | Sí | Cerrado (satisfactorio, fallido, detenido o agotado el tiempo de espera) | Sí | Inicio idempotente: Lambda devuelve la información de ejecución existente sin iniciar una nueva ejecución. Se devuelve el resultado de la ejecución cerrada. | 
| 6 | Sí | Cerrado (satisfactorio, fallido, detenido o agotado el tiempo de espera) | No | Error: Lambda devuelve un error de DurableExecutionAlreadyExists porque una ejecución con este nombre ya se completó con una carga útil diferente. | 

**Nota**  
Los escenarios 3 y 5 demuestran un comportamiento idempotente en el que Lambda gestiona de forma segura las solicitudes de invocación duplicadas al devolver la información de ejecución existente en lugar de crear duplicados.

## Idempotencia escalonada
<a name="durable-idempotency-steps"></a>

De forma predeterminada, los pasos tienen una semántica de ejecución de como mínimo una vez. Cuando la función se reproduce tras una espera, una devolución de llamada o un error, el SDK compara cada paso con el registro de los puntos de control. En el caso de los pasos que ya se hayan completado, el SDK devuelve el resultado del punto de control sin volver a ejecutar la lógica escalonada. Sin embargo, si un paso falla o la función se interrumpe antes de que se complete, es posible que el paso se ejecute varias veces.

Su lógica empresarial, encapsulada en pasos, debe ser idempotente para gestionar posibles reintentos. Utilice claves de idempotencia para asegurarse de que las operaciones como los pagos o la escritura de bases de datos se ejecuten solo una vez, incluso si se reintenta el paso.

**Ejemplo: uso de claves de idempotencia por pasos**

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';
import { randomUUID } from 'crypto';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate idempotency key once
    const idempotencyKey = await context.step('generate-key', async () => {
      return randomUUID();
    });
    
    // Use idempotency key in payment API to prevent duplicate charges
    const payment = await context.step('process-payment', async () => {
      return paymentAPI.charge({
        amount: event.amount,
        idempotencyKey: idempotencyKey
      });
    });
    
    return { statusCode: 200, payment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate idempotency key once
    idempotency_key = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-key'
    )
    
    # Use idempotency key in payment API to prevent duplicate charges
    payment = context.step(
        lambda _: payment_api.charge(
            amount=event['amount'],
            idempotency_key=idempotency_key
        ),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'payment': payment}
```

------

Puede configurar los pasos para utilizar la semántica de ejecución de como máximo una vez si configura el modo de ejecución en `AT_MOST_ONCE_PER_RETRY`. Esto garantiza que el paso se ejecute como máximo una vez por reintento, pero es posible que no se ejecute en absoluto si la función se interrumpe antes de que se complete el paso.

El SDK impone la reproducción determinista porque valida que los nombres y el orden de los pasos coincidan con el registro de los puntos de control durante la reproducción. Si el código intenta ejecutar los pasos en un orden diferente o con nombres diferentes, el SDK lanza un `NonDeterministicExecutionError`.

**Cómo funciona la reproducción con los pasos completados:**

1. Primera invocación: la función ejecuta el paso A, crea un punto de control y, a continuación, espera.

1. Segunda invocación (después de la espera): la función se reproduce desde el principio, el paso A devuelve el resultado del punto de control al instante sin volver a ejecutarse y, a continuación, continúa con el paso B.

1. Tercera invocación (después de la espera): la función se reproduce desde el principio, los pasos A y B devuelven los resultados de los puntos de control al instante y, a continuación, continúa con el paso C.

Este mecanismo de reproducción garantiza que los pasos completados no vuelvan a ejecutarse, pero su lógica empresarial debe seguir siendo idempotente para gestionar los reintentos antes de completarlos.