Les compteurs atomiques DynamoDB
Un compteur atomique est un attribut numérique que tu incrémentes sur place avec un
seul appel UpdateItem — pas de lecture d'abord, pas de course read-modify-write.
DynamoDB applique chaque incrément dans l'ordre d'arrivée et ne laisse jamais deux
écrivains s'écraser mutuellement le compteur.
Qu'est-ce qu'un compteur atomique DynamoDB ?
Un compteur atomique DynamoDB est un attribut numérique que tu incrémentes sur place avec un seul appel UpdateItem au moyen d'une expression de mise à jour ADD (ou SET x = x + :n). DynamoDB lit, ajoute et écrit la valeur côté serveur, si bien que les écrivains concurrents se sérialisent sans mises à jour perdues — mais ce n'est pas idempotent, donc un appel réessayé incrémente deux fois.
- Utilise
ADD(ouSET x = x + :n) pour incrémenter en un seul appel. DynamoDB lit, ajoute et écrit côté serveur — les appelants concurrents se sérialisent, pas de mises à jour perdues. - Pas de lecture d'abord. En venant de SQL tu ferais un
SELECTpuis unUPDATE; ici tu sautes la lecture entièrement et l'opération reste sûre en concurrence. - Les compteurs atomiques ne sont pas idempotents. Un
UpdateItemréessayé incrémente à nouveau. Si tu ne peux pas tolérer de sur- ou sous-comptage, utilise une mise à jour conditionnelle. ADDsur un attribut absent démarre à 0, donc le tout premier incrément fonctionne — pas d'écriture de seed nécessaire.
Le problème du read-modify-write
Disons que tu suis les vues d'une vidéo. L'instinct naïf, tout droit de SQL, c'est :
GetItem, ajouter un dans ton appli, PutItem le nouveau total.
Deux spectateurs lancent la lecture en même temps. Les deux lisent views = 41. Les
deux écrivent 42. Tu as compté une vue, pas deux. C'est une mise à jour perdue — le
footgun classique de concurrence, et il ne se montre pas avant d'avoir du trafic.
En SQL tu l'esquiverais avec UPDATE videos SET views = views + 1, en poussant
l'arithmétique dans la base. DynamoDB a le même geste, et c'est tout l'intérêt d'un
compteur atomique.
Incrémenter en un seul appel
Modélise un item de stats par vidéo. Clé de partition VID#<id>, clé de tri
STATS#TOTAL, avec un play_count numérique :
| PK | SK | play_count |
|---|---|---|
| "VID#9f3a" | "STATS#TOTAL" | 41 |
Pour enregistrer une lecture, envoie un seul UpdateItem avec une clause ADD :
# UpdateItem
Key PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression ADD play_count :one
Values :one = 1
DynamoDB lit play_count, ajoute 1 et écrit le résultat dans une seule opération
côté serveur. Il n'y a pas de fenêtre pour qu'un autre écrivain se glisse. Dix lectures
concurrentes produisent +10, à chaque fois — c'est ce que « atomique » t'achète.
Tu peux construire et copier cette expression exacte — noms, valeurs et les quatre types de clauses — avec le DynamoDB Expression Builder.
ADD fonctionne même quand play_count n'existe pas encore : DynamoDB traite un
attribut numérique absent comme 0, donc la première lecture le crée à 1. Pas
d'écriture de seed séparée. (AWS : Using update expressions)
ADD vs SET + : choisis-en un
Deux expressions font la même arithmétique. AWS recommande SET pour l'usage général
parce qu'il se compose avec d'autres actions SET et se lit plus explicitement. (AWS :
Using update expressions)
ADD play_count :one | SET play_count = play_count + :one | |
|---|---|---|
| Attr absent | Le crée, démarre à 0 | Erreur — nécessite if_not_exists |
| Types de données | Nombres et sets seulement | Nombres (et plus) via SET |
Combiner avec SET | Clause séparée | Une clause SET, séparée par virgules |
| Conseil AWS | OK pour les compteurs | Défaut recommandé |
Si l'attribut peut ne pas exister et que tu veux SET, garde-le :
SET play_count = if_not_exists(play_count, :zero) + :one. Avec ADD tu sautes ça —
il seed depuis 0 gratuitement.
Fais-le dans DynoTable
Ouvre l'item, édite play_count, et tu peux observer un incrément atomique atterrir
sans écrire de JSON à la main — le panneau de mise à jour émet l'expression ADD pour
toi et montre la nouvelle valeur au moment où elle commit.
Le piège : les compteurs ne sont pas idempotents
Voici la partie qui mord les équipes en production. Un compteur atomique incrémente
à chaque fois que UpdateItem s'exécute. (AWS : Working with items)
Imagine un blip réseau : tu envoies l'incrément, la connexion tombe avant que la réponse revienne, et tu ne sais pas si ça a atterri. Tu réessaies. Si le premier appel a réussi, tu as maintenant compté cette lecture deux fois.
Pour les vues vidéo c'est très bien — quelques doubles comptages sur un million de lectures ne feront de mal à personne, et AWS appelle ce cas exact « suivi des visiteurs » l'usage canonique des compteurs atomiques. (AWS : Working with items)
Ce n'est pas bien pour quoi que ce soit qui doive être exact : un inventaire que tu peux survendre, des crédits que tu peux dépenser deux fois, un solde que tu peux corrompre. Là, atteins une mise à jour conditionnelle.
Quand tu as besoin d'exactitude : les mises à jour conditionnelles
Une mise à jour conditionnelle est idempotente si tu conditionnes sur le même attribut
que tu changes. Incrémente play_count à 42, mais seulement s'il est actuellement à
41 :
# UpdateItem
Key PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression SET play_count = :next
ConditionExpression play_count = :current
Values :next = 42, :current = 41
Maintenant un retry est sûr : si la première écriture a déjà déplacé play_count à
42, la condition play_count = 41 échoue la seconde fois et rien ne change. (AWS :
Working with items)
Le coût est la concurrence. Deux écrivains en course sur la même condition signifie que
l'un gagne et l'autre obtient une ConditionalCheckFailedException à réessayer — tu as
échangé le débit du compteur inconditionnel contre la correction. Pour des compteurs
exacts et contestés c'est le bon compromis. Pour des compteurs de vues c'est excessif.
Pièges
- Un seul item chaud. Une seule ligne de compteur est une seule clé de partition.
Une vidéo virale martelant
VID#9f3a/STATS#TOTALpeut atteindre un plafond d'écriture par partition. Sharde-la : répartis les écritures surSTATS#TOTAL#0..Net somme à la lecture. - Pas d'incrément en batch.
BatchWriteItemest put/delete seulement — il ne peut pas exécuter d'expressions de mise à jour. Les compteurs passent parUpdateItem, un item par appel. ADDest nombres et sets seulement. Il ne touchera ni les strings ni les booléens ; c'est unSET. Voir les types de données DynamoDB pour le modèle d'attribut complet.
Étapes suivantes
Les compteurs atomiques sont un pattern d'écriture ; la façon dont tu lis les
agrégats est une question de modélisation — voir
single-table design pour garder les items de stats à
côté de leur parent, et Query vs Scan pour que rouler un
compteur shardé reste un Query.
Rédige et copie l'incrément dans le DynamoDB Expression Builder, puis essaie DynoTable pour exécuter des mises à jour atomiques sur tes propres tables et observer les compteurs bouger.