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 dizionari Put, Update, Delete e ConditionCheck, 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.
  • CancellationReasons non sta dentro Error. botocore solleva i campi di errore modellati in cima al dizionario della risposta, quindi l'eccezione intercettata porta un e.response con le chiavi CancellationReasons, Error, Message e ResponseMetadata una accanto all'altra. Cercarlo sotto e.response["Error"] non trova nulla, e e.response["Error"] contiene solo il codice e il messaggio di riepilogo.
  • Nessun "Message" sulle voci None — il motivo di un'azione riuscita è il dizionario a chiave singola {"Code": "None"}, quindi il naturale [r["Message"] for r in reasons] solleva KeyError: 'Message' esattamente sulle azioni che hanno funzionato. Usa r.get("Message").
  • Una classe di eccezione generata — botocore costruisce client.exceptions.TransactionCanceledException dal modello di servizio a runtime, ed è per questo che pende dall'istanza del client e per cui non puoi fare from botocore.exceptions import .... In un helper che non ha il client nello scope, intercetta botocore.exceptions.ClientError e ramifica su e.response["Error"]["Code"]; la classe generata ne è una sottoclasse.
  • Gli errori strutturali non arrivano come annullamenti, quindi la clausola except dello snippet non li vede mai. Due azioni puntate allo stesso Item sollevano un semplice ClientError il cui codice è ValidationException e il cui e.response non ha alcuna chiave CancellationReasons, dato che la transazione è stata rifiutata prima che qualsiasi azione partisse. Intercetta ClientError al bordo esterno se vuoi che vengano loggati con lo stesso contesto.
  • ReturnValuesOnConditionCheckFailure: "ALL_OLD" su un'azione mette l'Item perdente sotto una chiave Item nel motivo di quell'azione, in JSON DynamoDB, risparmiandoti il get_item di controllo dopo che hai già perso la corsa.
  • boto3 riempie ClientRequestToken al posto tuo. Catturate sul wire, due chiamate identiche a transact_write_items sono 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 su ConditionalCheckFailed — 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

Riferimenti

Ultima verifica 2026-07-28 rispetto alla documentazione ufficiale AWS collegata sopra.

Lavora con DynamoDB senza la Console

Un client desktop veloce per DynamoDB che esegue il vero SQL che DynamoDB non può — JOINs, GROUP BY, aggregazioni — con modifica visuale e un agente AI sulle tue chiavi Bedrock.

Prova gratuita di 30 giorni, senza carta di credito — poi il piano Free senza limiti di tempo.