DynamoDB Batch Write en Python (boto3 batch_writer)
batch_writer() es la única llamada de DynamoDB en la que Python da menos trabajo que los demás SDK. Almacena en búfer las escrituras y los borrados, los trocea en peticiones BatchWriteItem de 25 y reenvía él mismo los Items no procesados. Lo que no hace es protegerte de los dos fallos que rompen la mayoría de las cargas masivas, y ambos afloran en el vaciado del búfer y no en la línea que suministró el Item defectuoso.
Código
import boto3
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Music")
songs = [
{"Artist": "Arturo Sandoval", "SongTitle": "Cubano Chant", "AlbumTitle": "Danzon", "Year": 1994},
{"Artist": "Arturo Sandoval", "SongTitle": "A Mis Abuelos", "AlbumTitle": "Danzon", "Year": 1994},
{"Artist": "Arturo Sandoval", "SongTitle": "Groovin' High", "AlbumTitle": "Swingin'", "Year": 1996},
]
with table.batch_writer() as batch:
for song in songs:
batch.put_item(Item=song)
# batch_writer buffers deletes too — target a key you're NOT also putting
# (two writes to the same key in one batch are rejected as a duplicate)
batch.delete_item(Key={"Artist": "Ella Fitzgerald", "SongTitle": "Misty"})
print(f"Buffered {len(songs)} puts + 1 delete; the batch flushes on exit")Explicación
- Vaciado diferido —
batch.put_item()añade a una lista. No se valida, serializa ni envía nada hasta que el búfer llega a 25 o el bloquewithtermina, así que el traceback de un Item defectuoso viene del vaciado y no de la llamadaput_itemque lo suministró. Si cargas desde un iterador, lleva tu propio índice de lo que entró en el búfer. - Valores Python planos — esta es la API de recurso, así que escribes
1994, no{"N": "1994"}. LosDecimalson obligatorios para cualquier cosa fraccionaria; unfloatse acepta en el búfer y se rechaza al vaciarlo. batch_writer()es un método deTable. Su contrapartida del lado de la lectura no lo es:batch_get_itemvive en elServiceResource, ytable.batch_get_itemno existe. No hay ningún ayudante de búfer, troceado ni reintento para las lecturas por lotes.UnprocessedItems, no errores — ese es el único reintento que gestiona. Una escritura limitada se reenvía; unaValidationExceptionse propaga. Ir porclient.batch_write_itemen su lugar te entrega el bucle entero, como en el ejemplo de Node.js.- No puede levantar los límites del servicio. 25 escrituras por petición, 400 KB por Item, 16 MB por petición, sin condiciones y sin actualizaciones, y cada put reemplaza el Item almacenado entero. ¿Necesitas una guarda, o todo o nada? TransactWriteItems.
Qué hace realmente batch_writer al vaciar
Mete 30 puts en el búfer y observa las llamadas que hace. Envolviendo table.meta.client.batch_write_item y registrando los tamaños de petición, contra DynamoDB Local 3.3.0:
batch sizes sent: [25, 5]Dos peticiones, cortadas en el límite del servicio, con el resto vaciado por __exit__. Ese vaciado es incondicional: lanza un RuntimeError dentro del bloque y los Items en búfer se escriben igualmente al salir. Una carga masiva que muere a mitad de camino deja detrás una carga parcial, no borrón y cuenta nueva.
Ahora los dos fallos. Mete la misma clave dos veces en el búfer, que es lo que ocurre en cuanto tus datos de origen tienen una repetición:
with table.batch_writer() as batch:
batch.put_item(Item={"Artist": "Dup", "SongTitle": "Key", "Year": 1})
batch.put_item(Item={"Artist": "Dup", "SongTitle": "Key", "Year": 2})botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the
BatchWriteItem operation: Provided list of item keys contains duplicatesNinguno de los dos put_item protestó. batch_writer() no deduplica salvo que se lo pidas, y pedírselo es table.batch_writer(overwrite_by_pkeys=["Artist", "SongTitle"]). Pasa esos mismos dos puts por ahí y el Item se guarda como Year: 2 — el búfer conserva la última escritura por clave, así que la deduplicación es pérdida silenciosa de datos si tus dos filas debían ser Items distintos bajo una clave que escribiste mal.
El segundo es exclusivo de boto3 y nunca llega a DynamoDB:
TypeError: Float types are not supported. Use Decimal types instead.Un Rating de 4.5 se queda en el búfer sin protestar y revienta al vaciarlo. Decimal("4.5") va y vuelve correctamente como {"N": "4.5"}. Lee un precio o una valoración de un JSON con json.loads y todos los números son float, así que este es un fallo de primera ejecución para la mayoría de los scripts de importación. Pasar parse_float=Decimal a json.loads lo arregla en el origen.
Si mueves a mano valores nativos de Python al formato de cable y viceversa, el conversor de DynamoDB JSON muestra ambos lados del mismo Item para que veas en qué se convierte de verdad tu Decimal.
Para cargar en bloque desde CSV o JSON sin escribir tú el mapeo de tipos, descarga DynoTable.
Ejemplos relacionados
- DynamoDB BatchWriteItem en Node.js — el bucle de reintento manual que batch_writer esconde.
- DynamoDB BatchWriteItem con la AWS CLI — la misma escritura por lotes desde el shell.
- DynamoDB PutItem en Python — la escritura de un solo Item que esto agrupa.
- Operaciones por lotes en DynamoDB — límites, fallo parcial y cuándo compensa agrupar.
- "Too many items requested for the BatchWriteItem call" — más de 25 peticiones put/delete en un lote.
- "Provided list of item keys contains duplicates" — dos peticiones tocando la misma clave en un lote.
Referencias
- Amazon DynamoDB guide (batch_writer) — Boto3 documentation
- BatchWriteItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.