Avanzato6 min di lettura

Transazioni DynamoDB

Una transazione DynamoDB raggruppa più scritture in un'unica operazione tutto-o-niente: o ogni azione fa il commit, o nessuna lo fa. È così che eviti che due Item correlati divergano quando una scrittura fallisce a metà.

Nello scenario dell'audit-log, ogni evento che aggiungi in coda dovrebbe anche incrementare un eventCount per tenant. Se l'evento atterra ma il contatore no — o viceversa — il log e il conteggio non concorderanno mai più. Una transazione rende questo impossibile.

DynamoDB supporta le transazioni?

Sì. DynamoDB supporta le transazioni ACID tramite TransactWriteItems e TransactGetItems, che raggruppano fino a 100 azioni in un'unica operazione tutto-o-niente su una o più tabelle. O tutte le scritture fanno il commit, o nessuna lo fa, così gli Item correlati non possono divergere. Le scritture transazionali consumano il doppio della capacità delle scritture normali, e una condizione fallita o un conflitto annulla l'intera richiesta.

  • TransactWriteItems raggruppa fino a 100 azioni di scrittura su una o più tabelle, tutto-o-niente. La dimensione aggregata degli Item non può superare i 4 MB.
  • Le azioni sono Put, Update, Delete e ConditionCheck. Un ConditionCheck asserisce qualcosa su un Item che non stai scrivendo.
  • Costa il doppio. Una scrittura transazionale consuma il doppio della capacità di una scrittura normale — DynamoDB prepara e poi fa il commit.
  • Conflitti e condizioni fallite annullano l'intera operazione con una TransactionCanceledException; non resta nulla a metà.

Il problema: due scritture che devono concordare

Vuoi che ogni nuovo evento di audit incrementi anche il conteggio corrente del tenant. Fatto come due chiamate separate, qualunque fallimento tra le due corrompe i tuoi dati:

  1. PutItem del nuovo Item EVENT#… — riesce.
  2. UpdateItem per ADD eventCount 1 — va in timeout.

Ora il log ha una riga in più di quante ne dichiari il contatore. Ritentare il passo 2 alla cieca rischia un doppio conteggio; non ritentarlo li lascia incoerenti. Non c'è recupero sicuro perché le due scritture non sono mai state collegate.

Venendo da SQL, le avvolgeresti entrambe in BEGIN … COMMIT. La risposta di DynamoDB è una singola richiesta transazionale che porta entrambe le scritture insieme.

Come funziona TransactWriteItems

Secondo la AWS Developer Guide, TransactWriteItems "raggruppa fino a 100 azioni di scrittura in un'unica operazione tutto-o-niente" su un massimo di 100 Item distinti, e "la dimensione aggregata degli Item nella transazione non può superare i 4 MB." Le azioni si completano atomicamente — o riescono tutte o nessuna.

Puoi mescolare quattro tipi di azione in una transazione:

  • Put — crea o sostituisce un Item.
  • Update — modifica attributi (incluso ADD per il nostro contatore).
  • Delete — rimuove un Item per chiave.
  • ConditionCheck — asserisce una condizione su un Item che non stai altrimenti scrivendo (es. "questo tenant è ancora attivo").

Altre due regole mordono nella pratica. Primo, le transazioni consumano il doppio della capacità delle scritture non transazionali equivalenti — DynamoDB esegue una fase di prepare e una di commit. Secondo, non puoi puntare allo stesso Item due volte in una transazione, e le transazioni non possono essere eseguite contro gli Index.

"DynamoDB"App"DynamoDB"Appprepare both, checkconditions"TransactWriteItems [Put EVENT,Update counter]""both commit, orTransactionCanceledException"

Un esempio pratico: append + conteggio, atomicamente

Torniamo all'audit-log. Aggiungere in coda un evento per il tenant acme e incrementare il suo contatore è una transazione con due azioni:

actionitemeffect
PutTENANT#acmeEVENT#2026-06-24T09:14Z#a1write the new audit row
UpdateTENANT#acmeCOUNTERADD eventCount 1

Se la condizione di una delle due azioni fallisce — diciamo un ConditionCheck che il tenant non sia sospeso — l'intera richiesta viene annullata con una TransactionCanceledException e nessuna delle due scritture avviene. Il log e il contatore non possono mai discordare.

La ConditionExpression su ciascuna azione è la leva. Per asserire che la riga dell'evento non esista già (così un retry non possa duplicarla) e che il tenant sia attivo, componi condizioni come attribute_not_exists(SK) sul Put e status = :active come ConditionCheck.

Costruisci e copia quelle condition expression tipizzate nel DynamoDB Expression Builder invece di assemblare a mano ExpressionAttributeNames e placeholder :val — la modalità di scrittura condizionale emette esattamente la forma che TransactWriteItems vuole.

Per retry sicuri su una connessione instabile, allega un client token: una TransactWriteItems ripetuta con lo stesso token entro 10 minuti restituisce successo senza riapplicare le scritture (idempotency).

Fallo in DynoTable

DynoTable usa le transazioni sotto il cofano per le proprie scritture: quando metti in staging diverse modifiche a Item e ne fai il commit, le invia come TransactWriteItems con condition expression di optimistic-locking, così il tuo batch di modifiche è tutto-o-niente — non applichi mai a metà una modifica multi-Item.

Questo significa che puoi modificare la riga dell'evento e il contatore nello stesso batch in staging, rivedere il diff e committarli entrambi atomicamente senza scrivere alcun codice SDK.

Staging del nuovo evento di audit e della modifica al contatore del tenant in DynoTable, poi commit di entrambi come un'unica transazione tutto-o-niente.
Staging del nuovo evento di audit e della modifica al contatore del tenant in DynoTable, poi commit di entrambi come un'unica transazione tutto-o-niente.

Trappole e prossimi passi

  • Metti a budget la capacità doppia. Una scrittura transazionale fattura il doppio dei WCU di una scrittura semplice — va bene per l'occasionale coppia critica per la coerenza, costoso se avvolgi ogni singola scrittura in una transazione. Usala dove l'atomicità conta davvero.
  • Gestisci TransactionCanceledException esplicitamente. Viene restituita per una condizione fallita o un conflitto con un'altra transazione in volo sugli stessi Item. Le cancellation reason ti dicono quale azione è fallita — ispezionale, non ritentare alla cieca.
  • I record di stream non sono transaction-aware. Le modifiche di una transazione si propagano a Streams gradualmente e possono intervallarsi con altre; i consumer non possono assumere atomicità o ordinamento — vedi DynamoDB Streams.
  • Non per contatori ad alto throughput. Un singolo contatore caldo sotto pesante carico transazionale concorrente verrà rallentato; per quello, guarda gli atomic counter o lo sharding del contatore.

Le transazioni sono lo strumento per "queste scritture devono concordare." Una volta che gli eventi atterrano in modo coerente, la prossima preoccupazione è reagire ad essi — ed è DynamoDB Streams.

Scarica DynoTable per mettere in staging modifiche multi-Item e committarle come un'unica transazione contro la tua tabella.

Aggiornato