Batch write su DynamoDB in Python (boto3 batch_writer)
batch_writer() è l'unica chiamata DynamoDB in cui Python richiede meno lavoro degli altri SDK. Mette in buffer put e delete, li taglia in richieste BatchWriteItem da 25 e rimanda da sé gli item non elaborati. Quello che non fa è proteggerti dai due fallimenti che rompono la maggior parte dei caricamenti di massa, ed entrambi emergono al flush anziché sulla riga che ha fornito l'item sbagliato.
Codice
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")Spiegazione
- Flush differito —
batch.put_item()aggiunge a una lista. Nulla viene validato, serializzato o inviato finché il buffer non raggiunge 25 o il bloccowithnon termina, quindi il traceback di un item sbagliato arriva dal flush e non dalla chiamataput_itemche l'ha fornito. Se stai caricando da un iteratore, tieni un tuo indice di ciò che è finito nel buffer. - Valori Python normali — questa è la resource API, quindi scrivi
1994, non{"N": "1994"}. I Decimal sono obbligatori per qualsiasi valore frazionario; unfloatviene accettato nel buffer e rifiutato al flush. batch_writer()è un metodo diTable. La controparte in lettura non lo è:batch_get_itemvive sulServiceResource, etable.batch_get_itemnon esiste. Per le letture batch non c'è alcun helper di buffering, chunking o retry.UnprocessedItems, non errori — è l'unico retry che gestisce. Una scrittura sottoposta a throttling viene rimandata; unaValidationExceptionsi propaga. Passare invece daclient.batch_write_itemti consegna l'intero loop, come nell'esempio Node.js.- Non può sollevare i limiti del servizio. 25 scritture per richiesta, 400 KB per item, 16 MB per richiesta, niente condizioni e niente aggiornamenti, e ogni put sostituisce l'intero item memorizzato. Ti serve una guardia, o tutto-o-niente? TransactWriteItems.
Cosa fa davvero batch_writer al flush
Metti in buffer 30 put e guarda le chiamate che effettua. Avvolgendo table.meta.client.batch_write_item e registrando le dimensioni delle richieste, contro DynamoDB Local 3.3.0:
batch sizes sent: [25, 5]Due richieste, tagliate al limite del servizio, con il resto svuotato da __exit__. Quel flush è incondizionato: solleva un RuntimeError dentro il blocco e gli item in buffer vengono comunque scritti all'uscita. Un caricamento di massa che muore a metà lascia dietro di sé un caricamento parziale, non una tabula rasa.
Ora i due fallimenti. Metti in buffer due volte la stessa chiave, che è quello che succede nel momento in cui i tuoi dati di origine hanno una ripetizione:
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 duplicatesNessuno dei due put_item si è lamentato. batch_writer() non deduplica se non glielo chiedi, e chiederlo significa table.batch_writer(overwrite_by_pkeys=["Artist", "SongTitle"]). Fai passare le stesse due put da lì e l'item viene memorizzato come Year: 2 — il buffer tiene l'ultima scrittura per chiave, quindi la deduplicazione è perdita silenziosa di dati se le tue due righe dovevano essere item diversi sotto una chiave che hai sbagliato.
Il secondo è tutto di boto3 e non raggiunge mai DynamoDB:
TypeError: Float types are not supported. Use Decimal types instead.Un Rating di 4.5 resta nel buffer senza proteste ed esplode al flush. Decimal("4.5") fa il round-trip correttamente come {"N": "4.5"}. Leggi un prezzo o una valutazione da JSON con json.loads e ogni numero è un float, quindi questo è un fallimento alla prima esecuzione per la maggior parte degli script di import. Passare parse_float=Decimal a json.loads lo risolve alla fonte.
Se stai passando a mano tra valori Python nativi e il formato di trasporto, il convertitore DynamoDB JSON mostra entrambi i lati dello stesso item, così vedi in cosa si trasforma davvero il tuo Decimal.
Per caricare in blocco da CSV o JSON senza scrivere tu la mappatura dei tipi, scarica DynoTable.
Esempi correlati
- DynamoDB BatchWriteItem in Node.js — il loop di retry manuale che batch_writer nasconde.
- DynamoDB BatchWriteItem con la AWS CLI — la stessa scrittura batch dalla shell.
- DynamoDB PutItem in Python — la scrittura di un singolo item che questo raggruppa.
- Operazioni batch in DynamoDB — limiti, fallimento parziale e quando il batching conviene.
- "Too many items requested for the BatchWriteItem call" — più di 25 richieste put/delete in un solo batch.
- "Provided list of item keys contains duplicates" — due richieste che toccano la stessa chiave in un solo batch.
Riferimenti
- Amazon DynamoDB guide (batch_writer) — Boto3 documentation
- BatchWriteItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
Ultima verifica 2026-07-28 rispetto alla documentazione ufficiale AWS collegata sopra.