Escrita condicional no DynamoDB em Python (boto3)
O boto3 é o único SDK em que uma escrita condicional tem uma classe de exceção nomeada para capturar, e também é aquele em que o item retornado se esconde em um lugar que você não adivinharia. A expressão em si funciona igual em todo lugar; expressões de condição do DynamoDB cobre as funções e o padrão de bloqueio otimista.
Código
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"))Explicação
ConditionalCheckFailedExceptioné uma classe modelada, entãoexcept client.exceptions.…funciona. A maioria dos erros do DynamoDB não é:ValidationExceptionnão tem classe nenhuma e precisa ser identificado pore.response["Error"]["Code"]. A classe modelada ainda é subclasse deClientError, então umexcept ClientErroramplo mais acima vai engoli-la se você ordenar seus handlers sem cuidado.- O item retornado é uma chave de nível superior de
e.response, não dee.response["Error"]. É por isso que o bloco usae.response.get("Item"). É fácil procurar debaixo de["Error"]junto deCodeeMessage, não encontrar nada e concluir que o parâmetro não funcionou. - O item volta em DynamoDB JSON mesmo que você esteja acostumado a valores nativos, porque este é o cliente de baixo nível. O
boto3.dynamodb.types.TypeDeserializerconverte se você quiser Python puro. - A API de recurso expressa a mesma proteção como objetos,
ConditionExpression=Attr("Version").eq(7) & Attr("Artist").exists(), com valores nativos e sem mapas de placeholders. Ela lança a exceção idêntica, então o tratamento abaixo continua o mesmo. - Uma checagem que falha ainda cobra uma escrita. O Developer Guide é explícito ao dizer que uma condição falsa consome capacidade de escrita, dimensionada pelo maior entre o item antigo e o novo, então um retry sem limite em uma chave disputada custa dinheiro de verdade sem fazer progresso algum.
Onde o boto3 coloca o item retornado
Execute o bloco contra um Version armazenado igual a 9 e imprima as chaves da resposta da exceção. 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'}}Remova ReturnValuesOnConditionCheckFailure e a mesma falha dá ['Error', 'ResponseMetadata']. A chave Item está ausente, e e.response.get("Item") retorna None em vez de lançar erro. Essa é a versão desse bug que sobrevive ao code review e começa a logar None em produção.
Por que todo nome na expressão tem alias
O bloco escreve #version e #cond0 em vez de Version e Artist, o que parece exagero para duas palavras comuns. E é, para essas duas. Version não é uma palavra reservada do DynamoDB, e usada diretamente ela passa na validação de nomes.
Year é reservada, e a mesma tabela tem uma. Faça a proteção diretamente sobre ela e você recebe:
ValidationException: Invalid ConditionExpression: Attribute name is a reserved keyword;
reserved keyword: YearHá 573 palavras nessa lista, incluindo Name, Status, Size, Count, Data, Owner, Timestamp e Items. Dar alias a tudo é como o código gerado evita ter que saber qual é qual. Cole os nomes dos seus atributos no verificador de palavras reservadas e ele devolve o mapa ExpressionAttributeNames para os que precisam.
Para escrever essas proteções contra as suas próprias tabelas com o aliasing resolvido para você, baixe o DynoTable.
Exemplos relacionados
- Escrita condicional no DynamoDB em Node.js — o mesmo bloqueio otimista com o AWS SDK v3.
- Escrita condicional no DynamoDB com a AWS CLI — o mesmo bloqueio otimista pelo shell.
- PutItem do DynamoDB em Python — o put de
attribute_not_existsapenas para criação. - Expressões de condição do DynamoDB — todas as funções, com padrões.
- Garantindo unicidade em múltiplos atributos — condições + transações combinadas.
- DynamoDB ConditionalCheckFailedException — quando a checagem falha por esperado, e como lidar com isso de forma barata.
Referências
- 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
Verificado pela última vez em 2026-07-28 contra a documentação oficial da AWS vinculada acima.