Intermédiaire6 min de lecture

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 (ou SET 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 SELECT puis un UPDATE ; ici tu sautes la lecture entièrement et l'opération reste sûre en concurrence.
  • Les compteurs atomiques ne sont pas idempotents. Un UpdateItem réessayé incrémente à nouveau. Si tu ne peux pas tolérer de sur- ou sous-comptage, utilise une mise à jour conditionnelle.
  • ADD sur 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 :

PKSKplay_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 :oneSET play_count = play_count + :one
Attr absentLe crée, démarre à 0Erreur — nécessite if_not_exists
Types de donnéesNombres et sets seulementNombres (et plus) via SET
Combiner avec SETClause séparéeUne clause SET, séparée par virgules
Conseil AWSOK pour les compteursDé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#TOTAL peut atteindre un plafond d'écriture par partition. Sharde-la : répartis les écritures sur STATS#TOTAL#0..N et somme à la lecture.
  • Pas d'incrément en batch. BatchWriteItem est put/delete seulement — il ne peut pas exécuter d'expressions de mise à jour. Les compteurs passent par UpdateItem, un item par appel.
  • ADD est nombres et sets seulement. Il ne touchera ni les strings ni les booléens ; c'est un SET. 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.

Mis à jour