Intermédiaire9 min de lecture

Les items singleton dans DynamoDB

Un item singleton est une ligne unique avec une clé fixe et codée en dur qui détient un état pour toute ton application — pas un enregistrement par utilisateur ou par commande, mais un enregistrement, point. Feature flags, un blob de config, un coupe-circuit global : le genre de chose qu'une application relationnelle garderait dans une table de paramètres à une seule ligne.

En venant de SQL, tu dégainerais une table config avec id = 1 et un SELECT * FROM config. Dans DynamoDB tu fais la même chose avec une clé de partition codée en dur — et parce que tu connais toujours cette clé, tu la lis avec un GetItem, pas une Query ni un Scan.

Qu'est-ce qu'un item singleton dans DynamoDB ?

Un item singleton est une ligne DynamoDB unique stockée sous une clé fixe et codée en dur qui détient un état global pour toute ton application — feature flags, un blob de config, une version à l'échelle du système — plutôt qu'un enregistrement par utilisateur ou par commande. Parce que tu connais toujours la clé, tu la lis avec un GetItem et la mets à jour avec des et des expressions de condition.

  • Un singleton est un item avec une clé constante. Tu codes en dur les PK/SK dans ton code (par ex. CONFIG#GLOBAL) au lieu d'y injecter un id d'utilisateur ou de commande.
  • Lis-le avec GetItem, jamais Scan. Tu connais toujours la clé complète, donc une lecture ponctuelle a un coût stable et prévisible (au plus 1 RCU pour un petit item) — pas de filtre, pas de parcours de table.
  • C'est une par définition. Chaque requête peut toucher la même partition, donc mets-la en cache et garde l'item petit ; n'en fais pas un goulot d'étranglement en écriture.
  • Mute-le en toute sécurité avec des expressions de mise à jour et de condition, pas avec un lire-modifier-écrire dans ton application — c'est là que vit la course de perte de mise à jour.

Reconnais le motif

Tu as un état global quand les données ne sont rattachées à aucune entité particulière. Quelques indices :

  • Un flag identique pour tout le monde (signup_enabled = false).
  • Un blob de réglages que ton application lit au démarrage (limites de débit, quotas par défaut).
  • Un compteur ou un numéro de version pour tout le système, pas par ligne.

Tout ce qui est rattaché à un utilisateur, un locataire ou une commande n'est pas un singleton — c'est un item ordinaire clé par l'id de cette entité. Le singleton est la tranche globale résiduelle qui n'a nulle part où vivre.

Donne-lui une clé constante

Tout le motif repose sur une décision : la clé est un littéral, pas un gabarit. Pour un item global de feature flags dans une table unique surchargée, choisis un préfixe fixe et une valeur fixe :

PKSKattributes
SETTINGS#APPFLAGS#V1signup_enabled, maintenance_mode, ai_search_enabled

PK = "SETTINGS#APP" et SK = "FLAGS#V1" sont figés dans le code. Il n'y a pas d'id d'utilisateur, pas d'id de locataire — l'application demande exactement cet item à chaque fois. Cette prévisibilité est l'objectif : une clé connue est un GetItem, et un GetItem est la lecture la moins chère et la plus cohérente que DynamoDB offre.

Le suffixe V1 est délibéré. Si le schéma du flag change de forme plus tard, tu écris un item FLAGS#V2 et tu bascules les lecteurs, au lieu de muter l'item vivant en place. Versionner la clé du singleton t'offre une couture de migration propre.

Lis-le avec GetItem

Parce que la clé est entièrement connue, tu ne fais jamais de Query ni de Scan pour un singleton. Un Scan lit toute la table et filtre côté client — le classique piège du Scan — et c'est un excès absurde pour récupérer une ligne que tu peux adresser directement.

Un GetItem contre SETTINGS#APP / FLAGS#V1 renvoie les flags en une seule lecture fortement cohérente ou à cohérence à terme. AWS facture un GetItem d'un item ≤ 4 Ko à 0,5 RCU en cohérence à terme ou 1 RCU en cohérence forte (doc AWS sur la capacité de lecture/écriture). Garde le singleton petit et ce coût reste stable pour toujours.

Le chemin de lecture est simplement : l'application démarre ou une requête arrive, tu fais un GetItem sur la clé fixe, tu mets le résultat en cache. Voici le flux.

ouinonApp / requêteGetItem PK=SETTINGS#APPSK=FLAGS#V1Item trouvé ?Utiliser les flags, mise en cachelocaleRepli sur des valeurs sûres

La clé fixe transforme une recherche globale en une lecture ponctuelle avec un chemin de repli intégré.

Note la branche non : un singleton manquant ne devrait jamais te faire planter. Reviens à la valeur sûre (feature off, maintenance on) pour qu'un trou de premier déploiement ou une mauvaise clé échoue de façon fermée, pas ouverte.

Mets-le à jour sans course

Le piège est de mettre à jour un singleton avec un lire-modifier-écrire dans ton application : tu fais un GetItem des flags, en bascules un en mémoire, puis remets le tout avec un PutItem. Deux écrivains concurrents lisent tous deux l'ancien item et le second Put écrase le changement du premier. Perte de mise à jour.

Deux fonctionnalités de DynamoDB tuent la course sans verrouillage côté application :

  • Les mutent un attribut côté serveur, laissant le reste intact. Nul besoin de re-Put l'item entier.
  • Les font réussir l'écriture seulement si l'item ressemble encore à ce que tu attends, donc une écriture obsolète est rejetée avec ConditionalCheckFailedException (doc AWS sur les expressions de condition).

Pour basculer un flag, cible uniquement cet attribut avec un SET et protège-le avec une incrémentation de version pour que les écrivains concurrents ne se piétinent pas :

# UpdateItem
Key                  PK=SETTINGS#APP  SK=FLAGS#V1
UpdateExpression     SET signup_enabled = :on, schema_version = :next
ConditionExpression  schema_version = :current

Si deux écrivains font la course, la vérification schema_version = :current du second échoue et il réessaie contre la valeur fraîche. Tu peux échafauder les noms, les valeurs et cette forme exacte d'expression dans le Générateur d'expressions DynamoDB avant de le câbler dans le code. Pour un regard plus approfondi sur les opérateurs, voir le guide sur les idiomes d'expressions de mise à jour.

Attention à la clé chaude

Un singleton est, par construction, une clé chaude — chaque partie de ton application peut lire la même partition. C'est acceptable pour les lectures si tu mets en cache, mais c'est le seul vrai risque du motif.

  • Mets en cache agressivement. Lis les flags une fois par processus (ou toutes les N secondes), pas à chaque requête. La valeur du singleton est la chose la moins chère à mémoïser.
  • N'en fais pas un point chaud d'écriture. Un flag basculé par un admin quelques fois par jour n'est rien. Un singleton que tu incrémentes à chaque requête est un goulot d'étranglement de débit de partition — ça, c'est un problème de compteur, pas de singleton.
  • Garde-le petit. Le coût de lecture croît avec la taille de l'item par blocs de 4 Ko. Un blob de config gonflé rend chaque démarrage plus coûteux qu'il n'a besoin de l'être.

Si tu as réellement besoin d'un compteur global à forte écriture, le singleton est la mauvaise forme — shard-le sur N items et somme à la lecture. C'est un autre motif.

Singleton vs item par entité

La distinction est simplement ce à quoi la donnée est rattachée.

Item singletonItem par entité
CléConstante codée en dur (SETTINGS#APP)Gabarit avec un id (USER#42)
CombienExactement unUn par utilisateur / commande / locataire
Lecture typeGetItem sur la clé connueGetItem ou Query par entité
PortéeToute l'applicationUne seule entité
À utiliser pourFlags globaux, config, version systèmeProfils, commandes, tout ce qui est par-id

Si tu te surprends à vouloir deux singletons du même genre, tu n'as pas un singleton — tu as un item par entité et l'entité est ce que tu as oublié de clé (une config par locataire, disons).

Pièges et étapes suivantes

  • Ne fais pas de Scan pour ça. Tu connais la clé ; adresse-la directement.
  • Ne fais pas de lire-modifier-écrire dessus. Utilise des expressions de mise à jour et de condition.
  • Ne le laisse pas disparaître en silence. Reviens à la valeur sûre sur un cache miss.
  • Ne le surcharge pas d'écritures à haute fréquence. Ça, c'est un travail de compteur shardé.

Le singleton vit confortablement au sein d'un single-table design — c'est juste une collection d'items de plus avec une clé fixe, aux côtés de tes lignes d'entités.

Essaie DynoTable pour parcourir ta table, trouver la ligne singleton par sa clé fixe, et éditer les flags à la main pendant que tu construis le chemin d'écriture.

Mis à jour