Scrittura condizionale DynamoDB in Python (boto3)
boto3 è l'unico SDK in cui una scrittura condizionale ha una classe di eccezione dedicata da catturare, ed è anche quello in cui l'Item restituito si nasconde in un posto che non indovineresti. L'espressione in sé funziona allo stesso modo ovunque; le espressioni di condizione DynamoDB coprono le funzioni e il pattern di optimistic locking.
Codice
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"))Spiegazione
ConditionalCheckFailedExceptionè una classe modellata, quindiexcept client.exceptions.…funziona. La maggior parte degli errori DynamoDB non lo è:ValidationExceptionnon ha alcuna classe e va confrontata sue.response["Error"]["Code"]. La classe modellata è comunque una sottoclasse diClientError, quindi unexcept ClientErrorgenerico più a monte se la mangerà se ordini i tuoi handler con leggerezza.- L'Item restituito è una chiave di primo livello di
e.response, non die.response["Error"]. Ecco perché il blocco leggee.response.get("Item"). È facile andare a cercarlo sotto["Error"]accanto aCodeeMessage, non trovare nulla e concludere che il parametro non abbia funzionato. - L'Item torna in DynamoDB JSON anche se magari sei abituato ai valori nativi, perché questo è il client di basso livello.
boto3.dynamodb.types.TypeDeserializerlo converte se vuoi Python semplice. - L'API resource esprime la stessa guardia come oggetti,
ConditionExpression=Attr("Version").eq(7) & Attr("Artist").exists(), con valori nativi e senza mappe di placeholder. Solleva l'eccezione identica, quindi la gestione qui sotto resta invariata. - Un check fallito fattura comunque una scrittura. La Developer Guide è esplicita sul fatto che una condizione falsa consuma capacità di scrittura, dimensionata sul maggiore tra il vecchio e il nuovo Item, quindi un retry senza limiti su una chiave contesa costa soldi veri senza fare progressi.
Dove boto3 mette l'Item restituito
Esegui il blocco contro una Version memorizzata pari a 9 e stampa le chiavi della response dell'eccezione. 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'}}Togli ReturnValuesOnConditionCheckFailure e lo stesso fallimento dà ['Error', 'ResponseMetadata']. La chiave Item è assente, e e.response.get("Item") restituisce None invece di sollevare un'eccezione. È la versione di questo bug che sopravvive alla code review e inizia a loggare None in produzione.
Perché ogni nome nell'espressione è aliasato
Il blocco scrive #version e #cond0 invece di Version e Artist, il che sembra eccessivo per due parole ordinarie. Lo è, per queste due. Version non è una parola riservata di DynamoDB, e usata nuda passa la validazione dei nomi.
Year è riservata, e la stessa tabella ne ha una. Metti una guardia direttamente su di essa e ottieni:
ValidationException: Invalid ConditionExpression: Attribute name is a reserved keyword;
reserved keyword: YearSu quella lista ci sono 573 parole, tra cui Name, Status, Size, Count, Data, Owner, Timestamp e Items. Aliasare tutto è il modo in cui il codice generato evita di dover sapere quale sia quale. Incolla i nomi dei tuoi attributi nel checker delle parole riservate e ti restituisce la mappa ExpressionAttributeNames per quelli che ne hanno bisogno.
Per scrivere queste guardie sulle tue tabelle con l'aliasing gestito al posto tuo, scarica DynoTable.
Esempi correlati
- Scrittura condizionale DynamoDB in Node.js — lo stesso optimistic lock con AWS SDK v3.
- Scrittura condizionale DynamoDB con la AWS CLI — lo stesso optimistic lock dalla shell.
- DynamoDB PutItem in Python — il put di sola creazione con
attribute_not_exists. - Espressioni di condizione DynamoDB — ogni funzione, con i pattern.
- Imporre l'unicità su più attributi — condizioni + transazioni combinate.
- DynamoDB ConditionalCheckFailedException — quando il check fallito è previsto, e come gestirlo a basso costo.
Riferimenti
- 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
Ultima verifica 2026-07-28 rispetto alla documentazione ufficiale AWS collegata sopra.