Avancé7 min de lecture

Les transactions DynamoDB

Une transaction DynamoDB regroupe plusieurs écritures en une seule opération tout-ou-rien : soit chaque action est validée, soit aucune ne l'est. C'est ainsi que tu empêches deux items liés de se désynchroniser quand une écriture échoue à moitié.

Dans le scénario du journal d'audit, chaque événement que tu ajoutes devrait aussi incrémenter un eventCount par tenant. Si l'événement atterrit mais pas le compteur — ou l'inverse — le journal et le compte se contredisent pour toujours. Une transaction rend ça impossible.

DynamoDB prend-il en charge les transactions ?

Oui. DynamoDB prend en charge les transactions ACID via TransactWriteItems et TransactGetItems, qui regroupent jusqu'à 100 actions en une seule opération tout-ou-rien sur une ou plusieurs tables. Soit chaque écriture est validée, soit aucune ne l'est, ce qui empêche les items liés de se désynchroniser. Les écritures transactionnelles coûtent deux fois la capacité des écritures normales, et une condition échouée ou un conflit annule l'ensemble de la requête.

  • TransactWriteItems regroupe jusqu'à 100 actions d'écriture sur une ou plusieurs tables, en tout-ou-rien. La taille agrégée des items ne peut pas dépasser 4 Mo.
  • Les actions sont Put, Update, Delete et ConditionCheck. Un ConditionCheck affirme quelque chose sur un item que tu n'écris pas.
  • Ça coûte le double. Une écriture transactionnelle consomme deux fois la capacité d'une écriture normale — DynamoDB prépare puis valide.
  • Les conflits et les conditions échouées annulent l'ensemble avec une TransactionCanceledException ; rien de partiel n'est laissé derrière.

Le problème : deux écritures qui doivent s'accorder

Tu veux que chaque nouvel événement d'audit incrémente aussi le compteur courant du tenant. Fait en deux appels distincts, toute défaillance entre les deux corrompt tes données :

  1. PutItem du nouvel item EVENT#… — réussit.
  2. UpdateItem pour ADD eventCount 1 — expire en timeout.

Maintenant le journal a une ligne de plus que ce que prétend le compteur. Réessayer l'étape 2 à l'aveugle risque de double-compter ; ne pas réessayer les laisse incohérents. Il n'y a pas de récupération sûre car les deux écritures n'ont jamais été liées.

Venant de SQL, tu envelopperais les deux dans BEGIN … COMMIT. La réponse de DynamoDB est une unique requête transactionnelle qui porte les deux écritures ensemble.

Comment fonctionne TransactWriteItems

D'après le guide du développeur AWS, TransactWriteItems « regroupe jusqu'à 100 actions d'écriture en une seule opération tout-ou-rien » ciblant jusqu'à 100 items distincts, et « la taille agrégée des items de la transaction ne peut pas dépasser 4 Mo ». Les actions s'achèvent de façon atomique — toutes réussissent ou aucune.

Tu peux mélanger quatre types d'action dans une transaction :

  • Put — créer ou remplacer un item.
  • Update — modifier des attributs (y compris ADD pour notre compteur).
  • Delete — supprimer un item par clé.
  • ConditionCheck — affirmer une condition sur un item que tu n'écris pas par ailleurs (par ex. « ce tenant est toujours actif »).

Deux règles supplémentaires mordent en pratique. D'abord, les transactions consomment le double de la capacité des écritures non transactionnelles équivalentes — DynamoDB fait une phase de préparation et une phase de validation. Ensuite, tu ne peux pas cibler le même item deux fois dans une transaction, et les transactions ne peuvent pas être exécutées contre des index.

"DynamoDB"App"DynamoDB"Apppréparer les deux,vérifier les conditions"TransactWriteItems [Put EVENT,Update compteur]""les deux sont validées, ouTransactionCanceledException"

Un exemple concret : ajouter + compter, de façon atomique

Retour au journal d'audit. Ajouter un événement pour le tenant acme et incrémenter son compteur, c'est une transaction à deux actions :

actionitemeffet
PutTENANT#acmeEVENT#2026-06-24T09:14Z#a1écrire la nouvelle ligne d'audit
UpdateTENANT#acmeCOUNTERADD eventCount 1

Si la condition de l'une des actions échoue — disons un ConditionCheck que le tenant n'est pas suspendu — la requête entière est annulée avec une TransactionCanceledException et aucune des deux écritures n'a lieu. Le journal et le compteur ne peuvent jamais se contredire.

Le ConditionExpression de chaque action est le levier. Pour affirmer que la ligne de l'événement n'existe pas déjà (afin qu'un réessai ne puisse pas la dupliquer) et que le tenant est actif, tu composes des conditions comme attribute_not_exists(SK) sur le Put et status = :active comme ConditionCheck.

Construis et copie ces expressions de condition typées dans le DynamoDB Expression Builder plutôt que d'assembler à la main les ExpressionAttributeNames et les placeholders :val — le mode écriture conditionnelle émet exactement la forme que veut TransactWriteItems.

Pour des réessais sûrs sur une connexion instable, attache un client token : un TransactWriteItems répété avec le même token dans les 10 minutes renvoie un succès sans réappliquer les écritures (idempotence).

Fais-le dans DynoTable

DynoTable utilise les transactions en interne pour ses propres écritures : quand tu mets en attente plusieurs modifications d'items et que tu les valides, il les envoie sous forme de TransactWriteItems avec des expressions de condition de verrouillage optimiste, donc ton lot de modifications est tout-ou-rien — tu n'appliques jamais à moitié un changement multi-items.

Cela signifie que tu peux modifier la ligne de l'événement et le compteur dans le même lot mis en attente, revoir le diff, et valider les deux de façon atomique sans écrire de code SDK.

Mise en attente du nouvel événement d'audit et de la modification du compteur du tenant dans DynoTable, puis validation des deux en une seule transaction tout-ou-rien.
Mise en attente du nouvel événement d'audit et de la modification du compteur du tenant dans DynoTable, puis validation des deux en une seule transaction tout-ou-rien.

Pièges et étapes suivantes

  • Prévois le double de capacité. Une écriture transactionnelle facture deux fois la WCU d'une écriture simple — acceptable pour la paire occasionnelle critique pour la cohérence, coûteux si tu enveloppes chaque écriture dans une transaction. Utilise-la là où l'atomicité compte vraiment.
  • Gère TransactionCanceledException explicitement. Elle est renvoyée pour une condition échouée ou un conflit avec une autre transaction en cours sur les mêmes items. Les raisons d'annulation te disent quelle action a échoué — inspecte-les, ne réessaie pas à l'aveugle.
  • Les enregistrements de stream ne sont pas conscients des transactions. Les changements d'une transaction se propagent à Streams progressivement et peuvent s'entrelacer avec d'autres ; les consommateurs ne peuvent pas supposer d'atomicité ni d'ordre — voir DynamoDB Streams.
  • Pas pour des compteurs à fort débit. Un unique compteur chaud sous une lourde charge transactionnelle concurrente va ralentir ; pour ça, regarde les compteurs atomiques ou le sharding du compteur.

Les transactions sont l'outil pour « ces écritures doivent s'accorder ». Une fois que les événements atterrissent de façon cohérente, la préoccupation suivante est d'y réagir — c'est DynamoDB Streams.

Télécharge DynoTable pour mettre en attente des modifications multi-items et les valider en une seule transaction contre ta propre table.

Mis à jour