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/SKdans ton code (par ex.CONFIG#GLOBAL) au lieu d'y injecter un id d'utilisateur ou de commande. - Lis-le avec
GetItem, jamaisScan. 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 :
| PK | SK | attributes |
|---|---|---|
| SETTINGS#APP | FLAGS#V1 | signup_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.
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-
Putl'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 singleton | Item par entité | |
|---|---|---|
| Clé | Constante codée en dur (SETTINGS#APP) | Gabarit avec un id (USER#42) |
| Combien | Exactement un | Un par utilisateur / commande / locataire |
| Lecture type | GetItem sur la clé connue | GetItem ou Query par entité |
| Portée | Toute l'application | Une seule entité |
| À utiliser pour | Flags globaux, config, version système | Profils, 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
Scanpour ç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.