

# Práticas recomendadas para funções duráveis do Lambda
<a name="durable-best-practices"></a>

As funções duráveis usam um modelo de execução baseado em reprodução que exige padrões diferentes das funções do Lambda tradicionais. Siga essas melhores práticas para criar fluxos de trabalho confiáveis e econômicos.

## Escreva código determinístico
<a name="durable-determinism"></a>

Durante a reprodução, sua função é executada desde o início e deve seguir o mesmo caminho de execução da execução original. O código fora das operações duráveis deve ser determinístico, produzindo os mesmos resultados com as mesmas entradas.

**Encapsule as operações não determinísticas em etapas:**
+ Geração de números aleatórios e UUIDs
+ Hora atual ou timestamps
+ Chamadas de API externas e consultas de banco de dados
+ Operações do sistema de arquivos

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

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

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate transaction ID inside a step
    const transactionId = await context.step('generate-transaction-id', async () => {
      return randomUUID();
    });
    
    // Use the same ID throughout execution, even during replay
    const payment = await context.step('process-payment', async () => {
      return processPayment(event.amount, transactionId);
    });
    
    return { statusCode: 200, transactionId, payment };
  }
);
```

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

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate transaction ID inside a step
    transaction_id = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-transaction-id'
    )
    
    # Use the same ID throughout execution, even during replay
    payment = context.step(
        lambda _: process_payment(event['amount'], transaction_id),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'transactionId': transaction_id, 'payment': payment}
```

------

**Importante**  
Não use variáveis globais ou fechamentos para compartilhar o estado entre as etapas. Passe dados por meio de valores de retorno. O estado global é interrompido durante a reprodução, pois as etapas retornam resultados em cache, mas as variáveis globais são redefinidas.

**Evite mutações de encerramento:** as variáveis capturadas em fechamentos podem perder mutações durante a reprodução. As etapas retornam resultados em cache, mas as atualizações de variáveis fora da etapa não são reproduzidas.

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

```
// ❌ WRONG: Mutations lost on replay
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    await context.step(async () => {
      total += item.price; // ⚠️ Mutation lost on replay!
      return saveItem(item);
    });
  }
  
  return { total }; // Inconsistent value!
});

// ✅ CORRECT: Accumulate with return values
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    total = await context.step(async () => {
      const newTotal = total + item.price;
      await saveItem(item);
      return newTotal; // Return updated value
    });
  }
  
  return { total }; // Consistent!
});

// ✅ EVEN BETTER: Use map for parallel processing
export const handler = withDurableExecution(async (event, context) => {
  const results = await context.map(
    items,
    async (ctx, item) => {
      await ctx.step(async () => saveItem(item));
      return item.price;
    }
  );
  
  const total = results.getResults().reduce((sum, price) => sum + price, 0);
  return { total };
});
```

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

```
# ❌ WRONG: Mutations lost on replay
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        context.step(
            lambda _: save_item_and_mutate(item, total),  # ⚠️ Mutation lost on replay!
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Inconsistent value!

# ✅ CORRECT: Accumulate with return values
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        total = context.step(
            lambda _: save_item_and_return_total(item, total),
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Consistent!

# ✅ EVEN BETTER: Use map for parallel processing
@durable_execution
def handler(event, context: DurableContext):
    def process_item(ctx, item):
        ctx.step(lambda _: save_item(item))
        return item['price']
    
    results = context.map(items, process_item)
    total = sum(results.get_results())
    
    return {'total': total}
```

------

## Design para idempotência
<a name="durable-idempotency"></a>

As operações podem ser executadas várias vezes devido a novas tentativas ou repetições. Operações não idempotentes causam efeitos colaterais duplicados, como cobrar duas vezes dos clientes ou enviar vários e-mails.

**Use tokens de idempotência:** gere tokens dentro das etapas e inclua-os em chamadas externas de API para evitar operações duplicadas.

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

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

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

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

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

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

------

**Use a semântica de no máximo uma vez:** para operações críticas que nunca devem ser duplicadas (transações financeiras, deduções de estoque), configure o modo de execução de no máximo uma vez.

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

```
// Critical operation that must not duplicate
await context.step('deduct-inventory', async () => {
  return inventoryService.deduct(event.productId, event.quantity);
}, {
  executionMode: 'AT_MOST_ONCE_PER_RETRY'
});
```

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

```
# Critical operation that must not duplicate
context.step(
    lambda _: inventory_service.deduct(event['productId'], event['quantity']),
    name='deduct-inventory',
    config=StepConfig(execution_mode='AT_MOST_ONCE_PER_RETRY')
)
```

------

**Idempotência do banco de dados:** use padrões de verificação antes da gravação, atualizações condicionais ou operações de atualização para evitar registros duplicados.

## Gerencie o estado de forma eficiente
<a name="durable-state-management"></a>

Cada ponto de verificação salva o estado no armazenamento persistente. Objetos de estado grandes aumentam os custos, diminuem o controle e afetam a performance. Armazene somente dados essenciais de coordenação do fluxo de trabalho.

**Mantenha o estado mínimo:**
+ Armazene IDs e referências, não objetos completos
+ Obtenha dados detalhados em etapas, conforme necessário
+ Use o Amazon S3 ou o DynamoDB para dados grandes, passe referências no estado
+ Evite passar grandes cargas úteis entre as etapas

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

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

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Store only the order ID, not the full order object
    const orderId = event.orderId;
    
    // Fetch data within each step as needed
    await context.step('validate-order', async () => {
      const order = await orderService.getOrder(orderId);
      return validateOrder(order);
    });
    
    await context.step('process-payment', async () => {
      const order = await orderService.getOrder(orderId);
      return processPayment(order);
    });
    
    return { statusCode: 200, orderId };
  }
);
```

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

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext

@durable_execution
def handler(event, context: DurableContext):
    # Store only the order ID, not the full order object
    order_id = event['orderId']
    
    # Fetch data within each step as needed
    context.step(
        lambda _: validate_order(order_service.get_order(order_id)),
        name='validate-order'
    )
    
    context.step(
        lambda _: process_payment(order_service.get_order(order_id)),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'orderId': order_id}
```

------

## Projete etapas eficazes
<a name="durable-step-design"></a>

As etapas são a unidade fundamental de trabalho em funções duráveis. Etapas bem projetadas facilitam a compreensão, a depuração e a manutenção dos fluxos de trabalho.

**Princípios do projeto de etapas**
+ **Use nomes descritivos**: nomes como `validate-order` em vez de `step1` tornam os logs e os erros mais fáceis de entender
+ **Mantenha os nomes estáticos**: não use nomes dinâmicos com timestamps ou valores aleatórios. Os nomes das etapas devem ser determinísticos para a reprodução
+ **Equilibre a granularidade**: divida operações complexas em etapas focadas, mas evite pequenas etapas excessivas que aumentem a sobrecarga do ponto de verificação
+ **Operações relacionadas a grupos**: as operações que devem ter êxito ou falhar juntas pertencem à mesma etapa

## Use as operações de espera com eficiência
<a name="durable-wait-operations"></a>

As operações de espera suspendem a execução sem consumir recursos ou incorrer em custos. Use-as em vez de manter o Lambda em execução.

**Esperas com base no tempo:** use `context.wait()` para retardos em vez de `setTimeout` ou `sleep`.

**Retornos de chamada externos:** use `context.waitForCallback()` ao esperar por sistemas externos. Sempre defina tempos limite para evitar esperas indefinidas.

**Sondagem:** use `context.waitForCondition()` com recuo exponencial para sondar serviços externos sem sobrecarregá-los.

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

```
// Wait 24 hours without cost
await context.wait({ seconds: 86400 });

// Wait for external callback with timeout
const result = await context.waitForCallback(
  'external-job',
  async (callbackId) => {
    await externalService.submitJob({
      data: event.data,
      webhookUrl: `https://api.example.com/callbacks/${callbackId}`
    });
  },
  { timeout: { seconds: 3600 } }
);
```

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

```
# Wait 24 hours without cost
context.wait(86400)

# Wait for external callback with timeout
result = context.wait_for_callback(
    lambda callback_id: external_service.submit_job(
        data=event['data'],
        webhook_url=f'https://api.example.com/callbacks/{callback_id}'
    ),
    name='external-job',
    config=WaitForCallbackConfig(timeout_seconds=3600)
)
```

------

## Considerações adicionais
<a name="durable-additional-considerations"></a>

**Tratamento de erros:** faça uma nova tentativa em falhas transitórias, como tempos limite de rede e limites de taxa. Não tente novamente falhas permanentes, como entrada inválida ou erros de autenticação. Configure estratégias de novas tentativas com o máximo de tentativas e taxas de recuo apropriadas. Para obter exemplos detalhados, consulte [Tratamento de erros e novas tentativas](durable-execution-sdk-retries.md).

**Performance:** minimize o tamanho do ponto de verificação armazenando referências em vez de cargas úteis completas. Use `context.parallel()` e `context.map()` para executar operações independentes simultaneamente. Execute operações relacionadas em lotes para reduzir a sobrecarga dos pontos de verificação.

**Versionamento:** invoque funções com números de versão ou aliases para fixar execuções em versões de código específicas. Certifique-se de que as novas versões do código possam tratar o estado das versões mais antigas. Não renomeie etapas nem altere seu comportamento de forma a interromper a reprodução.

**Serialização:** use tipos compatíveis com JSON para entradas e resultados da operação. Converta datas em sequências ISO e objetos personalizados em objetos simples antes de passá-los para operações duráveis.

**Monitoramento:** habilite o registro em log estruturado com IDs de execução e nomes de etapas. Configure os alarmes do CloudWatch para as taxas de erro e a duração da execução. Use rastreamento para identificar gargalos. Para obter orientação detalhada, consulte [Monitoramento e depuração](durable-monitoring.md).

**Testes:** teste o caminho ideal, o tratamento de erros e o comportamento de reprodução. Teste cenários de tempo limite para retornos de chamada e esperas. Use testes locais para reduzir o tempo de iteração. Para obter orientação detalhada, consulte [Testes de funções duráveis](durable-testing.md).

**Erros comuns a serem evitados:** não aninhe chamadas `context.step()`, use contextos secundários. Encapsule as operações não determinísticas em etapas. Sempre defina tempos limite para retornos de chamada. Equilibre a granularidade das etapas com a sobrecarga do ponto de verificação. Armazene referências em vez de objetos grandes no estado.

## Recursos adicionais
<a name="durable-additional-resources"></a>
+ [Documentação do SDK do Python](https://github.com/aws/aws-durable-execution-sdk-python/tree/main/docs): referência completa da API, padrões de testes e exemplos avançados
+ [Documentação do SDK do TypeScript](https://github.com/aws/aws-durable-execution-sdk-js/tree/main/docs): referência completa da API, padrões de testes e exemplos avançados