

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Le migliori pratiche per le funzioni durevoli Lambda
<a name="durable-best-practices"></a>

Le funzioni durevoli utilizzano un modello di esecuzione basato sulla riproduzione che richiede modelli diversi rispetto alle tradizionali funzioni Lambda. Segui queste best practice per creare flussi di lavoro affidabili e convenienti.

## Scrivi codice deterministico
<a name="durable-determinism"></a>

Durante la riproduzione, la funzione viene eseguita dall'inizio e deve seguire lo stesso percorso di esecuzione dell'esecuzione originale. Il codice al di fuori delle operazioni durevoli deve essere deterministico e produrre gli stessi risultati con gli stessi input.

**Suddividi le operazioni non deterministiche in fasi:**
+ Generazione di numeri casuali e UUIDs
+ Ora o timestamp correnti
+ Chiamate API esterne e interrogazioni al database
+ Operazioni sul file system

------
#### [ 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**  
Non utilizzare variabili o chiusure globali per condividere lo stato tra i passaggi. Passa i dati attraverso i valori restituiti. Lo stato globale si interrompe durante la riproduzione perché i passaggi restituiscono risultati memorizzati nella cache ma le variabili globali vengono reimpostate.

**Evita le mutazioni di chiusura: le** variabili catturate nelle chiusure possono perdere mutazioni durante la riproduzione. I passaggi restituiscono risultati memorizzati nella cache, ma gli aggiornamenti delle variabili al di fuori del passaggio non vengono riprodotti.

------
#### [ 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}
```

------

## Progettato per l'idempotenza
<a name="durable-idempotency"></a>

Le operazioni possono essere eseguite più volte a causa di nuovi tentativi o ripetizioni. Le operazioni non idempotenti causano effetti collaterali duplicati, come addebitare due volte i clienti o inviare più e-mail.

**Usa token di idempotenza: genera token** all'interno dei passaggi e includili nelle chiamate API esterne per evitare operazioni duplicate.

------
#### [ 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}
```

------

**Usa la at-most-once semantica:** per operazioni critiche che non devono mai essere duplicate (transazioni finanziarie, detrazioni di inventario), configura la modalità di esecuzione. at-most-once

------
#### [ 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')
)
```

------

**Idempotenza del database:** utilizza check-before-write modelli, aggiornamenti condizionali o operazioni di alterazione per evitare la duplicazione dei record.

## Gestisci lo stato in modo efficiente
<a name="durable-state-management"></a>

Ogni checkpoint salva lo stato nell'archiviazione persistente. Gli oggetti a stato di grandi dimensioni aumentano i costi, rallentano il checkpoint e influiscono sulle prestazioni. Archivia solo i dati essenziali di coordinamento del flusso di lavoro.

**Mantieni lo stato minimo:**
+ Archivio IDs e riferimenti, non oggetti completi
+ Recupera dati dettagliati in pochi passaggi, se necessario
+ Usa Amazon S3 o DynamoDB per dati di grandi dimensioni, trasferisci i riferimenti nello stato
+ Evita di far passare carichi utili di grandi dimensioni tra i passaggi

------
#### [ 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}
```

------

## Progetta passaggi efficaci
<a name="durable-step-design"></a>

I gradini sono l'unità di lavoro fondamentale nelle funzioni durevoli. I passaggi ben progettati semplificano la comprensione, il debug e la manutenzione dei flussi di lavoro.

**Principi di progettazione Step:**
+ **Usa nomi descrittivi**, ad esempio `validate-order` invece di `step1` semplificare la comprensione di log ed errori
+ **Mantieni i nomi statici**: non utilizzare nomi dinamici con timestamp o valori casuali. I nomi delle fasi devono essere deterministici per la riproduzione
+ **Equilibra la granularità**: suddividi le operazioni complesse in fasi mirate, ma evita passaggi troppo piccoli che aumentano il sovraccarico dei checkpoint
+ **Operazioni relative al gruppo: le** operazioni che dovrebbero avere successo o fallire rientrano nella stessa fase

## Usa le operazioni di attesa in modo efficiente
<a name="durable-wait-operations"></a>

Le operazioni di attesa sospendono l'esecuzione senza consumare risorse o incorrere in costi. Usali invece di mantenere Lambda in funzione.

**Attese basate sul tempo: utilizzale** `context.wait()` per i ritardi anziché o. `setTimeout` `sleep`

**Callback esterni:** da utilizzare in attesa di `context.waitForCallback()` sistemi esterni. Imposta sempre dei timeout per evitare attese indefinite.

**Sondaggi:** utilizza `context.waitForCondition()` il backoff esponenziale per interrogare i servizi esterni senza sovraccaricarli.

------
#### [ 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)
)
```

------

## Ulteriori considerazioni
<a name="durable-additional-considerations"></a>

**Gestione degli errori:** riprova gli errori temporanei come i timeout di rete e i limiti di velocità. Non riprovare con errori permanenti come errori di input o di autenticazione non validi. Configura le strategie di riprova con un numero massimo di tentativi e tassi di backoff appropriati. Per esempi dettagliati, consulta [Gestione degli errori e nuovi tentativi](durable-execution-sdk-retries.md).

**Prestazioni:** riduci al minimo le dimensioni del checkpoint memorizzando i riferimenti anziché i payload completi. Usa `context.parallel()` ed `context.map()` esegui operazioni indipendenti contemporaneamente. Operazioni relative ai batch per ridurre il sovraccarico dei checkpoint.

**Controllo delle versioni:** richiama funzioni con numeri di versione o alias per associare le esecuzioni a versioni di codice specifiche. Assicurati che le nuove versioni del codice siano in grado di gestire lo stato delle versioni precedenti. Non rinominate i passaggi né modificate il loro comportamento in modo da interrompere la riproduzione.

**Serializzazione:** utilizza tipi compatibili con JSON per gli input e i risultati delle operazioni. Converti le date in stringhe ISO e gli oggetti personalizzati in oggetti semplici prima di passarli a operazioni durevoli.

**Monitoraggio:** abilita la registrazione strutturata con nomi di esecuzione IDs e passaggi. Imposta CloudWatch allarmi per i tassi di errore e la durata dell'esecuzione. Usa il tracciamento per identificare i punti deboli. [Per una guida dettagliata, consulta Monitoraggio e debug.](durable-monitoring.md)

**Test: verifica** il percorso felice, la gestione degli errori e il comportamento di riproduzione. Prova gli scenari di timeout per callback e attese. Utilizza i test locali per ridurre i tempi di iterazione. Per una guida dettagliata, consulta [Testare le funzioni durevoli](durable-testing.md).

**Errori comuni da evitare:** non `context.step()` annidate le chiamate, ma utilizzate invece contesti secondari. Suddividi le operazioni non deterministiche in fasi. Imposta sempre i timeout per i callback. Bilancia la granularità dei passaggi con il sovraccarico dei checkpoint. Memorizza i riferimenti anziché gli oggetti di grandi dimensioni nello stato.

## Risorse aggiuntive
<a name="durable-additional-resources"></a>
+ [Documentazione Python SDK](https://github.com/aws/aws-durable-execution-sdk-python/tree/main/docs): riferimento completo alle API, modelli di test ed esempi avanzati
+ [TypeScript Documentazione SDK](https://github.com/aws/aws-durable-execution-sdk-js/tree/main/docs): riferimento completo alle API, modelli di test ed esempi avanzati