DynamoDB TransactWriteItems in Python (boto3)
Le transazioni sono uno dei punti in cui le due API di boto3 divergono di più: transact_write_items esiste solo sul client di basso livello, quindi la comodità dei tipi Python nativi che ottieni da Table qui non è disponibile. E quando la transazione fallisce, quello che ti serve sta in un angolo dell'eccezione che quasi nessun codice boto3 guarda mai. (Cosa ti dà una transazione è uguale in ogni SDK.)
Codice
import boto3
client = boto3.client("dynamodb")
# Move one award between two songs — atomically. If the first song has no
# award to give, NEITHER update happens.
try:
client.transact_write_items(
TransactItems=[
{
"Update": {
"TableName": "Music",
"Key": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
"UpdateExpression": "SET #upd0 = #upd0 - :one",
"ConditionExpression": "#upd0 >= :one",
"ExpressionAttributeNames": {"#upd0": "Awards"},
"ExpressionAttributeValues": {":one": {"N": "1"}},
}
},
{
"Update": {
"TableName": "Music",
"Key": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "A Mis Abuelos"}},
"UpdateExpression": "SET #upd0 = if_not_exists(#upd0, :zero) + :one",
"ExpressionAttributeNames": {"#upd0": "Awards"},
"ExpressionAttributeValues": {":one": {"N": "1"}, ":zero": {"N": "0"}},
}
},
]
)
print("Transaction committed")
except client.exceptions.TransactionCanceledException as e:
# One reason per action, in TransactItems order. Code "None" means that
# action was fine — some OTHER action sank the transaction.
codes = [reason["Code"] for reason in e.response["CancellationReasons"]]
print(f"Transaction canceled: {codes}") # e.g. ['ConditionalCheckFailed', 'None']Spiegazione
TransactItems— una lista di dizionariPut,Update,DeleteeConditionCheck, ogni valore in JSON DynamoDB, senza eccezioni. È l'unica chiamata boto3 in cui la forma tipizzata non è opzionale, ed è il motivo per cui esiste la sezione in fondo a questa pagina. I limiti sono nella pagina della CLI.CancellationReasonsnon sta dentroError. botocore solleva i campi di errore modellati in cima al dizionario della risposta, quindi l'eccezione intercettata porta une.responsecon le chiaviCancellationReasons,Error,MessageeResponseMetadatauna accanto all'altra. Cercarlo sottoe.response["Error"]non trova nulla, ee.response["Error"]contiene solo il codice e il messaggio di riepilogo.- Nessun
"Message"sulle vociNone— il motivo di un'azione riuscita è il dizionario a chiave singola{"Code": "None"}, quindi il naturale[r["Message"] for r in reasons]sollevaKeyError: 'Message'esattamente sulle azioni che hanno funzionato. Usar.get("Message"). - Una classe di eccezione generata — botocore costruisce
client.exceptions.TransactionCanceledExceptiondal modello di servizio a runtime, ed è per questo che pende dall'istanza del client e per cui non puoi farefrom botocore.exceptions import .... In un helper che non ha il client nello scope, intercettabotocore.exceptions.ClientErrore ramifica sue.response["Error"]["Code"]; la classe generata ne è una sottoclasse. - Gli errori strutturali non arrivano come annullamenti, quindi la clausola
exceptdello snippet non li vede mai. Due azioni puntate allo stesso Item sollevano un sempliceClientErroril cui codice èValidationExceptione il cuie.responsenon ha alcuna chiaveCancellationReasons, dato che la transazione è stata rifiutata prima che qualsiasi azione partisse. IntercettaClientErroral bordo esterno se vuoi che vengano loggati con lo stesso contesto. ReturnValuesOnConditionCheckFailure: "ALL_OLD"su un'azione mette l'Item perdente sotto una chiaveItemnel motivo di quell'azione, in JSON DynamoDB, risparmiandoti ilget_itemdi controllo dopo che hai già perso la corsa.- boto3 riempie
ClientRequestTokenal posto tuo. Catturate sul wire, due chiamate identiche atransact_write_itemssono partite con due UUID diversi, quindi il token copre una singola chiamata e non il tuo loop di catch-and-retry. Passane uno stabile tu stesso se il retry può sopravvivere al processo. - Riprova su
TransactionConflict, mai suConditionalCheckFailed— il primo dice che qualcun altro ha tenuto l'Item per un istante; il secondo dice che la tua precondizione è falsa e lo sarà anche la prossima volta. Sono gli unici due codici che la maggior parte degli handler ha bisogno di distinguere, e l'insieme completo è decodificato nella pagina di TransactionCanceledException. - Costo — una scrittura transazionale fattura circa il doppio di quanto costa la stessa scrittura fuori da una transazione, misurato nella pagina della CLI. Se ti serve l'atomicità solo su un singolo Item, una scrittura condizionale te la compra a metà prezzo.
Non esiste una versione con l'API resource
boto3.resource("dynamodb").Table(...) non ha alcun attributo transact_write_items; ce l'ha solo resource.meta.client. Quindi un codebase che si è assestato su Table e sui tipi Python nativi deve ripiegare sul JSON DynamoDB tipizzato per le sue transazioni, oppure serializzare a mano con boto3.dynamodb.types.TypeSerializer:
from boto3.dynamodb.types import TypeSerializer
serialize = TypeSerializer().serialize
values = {k: serialize(v) for k, v in {":one": 1, ":zero": 0}.items()}TypeSerializer applica le stesse regole dell'API resource, il che significa che rifiuta float e si aspetta decimal.Decimal per qualsiasi valore frazionario. Il convertitore JSON DynamoDB fa la stessa conversione nel browser quando ti serve solo incollare un letterale in uno script. Per modificare gli Item che una transazione tocca senza scrivere a mano nessuna delle due forme, scarica DynoTable.
Esempi correlati
- DynamoDB TransactWriteItems in Node.js — la stessa transazione con AWS SDK v3.
- DynamoDB TransactWriteItems con la AWS CLI — la stessa transazione dalla shell.
- Scrittura condizionale DynamoDB in Python — atomicità su un singolo Item senza il costo 2×.
- Transazioni DynamoDB — isolamento, idempotenza e quando le transazioni valgono la pena.
- DynamoDB TransactionCanceledException — ogni codice di motivo di annullamento, decodificato.
- "Too many actions in a TransactWriteItems call" — i limiti di 100 azioni e 4 MB per transazione.
- "Transaction request cannot include multiple operations on one item" — un'azione per Item, per transazione.
Riferimenti
- TransactWriteItems — Amazon DynamoDB API Reference
- DynamoDB.Client.transact_write_items — Boto3 documentation
- Amazon DynamoDB transactions: how it works — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Ultima verifica 2026-07-28 rispetto alla documentazione ufficiale AWS collegata sopra.