Intermédiaire6 min de lecture

DynamoDB ReturnValues : ancien ou nouvel item

Par défaut, une écriture DynamoDB ne renvoie rien d'autre que le succès. Mais tu as souvent besoin des données autour de l'écriture — la valeur avant ta modification, ou la valeur fraîche après. Le réflexe naïf, c'est un second GetItem, qui est un aller-retour supplémentaire et une course : quelqu'un d'autre peut écrire entre les deux. DynamoDB évite les deux avec le paramètre ReturnValues, qui te renvoie l'ancien ou le nouvel item de façon atomique dans l'écriture elle-même.

Que fait ReturnValues dans DynamoDB ?

ReturnValues indique à une opération d'écriture DynamoDB de renvoyer l'item dans le même appel, ce qui évite un second GetItem et la course aux données qu'il crée. PutItem et DeleteItem acceptent NONE ou ALL_OLD ; UpdateItem accepte les cinq options (NONE, ALL_OLD, UPDATED_OLD, ALL_NEW, UPDATED_NEW), renvoyant les valeurs anciennes ou nouvelles de façon atomique.

  • ReturnValues renvoie l'item dans le cadre de l'écriture — pas de seconde lecture, pas de course.
  • NONE (par défaut) — ne renvoie rien.
  • ALL_OLD — l'item entier tel qu'il était avant l'écriture.
  • UPDATED_OLD — uniquement les attributs que la mise à jour a changés, valeurs d'avant.
  • ALL_NEW — l'item entier après l'écriture.
  • UPDATED_NEW — uniquement les attributs changés, valeurs d'après.
  • PutItem/DeleteItem n'acceptent que NONE ou ALL_OLD ; UpdateItem accepte les cinq.

Le problème : tu as besoin de la valeur que tu viens d'écraser

Disons que tu gères un service de support et qu'un agent fait passer le statut d'un ticket de open à pending. Ton journal d'audit doit consigner ce qu'était le statut avant le changement. Sans ReturnValues, tu ferais :

  1. GetItem pour lire le statut actuel,
  2. UpdateItem pour fixer le nouveau.

Entre les étapes 1 et 2, un autre agent pourrait changer le statut — et maintenant ton journal d'audit consigne une valeur « avant » périmée. Pire, ce sont deux appels pour une seule opération logique. ReturnValues réduit ça à un unique UpdateItem atomique qui renvoie l'ancien statut tel qu'il était réellement au moment de l'écriture.

Les cinq options, et quand utiliser chacune

UpdateItem prend en charge tout l'éventail ; le choix porte sur quelle tranche de l'item et quel côté de l'écriture tu veux :

ReturnValuesRenvoieÀ utiliser quand
NONErientu n'as pas besoin de récupérer l'item (par défaut)
ALL_OLDitem entier, avant écritureaudit / « qu'est-ce que je viens de remplacer ? »
UPDATED_OLDattrs changés, avant écritureseuls comptent les champs que tu as touchés
ALL_NEWitem entier, après écrituretu dois renvoyer l'item complet et frais à un appelant
UPDATED_NEWattrs changés, après écriturerelire un compteur/une valeur que tu viens d'incrémenter

UPDATED_NEW est le héros du quotidien : incrémente un compteur avec une expression de mise à jour et relis le nouveau total dans le même appel, sans course. Pour l'audit du ticket de support, ALL_OLD (ou UPDATED_OLD si tu ne consignes que le champ statut) capture l'état d'avant changement de façon atomique.

Note l'asymétrie : PutItem et DeleteItem ne prennent en charge que NONE et ALL_OLD — il n'y a pas de « nouvelle » valeur à renvoyer pour un delete, et la nouvelle valeur d'un put est simplement ce que tu as envoyé. Seul UpdateItem, qui modifie sur place, offre les cinq. AWS documente la matrice exacte.

Écrire la mise à jour dans DynoTable

Assemble l'UpdateItem et son expression de mise à jour visuellement avec le constructeur d'expressions DynamoDB — il émet la clause SET/ADD plus les maps de noms et de valeurs d'attributs. Dans l'application, DynoTable montre l'item résultant après la validation d'une écriture en attente, donc tu vois le nouvel état directement.

Revue d'un changement préparé sur un item dans DynoTable — les anciennes et nouvelles valeurs avant que la mise à jour ne soit validée.
Revue d'un changement préparé sur un item dans DynoTable — les anciennes et nouvelles valeurs avant que la mise à jour ne soit validée.

Pièges + étapes suivantes

  • Ne fais pas GetItem-puis-écriture pour lire autour d'un changement — c'est un aller-retour et une course ; utilise ReturnValues.
  • UPDATED_* ne renvoie que les attributs touchés — s'il te faut l'item entier, utilise ALL_*.
  • PutItem/DeleteItem ne peuvent pas renvoyer de nouvelles valeurs — seulement NONE/ALL_OLD.
  • ReturnValues n'est pas un substitut à une condition — pour protéger une écriture, ajoute une expression de condition ; pour en relire l'effet, utilise ReturnValues. Elles se composent.
  • À voir aussi : expressions de mise à jour, compteurs atomiques.

Envie de faire des modifications et de voir l'avant/après sans scripter deux appels ? Télécharge DynoTable et édite tes items directement.

Compteur atomique avec UPDATED_NEW

Les systèmes d'inventaire incrémentent un champ version ou stock à chaque écriture. Le motif est un UpdateItem avec ADD stock :inc et ReturnValues: UPDATED_NEW :

UpdateItem  PK=SKU#8842
  UpdateExpression: ADD stock :one
  ExpressionAttributeValues: {":one": {"N": "1"}}
  ReturnValues: UPDATED_NEW
Attributes.stock.N == "41"   (was 40)

Tu reçois seulement la map d'attributs changés, pas l'item entier — idéal quand l'item est gros mais que l'appelant a besoin du nouveau compteur. Pour des pistes d'audit qui doivent capturer chaque champ avant le changement, passe à ALL_OLD.

L'écriture se facture toujours comme un UpdateItem sur la taille de l'item ; ReturnValues n'ajoute pas de charge de lecture séparée — DynamoDB a déjà chargé l'item pour appliquer la mise à jour.

Note sur la capacité

Renvoyer des attributs ne double pas le coût WCU de l'écriture elle-même. Tu paies pour l'écriture selon la taille de l'item avant et après la mise à jour d'après les règles AWS, indépendamment du nombre d'attributs qui apparaissent dans le payload de réponse.

Si tu étais tenté de faire GetItem puis UpdateItem pour journaliser l'ancienne valeur, tu as payé une lecture plus une écriture. ReturnValues: ALL_OLD sur l'update retire complètement la lecture — sur un item de 2 Ko à 500 updates par seconde, ça économise environ 250 RCU/s à cohérence à terme.

Composer avec les expressions de condition

ReturnValues et les expressions de condition se composent sur le même appel. Exemple : incrémenter retryCount seulement tant qu'il est sous un plafond, et renvoyer le nouveau compte :

ConditionExpression: retryCount < :max
UpdateExpression: ADD retryCount :one
ReturnValues: UPDATED_NEW

Si la condition échoue, DynamoDB renvoie ConditionalCheckFailedException et aucun payload d'attribut — distinct d'un update réussi avec un UPDATED_NEW vide quand rien n'a changé.

Utilise l'expression builder pour générer ensemble l'UpdateExpression, la condition et les maps de valeurs marshallées.

Guide de décision

Tu as besoin de…RéglageMarche sur
Rien en retourNONEPut, Update, Delete
Item entier avant overwrite/deleteALL_OLDPut, Update, Delete
Seulement les champs changés, avantUPDATED_OLDUpdate
Item entier après patchALL_NEWUpdate
Seulement les champs changés, aprèsUPDATED_NEWUpdate

Deletes et puts

DeleteItem avec ReturnValues: ALL_OLD est comment tu implémentes une sémantique « pop et retourne » sur un item de file — la ligne supprimée revient dans Attributes. Il n'y a pas de ALL_NEW sur delete parce que l'item n'existe plus.

PutItem avec ALL_OLD renvoie l'item précédent quand tu écrases une clé existante — utile pour les workflows de swap. Quand la clé n'existait pas, la réponse omet Attributes.

Vérifier dans DynoTable

Prépare un changement d'attribut dans l'éditeur d'item : le panneau de revue montre les anciennes et nouvelles valeurs côte à côte avant le commit — la même information que UPDATED_OLD et UPDATED_NEW renverraient, sans écrire un script. Après commit, copie la ligne en JSON pour des fixtures de test via les actions d'export de la grille.

Mis à jour