DynamoDB BatchGetItem em Python (boto3)
batch_get_item busca até 100 itens por chave primária em uma única requisição. O while request_items: no bloco abaixo é todo o idioma do boto3: o DynamoDB devolve as sobras em uma resposta bem-sucedida, e um dicionário vazio é falsy, então o loop termina sozinho. Os limites e as regras de resultado parcial estão em operações em lote no DynamoDB; esta página é sobre a chamada do boto3 e os erros que ela levanta.
Código
import time
import boto3
client = boto3.client("dynamodb")
request_items = {
"Music": {
"Keys": [
{"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
{"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "A Mis Abuelos"}},
{"Artist": {"S": "Ella Fitzgerald"}, "SongTitle": {"S": "Misty"}},
]
}
}
items = []
attempt = 0
while request_items:
response = client.batch_get_item(RequestItems=request_items)
items.extend(response["Responses"].get("Music", []))
# A partial result is NOT an error: throttling, a >16 MB response, or an
# internal failure returns the leftovers in UnprocessedKeys. Retry them
# with exponential backoff.
request_items = response["UnprocessedKeys"]
if request_items:
attempt += 1
time.sleep(min(0.1 * 2**attempt, 5))
print(f"Fetched {len(items)} items")Explicação
response["UnprocessedKeys"]está sempre lá. Em um lote totalmente atendido a chave existe e contém{}, entãorequest_items = response["UnprocessedKeys"]é seguro de indexar e o dicionário vazio falsy é o que encerra owhile.Responsesé a que exige cuidado: uma tabela cujas chaves todas erraram fica ausente dele, e é por isso que o bloco usa.get("Music", []).ConsistentReadeProjectionExpressionvão dentro do dicionário de cada tabela, ao lado de"Keys", não junto deRequestItems. O boto3 vai enviar alegremente uma chave fora de lugar e deixar o serviço rejeitá-la.- Este é o client de baixo nível, então os valores são JSON do DynamoDB (
{"S": ...},{"N": ...}). A API de resource tembatch_get_itemno ServiceResource, não emTable.boto3.resource("dynamodb").batch_get_item(...)aceita valores nativos do Python;table.batch_get_itemnão existe. Essa assimetria surpreende quem recorre a ela depois de usartable.batch_writer(), que é um método deTable. - O backoff se aplica apenas a
UnprocessedKeys. UmValidationExceptioné um bug na requisição, e repeti-la só queima tempo de relógio.
Os dois erros que esta chamada levanta, literalmente
Ambos são erros do lado do cliente que nenhum retry conserta, e ambos aparecem como um simples botocore.exceptions.ClientError. Contra o DynamoDB Local 3.3.0, str(e):
An error occurred (ValidationException) when calling the BatchGetItem operation: Too many items requested for the BatchGetItem call
An error occurred (ValidationException) when calling the BatchGetItem operation: Provided list of item keys contains duplicatesO primeiro são 101 chaves, o segundo é a mesma chave listada duas vezes. Repare no que você não pode escrever para capturá-los:
except client.exceptions.ValidationException: # AttributeErrorO botocore modela 34 classes de exceção nomeadas no client do DynamoDB, e ValidationException não é uma delas. ConditionalCheckFailedException e ProvisionedThroughputExceededException são, e é por isso que a página de escrita condicional consegue capturar por classe e esta não. Existe até uma DuplicateItemException modelada, e ela não é o que uma chave duplicada em um lote devolve. Então uma leitura em lote precisa ramificar pelo código:
except ClientError as e:
if e.response["Error"]["Code"] == "ValidationException":
raise # a bug in the request; retrying will not helpO caso da chave duplicada é o que morde no código real, porque uma lista de chaves montada a partir de um resultado de Query ou de uma tabela de junção repete naturalmente. Deduplique antes de enviar, lembrando que dois dicionários só são iguais se todos os atributos de chave coincidirem.
O tamanho é o outro motivo pelo qual um lote de 100 não continua sendo um lote de 100: cada item é arredondado para cima para 4 KB na cobrança e contado contra os 16 MB da resposta, então 100 itens de 300 KB voltam como cerca de 52, com o resto em UnprocessedKeys. A calculadora de tamanho de item te dá o valor por item para multiplicar.
Para trazer um conjunto de chaves de volta e inspecionar o que retornou antes de escrever o loop, baixe o DynoTable.
Exemplos relacionados
- DynamoDB BatchGetItem em Node.js — a mesma leitura em lote com o AWS SDK v3.
- DynamoDB BatchGetItem com a AWS CLI — a mesma leitura em lote a partir do shell.
- DynamoDB GetItem em Python — a leitura de item único que isto agrupa em lote.
- Operações em lote no DynamoDB — limites, falha parcial e quando o lote compensa.
- "Too many items requested for the BatchGetItem call" — mais de 100 chaves em uma única requisição.
- "Provided list of item keys contains duplicates" — a mesma chave duas vezes em um lote.
Referências
- BatchGetItem — Amazon DynamoDB API Reference
- DynamoDB.Client.batch_get_item — Boto3 documentation
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
Verificado pela última vez em 2026-07-28 contra a documentação oficial da AWS vinculada acima.