DynamoDB TransactWriteItems em Python (boto3)
Transações são um dos pontos em que as duas APIs do boto3 mais divergem: transact_write_items existe apenas no client de baixo nível, então a conveniência de tipos nativos do Python que você tem com Table está fora de questão aqui. E quando a transação falha, o que você precisa está em um canto da exceção para o qual a maior parte do código boto3 nunca olha. (O que uma transação te dá é o mesmo em todos os SDKs.)
Código
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']Explicação
TransactItems— uma lista de dicionáriosPut,Update,DeleteeConditionCheck, com todos os valores em JSON do DynamoDB, sem exceção. Esta é a única chamada do boto3 em que a forma tipada não é opcional, e é por isso que a seção no fim desta página existe. Os tetos estão na página da CLI.CancellationReasonsnão está dentro deError. O botocore eleva os campos de erro modelados para o topo do dicionário de resposta, então a exceção capturada carregae.responsecom as chavesCancellationReasons,Error,MessageeResponseMetadatalado a lado. Procurá-la sobe.response["Error"]não encontra nada, ee.response["Error"]guarda apenas o código e a mensagem de resumo.- Nenhum
"Message"nas entradasNone— o motivo de uma ação bem-sucedida é o dicionário de chave única{"Code": "None"}, então o natural[r["Message"] for r in reasons]levantaKeyError: 'Message'exatamente nas ações que funcionaram. User.get("Message"). - Uma classe de exceção gerada — o botocore constrói
client.exceptions.TransactionCanceledExceptiona partir do modelo de serviço em tempo de execução, e é por isso que ela pende da instância do client e por que você não consegue fazerfrom botocore.exceptions import ...dela. Em uma função auxiliar que não tem o client no escopo, capturebotocore.exceptions.ClientErrore ramifique eme.response["Error"]["Code"]; a classe gerada é uma subclasse dela. - Erros estruturais não chegam como cancelamentos, então a cláusula
exceptdo trecho nunca os vê. Duas ações mirando o mesmo item levantam umClientErrorpuro cujo código éValidationExceptione cujoe.responsenão tem chaveCancellationReasons, já que a transação foi rejeitada antes de qualquer ação rodar. CaptureClientErrorna borda externa se quiser esses casos logados com o mesmo contexto. ReturnValuesOnConditionCheckFailure: "ALL_OLD"em uma ação coloca o item perdedor sob uma chaveItemno motivo daquela ação, em JSON do DynamoDB, te poupando oget_itemseguinte depois de você já ter perdido a corrida.- O boto3 preenche o
ClientRequestTokenpor você. Capturadas no protocolo, duas chamadas idênticas detransact_write_itemssaíram com dois UUIDs diferentes, então o token cobre uma única chamada e não o seu próprio loop de capturar-e-repetir. Passe um estável você mesmo se o retry puder sobreviver ao processo. - Repita em
TransactionConflict, nunca emConditionalCheckFailed— o primeiro diz que outra pessoa segurou o item por um instante; o segundo diz que a sua pré-condição é falsa e continuará falsa na próxima vez. Esses são os únicos dois códigos que a maioria dos handlers precisa separar, e o conjunto completo está decodificado na página do TransactionCanceledException. - Custo — uma escrita transacional cobra cerca do dobro do que a mesma escrita custa fora de uma, medido na página da CLI. Se você só precisa de atomicidade em um único item, uma escrita condicional compra isso pela metade do preço.
Não existe versão disto na API de resource
boto3.resource("dynamodb").Table(...) não tem atributo transact_write_items; só resource.meta.client tem. Então uma base de código que já se acomodou em Table e tipos nativos do Python precisa recuar para JSON do DynamoDB tipado nas suas transações, ou serializar à mão com 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 aplica as mesmas regras da API de resource, o que significa que ele rejeita float e espera decimal.Decimal para qualquer coisa fracionária. O conversor de JSON do DynamoDB faz a mesma conversão no navegador quando você só precisa colar um literal em um script. Para editar os itens que uma transação toca sem escrever nenhuma das duas formas à mão, baixe o DynoTable.
Exemplos relacionados
- DynamoDB TransactWriteItems em Node.js — a mesma transação com o AWS SDK v3.
- DynamoDB TransactWriteItems com a AWS CLI — a mesma transação a partir do shell.
- Escrita condicional no DynamoDB em Python — atomicidade de item único sem o custo 2×.
- Transações do DynamoDB — isolamento, idempotência e quando as transações valem a pena.
- DynamoDB TransactionCanceledException — todos os códigos de motivo de cancelamento, decodificados.
- "Too many actions in a TransactWriteItems call" — os limites de 100 ações e 4 MB da transação.
- "Transaction request cannot include multiple operations on one item" — uma ação por item, por transação.
Referências
- 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
Verificado pela última vez em 2026-07-28 contra a documentação oficial da AWS vinculada acima.