DynamoDB Conditional Write in Python (boto3)
boto3 ist das eine SDK, in dem ein Conditional Write eine benannte Exception-Klasse zum Abfangen hat — und zugleich das eine, in dem sich das zurückgelieferte Item an einer Stelle versteckt, die du nicht erraten würdest. Die Expression selbst funktioniert überall gleich; DynamoDB Condition Expressions behandelt die Funktionen und das Optimistic-Locking-Muster.
Code
import boto3
client = boto3.client("dynamodb")
# Update the item only if nobody changed it since we read version 7.
try:
client.update_item(
TableName="Music",
Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
UpdateExpression="SET #upd0 = :updValue0, #version = :newVersion",
ConditionExpression="attribute_exists(#cond0) AND #version = :expectedVersion",
ExpressionAttributeNames={"#upd0": "Genre", "#version": "Version", "#cond0": "Artist"},
ExpressionAttributeValues={
":updValue0": {"S": "Latin Jazz"},
":expectedVersion": {"N": "7"},
":newVersion": {"N": "8"},
},
ReturnValuesOnConditionCheckFailure="ALL_OLD",
)
print("Updated to version 8")
except client.exceptions.ConditionalCheckFailedException as e:
# With ReturnValuesOnConditionCheckFailure="ALL_OLD", the current item
# rides back on the exception — no extra read to see what beat you.
print("Lost the race — item is now:", e.response.get("Item"))Erklärung
ConditionalCheckFailedExceptionist eine modellierte Klasse,except client.exceptions.…funktioniert also. Die meisten DynamoDB-Fehler sind es nicht:ValidationExceptionhat überhaupt keine Klasse und muss übere.response["Error"]["Code"]erkannt werden. Die modellierte Klasse erbt weiterhin vonClientError— ein weit gefasstesexcept ClientErrorweiter oben verschluckt sie also, wenn du deine Handler unbedacht anordnest.- Das zurückgelieferte Item ist ein Top-Level-Key von
e.response, nicht vone.response["Error"]. Deshalb liest der Blocke.response.get("Item"). Es ist leicht, unter["Error"]nebenCodeundMessagezu suchen, nichts zu finden und zu schließen, der Parameter habe nicht funktioniert. - Das Item kommt als DynamoDB JSON zurück, auch wenn du native Werte gewohnt sein magst, denn das hier ist der Low-Level-Client.
boto3.dynamodb.types.TypeDeserializerkonvertiert es, wenn du reines Python willst. - Die Resource-API drückt denselben Guard als Objekte aus,
ConditionExpression=Attr("Version").eq(7) & Attr("Artist").exists(), mit nativen Werten und ohne Platzhalter-Maps. Sie wirft dieselbe Exception, die Behandlung unten bleibt also unverändert. - Eine fehlgeschlagene Prüfung wird trotzdem als Write abgerechnet. Der Developer Guide sagt ausdrücklich, dass eine false-Bedingung Write-Kapazität verbraucht, bemessen am größeren von altem und neuem Item — ein unbegrenzter Retry auf einem umkämpften Key kostet also echtes Geld, ohne voranzukommen.
Wo boto3 das zurückgelieferte Item ablegt
Führe den Block gegen eine gespeicherte Version von 9 aus und gib die Response-Keys der Exception aus. DynamoDB Local 3.3.0, boto3 1.43.58:
sorted(e.response.keys()) -> ['Error', 'Item', 'ResponseMetadata']
e.response["Item"] -> {'Artist': {'S': 'Arturo Sandoval'},
'Year': {'N': '1994'},
'Version': {'N': '9'},
'SongTitle': {'S': 'Cubano Chant'},
'AlbumTitle': {'S': 'Danzon'}}Lässt du ReturnValuesOnConditionCheckFailure weg, liefert derselbe Fehlschlag ['Error', 'ResponseMetadata']. Der Item-Key fehlt, und e.response.get("Item") gibt None zurück, statt zu werfen. Das ist die Variante dieses Bugs, die das Code-Review überlebt und anfängt, in Produktion None zu loggen.
Warum jeder Name in der Expression aliasiert ist
Der Block schreibt #version und #cond0 statt Version und Artist, was für zwei gewöhnliche Wörter übertrieben aussieht. Für diese zwei ist es das auch. Version ist kein reserviertes DynamoDB-Wort und besteht die Namensvalidierung auch nackt.
Year ist reserviert, und dieselbe Tabelle hat eins. Prüfe direkt darauf und du bekommst:
ValidationException: Invalid ConditionExpression: Attribute name is a reserved keyword;
reserved keyword: Year573 Wörter stehen auf dieser Liste, darunter Name, Status, Size, Count, Data, Owner, Timestamp und Items. Alles zu aliasieren ist die Art, wie generierter Code es vermeidet, jemals wissen zu müssen, was was ist. Füg deine Attributnamen in den Reserved-Words-Checker ein und er gibt dir die ExpressionAttributeNames-Map für die zurück, die sie brauchen.
Um diese Guards gegen deine eigenen Tabellen zu schreiben, mit erledigtem Aliasing, lade DynoTable herunter.
Verwandte Beispiele
- DynamoDB Conditional Write in Node.js — derselbe Optimistic Lock mit AWS SDK v3.
- DynamoDB Conditional Write mit der AWS CLI — derselbe Optimistic Lock aus der Shell.
- DynamoDB PutItem in Python — das nur-erzeugende
attribute_not_exists-Put. - DynamoDB Condition Expressions — jede Funktion, mit Mustern.
- Eindeutigkeit über mehrere Attribute erzwingen — Bedingungen + Transaktionen kombiniert.
- DynamoDB ConditionalCheckFailedException — wann die fehlgeschlagene Prüfung erwartet ist und wie du sie günstig behandelst.
Referenzen
- UpdateItem — Amazon DynamoDB API Reference
- DynamoDB.Client.update_item — Boto3 documentation
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
- Reserved words in DynamoDB — Amazon DynamoDB Developer Guide
Zuletzt verifiziert am 2026-07-28 gegen die oben verlinkte offizielle AWS-Dokumentation.