DynamoDB TransactWriteItems in Python (boto3)
Transaktionen sind eine der Stellen, an denen boto3s zwei APIs am härtesten auseinandergehen: transact_write_items gibt es nur auf dem Low-Level-Client, der Komfort nativer Python-Typen von Table fällt hier also weg. Und wenn die Transaktion scheitert, steht das, was du brauchst, in einer Ecke der Exception, in die der meiste boto3-Code nie schaut. (Was dir eine Transaktion bringt ist in jedem SDK gleich.)
Code
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']Erklärung
TransactItems— eine Liste ausPut-,Update-,Delete- undConditionCheck-Dicts, jeder Wert in DynamoDB JSON, ohne Ausnahme. Das ist der eine boto3-Aufruf, bei dem die typisierte Form nicht optional ist, und deshalb gibt es den Abschnitt am Ende dieser Seite. Die Obergrenzen stehen auf der CLI-Seite.CancellationReasonssteht nicht innerhalb vonError. botocore hebt modellierte Fehlerfelder an den Anfang des Response-Dicts, die gefangene Exception trägt also eine.responsemit den KeysCancellationReasons,Error,MessageundResponseMetadatanebeneinander. Wer es untere.response["Error"]sucht, findet nichts, unde.response["Error"]enthält nur Code und Meldung der Zusammenfassung.- Kein
"Message"auf denNone-Einträgen — der Grund einer erfolgreichen Aktion ist das Ein-Key-Dict{"Code": "None"}, das naheliegende[r["Message"] for r in reasons]wirft also genau bei den Aktionen, die funktioniert haben, einenKeyError: 'Message'. Nimmr.get("Message"). - Eine generierte Exception-Klasse — botocore baut
client.exceptions.TransactionCanceledExceptionzur Laufzeit aus dem Servicemodell, deshalb hängt sie an der Client-Instanz und deshalb kannst du sie nicht perfrom botocore.exceptions import ...holen. In einer Hilfsfunktion, die den Client nicht im Scope hat, fangebotocore.exceptions.ClientErrorund verzweige übere.response["Error"]["Code"]; die generierte Klasse ist eine Unterklasse davon. - Strukturelle Fehler kommen nicht als Stornierungen an, die
except-Klausel im Snippet sieht sie also nie. Zwei Aktionen auf dasselbe Item lösen einen nacktenClientErrormit dem CodeValidationExceptionaus, dessene.responsekeinenCancellationReasons-Key hat, weil die Transaktion abgelehnt wurde, bevor irgendeine Aktion lief. FangeClientErroran der äußeren Kante, wenn du diese Fälle mit demselben Kontext protokollieren willst. ReturnValuesOnConditionCheckFailure: "ALL_OLD"an einer Aktion legt das unterlegene Item unter einemItem-Key in den Grund dieser Aktion, in DynamoDB JSON — das spart dir das nachgelagerteget_item, nachdem du das Rennen bereits verloren hast.- boto3 füllt
ClientRequestTokenfür dich. Auf der Leitung mitgeschnitten, verließen zwei identischetransact_write_items-Aufrufe die Maschine mit zwei verschiedenen UUIDs — der Token deckt also einen einzelnen Aufruf ab und nicht deine eigene Catch-and-Retry-Schleife. Übergib selbst einen stabilen Token, wenn das Retry den Prozess überleben kann. - Wiederhole bei
TransactionConflict, nie beiConditionalCheckFailed— der erste sagt, dass jemand anders das Item einen Moment lang hielt; der zweite sagt, dass deine Vorbedingung falsch ist und es beim nächsten Mal auch sein wird. Das sind die einzigen zwei Codes, die die meisten Handler auseinanderhalten müssen, und der vollständige Satz ist auf der Seite zur TransactionCanceledException entschlüsselt. - Kosten — ein transaktionaler Write kostet etwa doppelt so viel wie derselbe Write außerhalb einer Transaktion, auf der CLI-Seite gemessen. Wenn du Atomarität nur auf einem einzelnen Item brauchst, bekommst du sie mit einem bedingten Write zum halben Preis.
Eine Resource-API-Variante davon gibt es nicht
boto3.resource("dynamodb").Table(...) hat kein transact_write_items-Attribut; nur resource.meta.client hat es. Eine Codebasis, die sich auf Table und native Python-Typen festgelegt hat, muss für ihre Transaktionen also auf typisiertes DynamoDB JSON zurückfallen oder von Hand mit boto3.dynamodb.types.TypeSerializer serialisieren:
from boto3.dynamodb.types import TypeSerializer
serialize = TypeSerializer().serialize
values = {k: serialize(v) for k, v in {":one": 1, ":zero": 0}.items()}TypeSerializer wendet dieselben Regeln an wie die Resource-API, lehnt also float ab und erwartet für alles Gebrochene decimal.Decimal. Der DynamoDB-JSON-Konverter macht dieselbe Umwandlung im Browser, wenn du nur ein Literal in ein Skript einfügen willst. Um die Items, die eine Transaktion anfasst, zu bearbeiten, ohne eine der beiden Formen von Hand zu schreiben, lade DynoTable herunter.
Verwandte Beispiele
- DynamoDB TransactWriteItems in Node.js — dieselbe Transaktion mit AWS SDK v3.
- DynamoDB TransactWriteItems mit der AWS CLI — dieselbe Transaktion aus der Shell.
- Bedingter DynamoDB-Write in Python — Atomarität auf einem Item ohne die doppelten Kosten.
- DynamoDB-Transaktionen — Isolation, Idempotenz und wann Transaktionen sich lohnen.
- DynamoDB TransactionCanceledException — jeder Cancellation-Reason-Code, entschlüsselt.
- "Too many actions in a TransactWriteItems call" — die Limits von 100 Aktionen und 4 MB pro Transaktion.
- "Transaction request cannot include multiple operations on one item" — eine Aktion pro Item, pro Transaktion.
Referenzen
- 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
Zuletzt verifiziert am 2026-07-28 gegen die oben verlinkte offizielle AWS-Dokumentation.