

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 la gestione degli aggiornamenti simultanei in DynamoDB
<a name="BestPractices_ImplementingVersionControl"></a>

Nei sistemi distribuiti, più processi o utenti possono tentare di modificare gli stessi dati contemporaneamente. Senza il controllo della concorrenza, queste scritture simultanee possono portare alla perdita di aggiornamenti, alla mancata coerenza dei dati o alle condizioni di gara. DynamoDB offre diversi meccanismi per aiutarti a gestire l'accesso simultaneo e mantenere l'integrità dei dati.

**Nota**  
`UpdateItem`Le singole operazioni di scrittura, ad esempio Atomic, funzionano sempre sulla versione più recente dell'elemento, indipendentemente dalla concorrenza. Le strategie di blocco sono necessarie quando l'applicazione deve leggere un elemento e quindi riscriverlo in base al valore di lettura (un read-modify-write ciclo), poiché un altro processo potrebbe modificare l'elemento tra la lettura e la scrittura.

Esistono due strategie principali per la gestione degli aggiornamenti simultanei:
+ **Blocco ottimistico**: presuppone che i conflitti siano rari. Consente l'accesso simultaneo e rileva i conflitti in fase di scrittura utilizzando scritture condizionali. Se viene rilevato un conflitto, la scrittura fallisce e l'applicazione può riprovare.
+ **Blocco pessimistico**: presuppone la probabilità di conflitti. Impedisce l'accesso simultaneo acquisendo l'accesso esclusivo a una risorsa prima di modificarla. Gli altri processi devono attendere il rilascio del blocco.

La tabella seguente riassume gli approcci disponibili in DynamoDB:


| Approccio | Meccanismo | Ideale per | 
| --- | --- | --- | 
| Blocco ottimistico | Attributo di versione \$1 scritture condizionali | Tentativi a basso livello di contesa e poco costosi | 
| Blocco pessimistico (transazioni) | TransactWriteItems | Atomicità multielemento, contesa moderata | 
| Blocco pessimistico (lock client) | Tavolo di chiusura dedicato con leasing e battito cardiaco | Flussi di lavoro di lunga durata, coordinamento distribuito | 

# Blocco ottimistico con il numero di versione
<a name="BestPractices_OptimisticLocking"></a>

Il blocco ottimistico è una strategia che rileva i conflitti in fase di scrittura anziché prevenirli. Ogni elemento include un attributo di versione che aumenta con ogni aggiornamento. Quando si aggiorna un elemento, si include un'[espressione di condizione](Expressions.ConditionExpressions.md) che verifica se il numero di versione corrisponde al valore letto per l'ultima volta dall'applicazione. Se nel frattempo un altro processo ha modificato l'elemento, la condizione fallisce e DynamoDB restituisce un. `ConditionalCheckFailedException`

## Quando utilizzare il blocco ottimistico
<a name="BestPractices_OptimisticLocking_WhenToUse"></a>

Il blocco ottimistico è la soluzione ideale quando:
+ È possibile che più utenti o processi aggiornino lo stesso elemento, ma i conflitti sono rari.
+ Riprovare una scrittura non riuscita è poco costoso per l'applicazione.
+ Desiderate evitare il sovraccarico e la complessità della gestione dei blocchi distribuiti.

Gli esempi più comuni includono gli aggiornamenti dell'inventario per l'e-commerce, le piattaforme di modifica collaborativa e i registri delle transazioni finanziarie.

## Compromessi
<a name="BestPractices_OptimisticLocking_Tradeoffs"></a>

**Riprova le spese generali in caso di alta contesa**  
In ambienti caratterizzati da un elevato tasso di simultaneità, aumenta la probabilità che si verifichino conflitti, con conseguenti potenziali ripetizioni dei tentativi e costi di scrittura.

**Complessità dell’implementazione**  
L'aggiunta del controllo della versione agli articoli e la gestione dei controlli condizionali aggiungono complessità alla logica dell'applicazione. L' AWS SDK for Java v2 Enhanced Client fornisce supporto integrato tramite [https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE)l'annotazione, che gestisce automaticamente i numeri di versione per te.

## Progettazione dei modelli
<a name="BestPractices_OptimisticLocking_PatternDesign"></a>

Includi un attributo di versione in ogni elemento. Ecco un semplice schema di progettazione:
+ Chiave di partizione: un identificatore univoco per ogni elemento (ad esempio,`ItemId`).
+ Attributi:
  + `ItemId`: l’identificatore univoco per l’elemento.
  + `Version`: un numero intero che rappresenta il numero di versione dell’elemento.
  + `QuantityLeft`: l’inventario rimanente dell’elemento.

Quando un elemento viene creato per la prima volta, l’attributo `Version` è impostato su 1. A ogni aggiornamento, il numero di versione aumenta di 1.


| ItemID (chiave di partizione) | Versione | QuantityLeft | 
| --- | --- | --- | 
| Banane | 1 | 10 | 
| Mele | 1 | 5 | 
| Arance | 1 | 7 | 

## Implementazione
<a name="BestPractices_OptimisticLocking_Implementation"></a>

Per implementare il blocco ottimistico, procedi nel seguente modo:

1. La versione corrente dell’elemento.

   ```
   def get_item(item_id):
       response = table.get_item(Key={'ItemID': item_id})
       return response['Item']
   
   item = get_item('Bananas')
   current_version = item['Version']
   ```

1. Aggiorna l'elemento utilizzando un'espressione di condizione che controlla il numero di versione.

   ```
   def update_item(item_id, qty_bought, current_version):
       try:
           response = table.update_item(
               Key={'ItemID': item_id},
               UpdateExpression="SET QuantityLeft = QuantityLeft - :qty, Version = :new_v",
               ConditionExpression="Version = :expected_v",
               ExpressionAttributeValues={
                   ':qty': qty_bought,
                   ':new_v': current_version + 1,
                   ':expected_v': current_version
               },
               ReturnValues="UPDATED_NEW"
           )
           return response
       except ClientError as e:
           if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
               print("Version conflict: another process updated this item.")
           raise
   ```

1. Gestisci i conflitti riprovando con una nuova lettura.

   Ogni nuovo tentativo richiede una lettura aggiuntiva, quindi limita il numero totale di tentativi.

   ```
   def update_with_retry(item_id, qty_bought, max_retries=3):
       for attempt in range(max_retries):
           item = get_item(item_id)
           try:
               return update_item(item_id, qty_bought, item['Version'])
           except ClientError as e:
               if e.response['Error']['Code'] != 'ConditionalCheckFailedException':
                   raise
               print(f"Retry {attempt + 1}/{max_retries}")
       raise Exception("Update failed after maximum retries.")
   ```

Per le applicazioni Java, l' AWS SDK for Java v2 Enhanced Client offre un supporto integrato per il blocco ottimistico tramite [https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE)l'annotazione, che gestisce automaticamente i numeri di versione per te.

Per ulteriori informazioni sulle espressioni condizionali, vedere. [Esempio di CLI di espressione condizionale in DynamoDB](Expressions.ConditionExpressions.md)

# Blocco pessimistico con transazioni DynamoDB
<a name="BestPractices_PessimisticLocking"></a>

Le transazioni [DynamoDB](transactions.md) forniscono all-or-nothing un approccio alle operazioni raggruppate. Quando si utilizza`TransactWriteItems`, DynamoDB monitora tutti gli elementi della transazione. Se un elemento viene modificato da un'altra operazione durante la transazione, l'intera transazione viene annullata e DynamoDB restituisce un. `TransactionCanceledException` Questo comportamento fornisce una forma di controllo pessimistico della concorrenza, poiché le modifiche concomitanti in conflitto vengono prevenute anziché rilevate a posteriori.

## Quando utilizzare le transazioni per il blocco
<a name="BestPractices_PessimisticLocking_WhenToUse"></a>

Le transazioni sono adatte quando:
+ È necessario aggiornare più elementi in modo atomico, all'interno della stessa tabella o tra tabelle diverse.
+ La tua logica aziendale richiede la all-or-nothing semantica: o tutte le modifiche hanno esito positivo o nessuna viene applicata.

Gli esempi più comuni includono il trasferimento di fondi tra conti, l'effettuazione di ordini che aggiornano sia l'inventario che le tabelle degli ordini e lo scambio di articoli tra giocatori durante una partita.

## Compromessi
<a name="BestPractices_PessimisticLocking_Tradeoffs"></a>

**Costo di scrittura più elevato**  
Per articoli fino a 1 KB, le transazioni consumano 2 unità WCUs per articolo (una per la preparazione e l'altra per il completamento), rispetto a 1 WCU per una scrittura standard.

**Limite di articoli**  
Una singola transazione può includere fino a 100 azioni su una o più tabelle.

**Sensibilità ai conflitti**  
Se un elemento della transazione viene modificato da un'altra operazione, l'intera transazione ha esito negativo. In scenari ad alta tensione, ciò può portare a cancellazioni frequenti.

## Implementazione
<a name="BestPractices_PessimisticLocking_Implementation"></a>

L'esempio seguente utilizza `TransactWriteItems` il trasferimento atomico dell'inventario tra due articoli. Se un altro processo modifica uno degli articoli durante la transazione, l'intera operazione viene annullata.

```
import boto3

client = boto3.client('dynamodb')

def transfer_inventory(source_id, target_id, quantity):
    try:
        client.transact_write_items(
            TransactItems=[
                {
                    'Update': {
                        'TableName': 'Inventory',
                        'Key': {'ItemID': {'S': source_id}},
                        'UpdateExpression': 'SET QuantityLeft = QuantityLeft - :qty',
                        'ConditionExpression': 'QuantityLeft >= :qty',
                        'ExpressionAttributeValues': {
                            ':qty': {'N': str(quantity)}
                        }
                    }
                },
                {
                    'Update': {
                        'TableName': 'Inventory',
                        'Key': {'ItemID': {'S': target_id}},
                        'UpdateExpression': 'SET QuantityLeft = QuantityLeft + :qty',
                        'ExpressionAttributeValues': {
                            ':qty': {'N': str(quantity)}
                        }
                    }
                }
            ]
        )
        return True
    except client.exceptions.TransactionCanceledException as e:
        print(f"Transaction canceled: {e}")
        return False
```

In questo esempio, l'espressione condition verifica che esista un inventario sufficiente, ma non è necessario alcun attributo di versione. DynamoDB annulla automaticamente la transazione se un elemento della transazione viene modificato da un'altra operazione tra le fasi di preparazione e di commit. Questo è ciò che fornisce il pessimistico controllo della concorrenza: le modifiche concomitanti in conflitto sono impedite dalla transazione stessa.

**Nota**  
È possibile combinare le transazioni con il blocco ottimistico aggiungendo controlli di versione come espressioni di condizione aggiuntive. Ciò fornisce un ulteriore livello di protezione, ma non è necessario affinché la transazione rilevi i conflitti.

Per ulteriori informazioni, consulta [Gestione di flussi di lavoro complessi con transazioni DynamoDB](transactions.md).

# Blocco distribuito con il DynamoDB Lock Client
<a name="BestPractices_DistributedLocking"></a>

Per le applicazioni che richiedono la lock-acquire-release semantica tradizionale, DynamoDB Lock Client è una libreria open source che implementa il blocco distribuito utilizzando una tabella DynamoDB come archivio di blocco. Questo approccio è utile quando è necessario coordinare l'accesso a una risorsa esterna (come un oggetto S3 o una configurazione condivisa) tra più istanze dell'applicazione.

[Il lock client è disponibile come libreria Java open source.](https://github.com/awslabs/amazon-dynamodb-lock-client)

## Come funziona
<a name="BestPractices_DistributedLocking_HowItWorks"></a>

Il lock client utilizza una tabella DynamoDB dedicata per tracciare i blocchi. Ogni blocco è rappresentato come un elemento con i seguenti attributi chiave:
+ Una chiave di partizione che identifica la risorsa da bloccare.
+ Una durata del leasing che specifica per quanto tempo è valido il blocco. Se il titolare del lucchetto si blocca o non risponde, il lucchetto scade automaticamente dopo la durata del leasing.
+ Un battito cardiaco che il titolare della serratura invia periodicamente per prolungare il contratto di locazione. In questo modo si evita che il lucchetto scada mentre il titolare è ancora in fase di elaborazione attiva.

Il lock client utilizza scritture condizionali per garantire che un solo processo possa acquisire un blocco alla volta. Se è già presente un blocco, il chiamante può scegliere di attendere e riprovare o fallire immediatamente.

## Quando usare il lock client
<a name="BestPractices_DistributedLocking_WhenToUse"></a>

Il lock client è adatto quando:
+ È necessario coordinare l'accesso a una risorsa condivisa tra più istanze di applicazioni o microservizi.
+ La sezione critica richiede tempi lunghi (da secondi a minuti) e riprovare l'intera operazione in caso di conflitto sarebbe costoso.
+ È necessaria la scadenza automatica del blocco per gestire con garbo gli errori del processo.

Gli esempi più comuni includono l'orchestrazione di flussi di lavoro distribuiti, il coordinamento dei cron job su più istanze e la gestione dell'accesso a risorse esterne condivise.

## Compromessi
<a name="BestPractices_DistributedLocking_Tradeoffs"></a>

**Infrastruttura aggiuntiva**  
Richiede una tabella DynamoDB dedicata per la gestione dei blocchi, con capacità di lettura e scrittura aggiuntiva per le operazioni di blocco e gli heartbeat.

**Dipendenza dall'orologio**  
La scadenza del lucchetto dipende dai timestamp. Una significativa differenza di clock tra i clienti può causare comportamenti imprevisti, in particolare per periodi di leasing di breve durata.

**Rischio di stallo**  
Se l'applicazione acquisisce blocchi su più risorse, è necessario acquisirli in un ordine coerente per evitare situazioni di stallo. La durata del leasing fornisce una rete di sicurezza sbloccando automaticamente le serrature dai possessori che non rispondono.

## Implementazione
<a name="BestPractices_DistributedLocking_Implementation"></a>

L'esempio seguente mostra come utilizzare il DynamoDB Lock Client per acquisire e rilasciare un blocco:

```
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

final DynamoDbClient dynamoDB = DynamoDbClient.builder()
    .region(Region.US_WEST_2)
    .build();

final AmazonDynamoDBLockClient lockClient = new AmazonDynamoDBLockClient(
    AmazonDynamoDBLockClientOptions.builder(dynamoDB, "Locks")
        .withTimeUnit(TimeUnit.SECONDS)
        .withLeaseDuration(10L)
        .withHeartbeatPeriod(3L)
        .withCreateHeartbeatBackgroundThread(true)
        .build());

// Try to acquire a lock on a resource
final Optional<LockItem> lock =
    lockClient.tryAcquireLock(AcquireLockOptions.builder("my-shared-resource").build());

if (lock.isPresent()) {
    try {
        // Perform operations that require exclusive access
        processSharedResource();
    } finally {
        // Always release the lock when done
        lockClient.releaseLock(lock.get());
    }
} else {
    System.out.println("Failed to acquire lock.");
}

lockClient.close();
```

**Importante**  
Rilascia sempre i blocchi in un `finally` blocco per garantire che i blocchi vengano rilasciati anche se la logica di elaborazione genera un'eccezione. I blocchi non rilasciati bloccano altri processi fino alla scadenza del contratto di locazione.

È inoltre possibile implementare un semplice meccanismo di blocco senza la libreria lock client utilizzando direttamente le scritture condizionali. L'esempio seguente utilizza `UpdateItem` un'espressione condizionale per acquisire un blocco e `DeleteItem` rilasciarlo:

```
from datetime import datetime, timedelta
from boto3.dynamodb.conditions import Attr

def acquire_lock(table, resource_name, owner_id, ttl_seconds):
    """Attempt to acquire a lock. Returns True if successful."""
    expiry = (datetime.now() + timedelta(seconds=ttl_seconds)).isoformat()
    now = datetime.now().isoformat()
    try:
        table.update_item(
            Key={'LockID': resource_name},
            UpdateExpression='SET #owner = :owner, #expiry = :expiry',
            ConditionExpression=Attr('LockID').not_exists() | Attr('ExpiresAt').lt(now),
            ExpressionAttributeNames={'#owner': 'OwnerID', '#expiry': 'ExpiresAt'},
            ExpressionAttributeValues={':owner': owner_id, ':expiry': expiry}
        )
        return True
    except table.meta.client.exceptions.ConditionalCheckFailedException:
        return False

def release_lock(table, resource_name, owner_id):
    """Release a lock. Only succeeds if the caller is the lock owner."""
    try:
        table.delete_item(
            Key={'LockID': resource_name},
            ConditionExpression=Attr('OwnerID').eq(owner_id)
        )
        return True
    except table.meta.client.exceptions.ConditionalCheckFailedException:
        return False
```

Questo approccio utilizza un'espressione di condizione per garantire che un blocco possa essere acquisito solo se non esiste o è scaduto e può essere rilasciato solo dal processo che lo ha acquisito. Valuta la possibilità [di abilitare Time to Live (TTL)](TTL.md) sulla tabella di blocco per ripulire automaticamente gli elementi bloccati scaduti.

## Scelta di una strategia di controllo della concorrenza
<a name="BestPractices_ChoosingLockingStrategy"></a>

Utilizza le seguenti linee guida per scegliere l'approccio giusto per il tuo carico di lavoro:

**Utilizza il blocco ottimistico quando**:  
+ I conflitti sono rari.
+ Riprovare una scrittura fallita è poco costoso.
+ Si sta aggiornando un singolo elemento alla volta.

**Utilizza le transazioni** quando:  
+ È necessario aggiornare più elementi in modo atomico.
+ È necessaria la all-or-nothing semantica tra elementi o tabelle.
+ È necessario combinare i controlli delle condizioni con le scritture in un'unica operazione.

**Usa il lock client** quando:  
+ È necessario coordinare l'accesso alle risorse esterne attraverso i processi distribuiti.
+ La sezione critica è di lunga durata e riprovare in caso di conflitto è costoso.
+ È necessaria la scadenza automatica del blocco per gestire gli errori del processo.

**Nota**  
Se utilizzi tabelle globali [DynamoDB, tieni presente che le tabelle globali](GlobalTables.md) utilizzano una strategia di riconciliazione «l'ultimo scrittore vince» per gli aggiornamenti simultanei. Il blocco ottimistico con i numeri di versione non funziona come previsto in tutte le regioni, perché una scrittura in una regione può sovrascrivere una scrittura simultanea in un'altra regione senza un controllo della versione. Progetta l'applicazione in modo da gestire i conflitti a livello di applicazione quando utilizzi tabelle globali.