Intermedio7 min de lectura

Operaciones por lotes en DynamoDB

Cuando necesitas leer o escribir muchos items de golpe, disparar un GetItem o PutItem por item significa un round trip de red por item — lento y hablador. Las APIs de lote de DynamoDB pliegan muchas operaciones de item en una sola petición: BatchGetItem para lecturas, BatchWriteItem para escrituras.

Son una ganancia de throughput y latencia, no una garantía de consistencia — y esa distinción es donde la gente se quema. Un lote no es una transacción.

¿Qué son las operaciones por lotes de DynamoDB?

Las operaciones por lotes de DynamoDB pliegan muchas lecturas o escrituras de items en una sola petición: BatchGetItem trae hasta 100 items, BatchWriteItem pone o borra hasta 25, cada uno con tope de 16 MB. Ahorran round trips, no capacidad. Lo crítico: un lote no es una transacción — los items tienen éxito o fallan por separado, sin rollback.

  • BatchGetItem — trae hasta 100 items (o 16 MB) a través de una o más tablas en una llamada.
  • BatchWriteItem — hasta 25 operaciones put/delete (o 16 MB) en una llamada. Sin updates — solo puts y deletes.
  • No es atómico. Items individuales pueden tener éxito mientras otros fallan. No hay rollback.
  • El fallo parcial es normal. Los items con throttle vuelven en UnprocessedItems / UnprocessedKeys — tienes que reintentarlos tú, con backoff.
  • El mismo coste de capacidad que las llamadas individuales — el lote ahorra round trips, no unidades de capacidad.

El problema: muchos items, un round trip

Digamos que llevas un support desk. Un dashboard necesita cargar 50 tickets por ID para renderizar una cola; un job nocturno archiva 1.000 tickets resueltos. Hacerlo un item a la vez son 50 (o 1.000) round trips secuenciales — la latencia se acumula y el job se arrastra.

El lote colapsa eso en un puñado de llamadas. La lectura de 50 tickets pasa a ser un solo BatchGetItem; el job de archivo pasa a ser un stream de llamadas BatchWriteItem de 25 deletes cada una. Muchos menos round trips, los mismos datos movidos.

Cómo funcionan las APIs de lote

BatchGetItem toma un conjunto de claves primarias (a través de una o más tablas) y devuelve los items que coinciden. Puedes pedir lecturas de consistencia fuerte por tabla. Lo que no pudo leer — normalmente porque la petición rozó un límite de throughput — vuelve en UnprocessedKeys en vez de fallar toda la llamada.

BatchWriteItem toma una lista de operaciones PutRequest / DeleteRequest. Fíjate en lo que falta: no hay update. Una escritura por lote o reemplaza un item entero (put) o lo quita (delete) — para modificar atributos concretos sigues necesitando UpdateItem. Los items que no pudo escribir vuelven en UnprocessedItems.

éxitothrottlereintentar con backoffBatchWriteItem: 25 puts/deletesProcesamiento por itemEscritoUnprocessedItems

Un lote es un paquete de operaciones independientes, cada una con éxito o fallo por su cuenta — no una unidad todo-o-nada.

Los lotes no son transacciones

Esta es la trampa. Si el lote del job de archivo choca con un límite de throughput a mitad, algunos tickets se borran y otros no — y DynamoDB no deshace los que pasaron. No hay rollback, no hay aislamiento, no hay "los 25 o ninguno".

Si necesitas semántica todo-o-nada — "mover el ticket a archived y decrementar el contador de tickets open, o no hacer ninguna de las dos" — eso es TransactWriteItems, no un lote. Las transacciones cuestan más (cada operación se factura al doble) y topean en 100 items, pero te dan la atomicidad que los lotes deliberadamente no dan.

Manejar items no procesados

Un llamador de lote correcto siempre mira el conjunto no procesado y lo reintenta. DynamoDB devuelve UnprocessedItems/UnprocessedKeys cuando la petición en conjunto fue aceptada pero algunos items no pudieron servirse — típicamente throttle transitorio.

Reenvía solo los items no procesados, con exponential backoff y jitter. Tratar un lote como fire-and-forget deja caer escrituras en silencio — el tipo de bug que aparece meses después como datos que faltan.

Escrituras por lote en DynoTable

Estima primero qué costará un job masivo con la calculadora de pricing de DynamoDB — un lote consume la misma capacidad que las escrituras individuales que empaqueta, solo en menos peticiones.

En DynoTable, stageas tus edits en local y los revisas antes de confirmarlos — los cambios masivos a través de muchas filas salen como peticiones agrupadas en vez de una llamada API cada una. Los deletes masivos salen como escrituras por lote, con el retry de items no procesados resuelto por ti.

Revisando edits stageados antes de confirmarlos como lote en DynoTable.
Revisando edits stageados antes de confirmarlos como lote en DynoTable.

Escollos + próximos pasos

  • Siempre reintenta UnprocessedItems/UnprocessedKeys con backoff — son esperados, no excepcionales.
  • No hay rollback de fallo parcial. ¿Necesitas atomicidad? Usa transacciones.
  • No hay updates en una escritura por loteBatchWriteItem es solo put/delete; ve a UpdateItem para cambiar atributos.
  • Cuida los topes por llamada — 25 escrituras / 100 lecturas / 16 MB. Pasarlos falla toda la llamada con un ValidationException (demasiados items en BatchGetItem, en BatchWriteItem). Pagina a través de jobs más grandes; mira paginación.

¿Quieres ejecutar lecturas y escrituras masivas sin scriptear el loop de retry? Descarga DynoTable y edita tus tablas directamente.

Matemática de round trips

Las llamadas GetItem en serie pagan latencia por hop. BatchGetItem empaqueta hasta 100 claves o 16 MB por petición — el límite que llegue primero.

PatrónClavesRound trips aprox. @ 50 clavesNotas
GetItem en serie5050Código más simple; peor latencia de cola
Un BatchGetItem501Mismo total de RCU que 50 Gets
Dos lotes1202El segundo lote lleva 20 claves

El coste de capacidad no cambia — el lote ahorra wall-clock y CPU del cliente, no RCU. Para escrituras, 1.000 deletes a 25 por lote son 40 llamadas BatchWriteItem en vez de 1.000 deletes individuales.

Lecturas por lote de consistencia fuerte

BatchGetItem acepta ConsistentRead: true por tabla en el mapa de la petición. Las lecturas fuertes siguen costando 2× las RCU de las lecturas eventuales para los mismos items. Mezclar tablas de consistencia fuerte y eventual en una llamada de lote está bien — cada entrada de tabla lleva su propia flag.

Trocear jobs grandes

Al archivar 1.000 items de ~3 KB cada uno, una sola lectura por lote se queda bajo el tope de 100 items pero puede pasar de 16 MB (100 × 3 KB = 300 KB — seguro). Archiva items de 50 KB y chocas el tope de megabytes alrededor de 320 items por llamada aunque el límite de conteo sea 100.

Pagina las escrituras con loops explícitos:

for each chunk of 25 keys:
  BatchWriteItem
  retry UnprocessedItems with backoff until empty

El commit stageado de DynoTable agrupa escrituras elegibles y reintenta items no procesados automáticamente — el patrón que si no scriptearías con sleep con jitter.

Decisión lote vs transacción

NecesitasAPIMáx. itemsEn fallo parcial
Carga masiva best-effortBatchWriteItem25 opsReintentar no procesados
Movimiento de ledger todo-o-nadaTransactWriteItems100 opsToda la txn hace rollback
Leer muchas claves conocidasBatchGetItem100 clavesReintentar claves no procesadas
Leer + escribir de forma atómicaTransactWriteItems25 ops de transact (aplican límites documentados)Todo o nada

Genera payloads put/delete desde JSON plano con el convertidor JSON de DynamoDB al sembrar cargas por lote desde fixtures.

Inspeccionar capacidad consumida

Las respuestas de lote pueden incluir ConsumedCapacity por tabla cuando se pide. Loguéalo durante backfills — una tasa de throttle creciente aparece como conjuntos no procesados que crecen antes de que los jobs se paren del todo. Contrasta WCU sostenida con la calculadora de pricing si los lotes corren con horario.

Actualizado