Écriture par lots DynamoDB en Python (boto3 batch_writer)
batch_writer() est le seul appel DynamoDB où Python demande moins de travail que les autres SDK. Il met en tampon les puts et les deletes, les découpe en requêtes BatchWriteItem de 25, et renvoie lui-même les éléments non traités. Ce qu'il ne fait pas, c'est te protéger des deux échecs qui cassent la plupart des chargements en masse — et tous les deux apparaissent au vidage du tampon, pas à la ligne qui a fourni le mauvais élément.
Code
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")Explication
- Vidage différé —
batch.put_item()ajoute à une liste. Rien n'est validé, sérialisé ni envoyé tant que le tampon n'atteint pas 25 ou que le blocwithne se termine pas : la trace d'un élément invalide vient donc du vidage, et non de l'appelput_itemqui l'a fourni. Si tu charges depuis un itérateur, garde ton propre index de ce qui est entré dans le tampon. - Des valeurs Python natives — c'est l'API resource, donc tu écris
1994, pas{"N": "1994"}. Les décimaux exigentDecimal; unfloatest accepté dans le tampon puis rejeté au vidage. batch_writer()est une méthode deTable. Son pendant côté lecture ne l'est pas :batch_get_itemvit sur leServiceResource, ettable.batch_get_itemn'existe pas. Il n'y a aucun helper de mise en tampon, de découpage ou de reprise pour les lectures par lots.UnprocessedItems, pas les erreurs — c'est la seule reprise qu'il gère. Une écriture throttlée est renvoyée ; uneValidationExceptionremonte. Passer parclient.batch_write_itemte rend toute la boucle, comme dans l'exemple Node.js.- Il ne peut pas lever les limites du service. 25 écritures par requête, 400 Ko par élément, 16 Mo par requête, ni conditions ni mises à jour, et chaque put remplace l'élément stocké en entier. Besoin d'une garde, ou du tout-ou-rien ? TransactWriteItems.
Ce que batch_writer fait réellement au vidage
Mets 30 puts en tampon et observe les appels qu'il émet. En enveloppant table.meta.client.batch_write_item pour enregistrer la taille des requêtes, sur DynamoDB Local 3.3.0 :
batch sizes sent: [25, 5]Deux requêtes, coupées à la limite du service, avec le reliquat vidé par __exit__. Ce vidage est inconditionnel : lève une RuntimeError dans le bloc et les éléments en tampon sont quand même écrits à la sortie. Un chargement en masse qui meurt à mi-parcours laisse derrière lui un chargement partiel, pas une ardoise propre.
Passons aux deux échecs. Mets deux fois la même clé en tampon, ce qui arrive dès que tes données source contiennent un doublon :
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 duplicatesAucun des deux put_item n'a bronché. batch_writer() ne déduplique pas sauf si tu le demandes, et le demander s'écrit table.batch_writer(overwrite_by_pkeys=["Artist", "SongTitle"]). Fais passer les deux mêmes puts par là et l'élément est stocké avec Year: 2 — le tampon garde la dernière écriture par clé, donc la déduplication est une perte de données silencieuse si tes deux lignes étaient censées être des éléments différents sous une clé que tu as mal choisie.
Le second échec appartient à boto3 seul et n'atteint jamais DynamoDB :
TypeError: Float types are not supported. Use Decimal types instead.Un Rating à 4.5 reste dans le tampon sans provoquer la moindre plainte, puis explose au vidage. Decimal("4.5") fait l'aller-retour correctement sous la forme {"N": "4.5"}. Lis un prix ou une note depuis du JSON avec json.loads et chaque nombre est un float : c'est donc un échec dès la première exécution pour la plupart des scripts d'import. Passer parse_float=Decimal à json.loads corrige le problème à la source.
Si tu passes à la main des valeurs Python natives au format de transport, le convertisseur DynamoDB JSON affiche les deux faces du même élément pour que tu voies ce que ton Decimal devient vraiment.
Pour charger en masse depuis du CSV ou du JSON sans écrire toi-même le mappage de types, télécharge DynoTable.
Exemples liés
- BatchWriteItem DynamoDB en Node.js — la boucle de reprise manuelle que batch_writer cache.
- BatchWriteItem DynamoDB avec l'AWS CLI — la même écriture par lots depuis le shell.
- PutItem DynamoDB en Python — l'écriture d'un seul élément que ceci met en lots.
- Opérations par lots dans DynamoDB — limites, échec partiel, et quand le batch est rentable.
- "Too many items requested for the BatchWriteItem call" — plus de 25 requêtes put/delete dans un même lot.
- "Provided list of item keys contains duplicates" — deux requêtes touchant la même clé dans un même lot.
Références
- Amazon DynamoDB guide (batch_writer) — Boto3 documentation
- BatchWriteItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
Dernière vérification le 2026-07-28 par rapport à la documentation officielle AWS liée ci-dessus.