DynamoDB BatchGetItem en Python (boto3)

batch_get_item recupera hasta 100 elementos por clave principal en una sola petición. El while request_items: del bloque de abajo es todo el idioma de boto3: DynamoDB te devuelve los pendientes en una respuesta correcta, y un diccionario vacío es falsy, así que el bucle se termina solo. Los límites y las reglas de resultados parciales viven en operaciones por lotes en DynamoDB; esta página trata de la llamada de boto3 y de los errores que lanza.

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")

Explicación

  • response["UnprocessedKeys"] siempre está. En un lote servido por completo la clave existe y contiene {}, así que request_items = response["UnprocessedKeys"] es seguro de indexar y el diccionario vacío falsy es lo que termina el while. Con Responses hay que tener más cuidado: una tabla cuyas claves fallaron todas simplemente no aparece, y por eso el bloque usa .get("Music", []).
  • ConsistentRead y ProjectionExpression van dentro del diccionario de cada tabla, junto a "Keys", no al lado de RequestItems. boto3 enviará encantado una clave mal colocada y dejará que el servicio la rechace.
  • Este es el cliente de bajo nivel, así que los valores son JSON de DynamoDB ({"S": ...}, {"N": ...}). La API de recursos tiene batch_get_item en el ServiceResource, no en Table. boto3.resource("dynamodb").batch_get_item(...) acepta valores nativos de Python; table.batch_get_item no existe. Esa asimetría sorprende a quien lo busca después de usar table.batch_writer(), que es un método de Table.
  • El backoff se aplica solo a UnprocessedKeys. Una ValidationException es un error en la petición, y reintentarla solo quema tiempo.

Los dos errores que lanza esta llamada, literales

Ambos son fallos del lado del cliente que ningún reintento arregla, y ambos aparecen como un simple botocore.exceptions.ClientError. Contra 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 duplicates

El primero son 101 claves, el segundo es la misma clave listada dos veces. Fíjate en lo que no puedes escribir para capturarlos:

except client.exceptions.ValidationException:  # AttributeError

botocore modela 34 clases de excepción con nombre en el cliente de DynamoDB, y ValidationException no es una de ellas. ConditionalCheckFailedException y ProvisionedThroughputExceededException sí lo son, y por eso la página de escritura condicional puede capturar por clase y esta no. Existe incluso una DuplicateItemException modelada, y no es lo que te da una clave duplicada en un lote. Así que una lectura por lotes tiene que ramificar según el código:

except ClientError as e:
    if e.response["Error"]["Code"] == "ValidationException":
        raise  # a bug in the request; retrying will not help

El caso de la clave duplicada es el que muerde en código real, porque una lista de claves montada a partir de un resultado de Query o de una tabla de unión repite con naturalidad. Deduplica antes de enviar, recordando que dos diccionarios son iguales solo si coincide cada atributo de clave.

El tamaño es la otra razón por la que un lote de 100 no sigue siendo un lote de 100: cada elemento se redondea a 4 KB para la facturación y cuenta contra los 16 MB de la respuesta, así que 100 elementos de 300 KB vuelven como unos 52 y el resto en UnprocessedKeys. La calculadora de tamaño de elemento te da la cifra por elemento que hay que multiplicar.

Para traerte de vuelta un conjunto de claves e inspeccionar lo que devolvió antes de escribir el bucle, descarga DynoTable.

Ejemplos relacionados

Referencias

Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.

Trabaja con DynamoDB sin la Consola

Un cliente de escritorio rápido para DynamoDB que ejecuta el SQL real que DynamoDB no puede — JOINs, GROUP BY, agregaciones — con edición visual y un agente de IA con tus propias claves de Bedrock.

Prueba gratuita de 30 días, sin tarjeta — después, el plan Free sin límite de tiempo.