Pourquoi un GSI throttle la table de base
Tu écris dans ta table. L'écriture échoue avec une exception de débit — mais l'exception nomme un index secondaire global, pas la table. La table a de la capacité disponible.
Quand tu viens du SQL, c'est absurde : un index secondaire ne peut pas bloquer un
INSERT. Dans DynamoDB, si — et le mécanisme s'appelle la contre-pression du GSI.
Pourquoi un GSI DynamoDB throttle-t-il les écritures de la table de base ?
DynamoDB throttle l'écriture de la table de base parce que chaque écriture se réplique aussi vers chaque GSI, et si une partition de GSI ne peut pas absorber sa part, DynamoDB applique une contre-pression pour empêcher l'index de prendre un retard définitif. Une clé de GSI sous-provisionnée ou à faible cardinalité devient donc un plafond dur sur ton taux d'écriture dans la table de base.
- Une écriture dans la table de base écrit aussi dans chaque GSI. Si un GSI ne peut pas absorber sa part, DynamoDB throttle l'écriture de la table de base pour empêcher l'index de prendre un retard définitif. (docs AWS)
- Une table de base équilibrée ne te sauve pas. Le GSI est partitionné par sa propre clé.
Une clé de GSI à faible cardinalité (comme
status) crée une même quand les écritures de la table de base sont parfaitement réparties. - L'exception ment sur la victime. Le
ResourceArnpointe vers le GSI ; l'opération réellement throttlée est ton écriture dans la table. - Le correctif est la capacité ou la conception des clés, pas des boucles de réessai — augmente le débit du GSI, ou choisis une de GSI qui répartit bien.
Comment une seule écriture touche l'index
Un PutItem sur la table de base n'est pas une seule écriture. DynamoDB réplique les
attributs projetés de l'item dans chaque GSI de façon asynchrone, sur un modèle à
cohérence à terme. Une écriture logique se démultiplie en N écritures physiques — la table
plus chaque index.
Cette réplication n'est ni gratuite ni optionnelle. Le GSI doit suivre le rythme, sinon l'index dérive un peu plus loin de la table à chaque opération.
Pour arrêter cette dérive, DynamoDB applique une contre-pression : il throttle l'écriture source afin que l'index ne devienne jamais indéfiniment obsolète.
La capacité d'écriture du GSI est donc un plafond dur sur ton taux d'écriture dans la table de base — alors même que tu n'écris jamais directement dans le GSI.
Un exemple concret : une table de commandes
Disons que tu gères une table de commandes. L'item de base :
| field | value | note |
|---|---|---|
| PK | "CUST#8841" | partition key |
| SK | "ORD#2026-06-23#A7" | sort key |
| order_state | "PROCESSING" | |
| warehouse | "EU-MAD-2" | |
| total_cents | 4990 |
Les écritures de la table de base sont saines. CUST#... a une cardinalité élevée, donc
les écritures de commandes se répartissent uniformément entre les partitions de base. Pas
de clé surchargée, capacité largement suffisante.
Maintenant tu ajoutes un GSI pour répondre à « montre-moi toutes les commandes dans un état donné » :
| field | value | note |
|---|---|---|
| GSI-PK | order_state | "PENDING" | "PROCESSING" | "SHIPPED" | "CANCELED" |
| GSI-SK | SK |
Quatre valeurs de clé de partition possibles. Pendant une vente flash, presque chaque
nouvelle commande atterrit dans order_state = "PENDING". Chacune de ces écritures frappe
la même partition de GSI.
Cette partition a une limite de débit par partition, et tu viens d'y diriger toute ta tempête d'écritures.
La table de base va bien. La partition de GSI PENDING est en feu. DynamoDB throttle le
PutItem de la table de base pour protéger l'index.
Le flux qui te mord
Voici le chemin de la contre-pression — écriture de base équilibrée, écriture d'index concentrée :
Le throttling remonte à contre-courant : une partition de GSI surchargée rejette l'écriture de la table de base qui l'alimentait.
Lis l'exception, pas ton intuition
Le type d'exception te dit exactement quel plafond tu as atteint. Le ResourceArn nomme
le GSI ; l'opération throttlée reste l'écriture de la table.
| Mode | Code de raison | Ce qui a manqué |
|---|---|---|
| Provisionné | IndexWriteProvisionedThroughputExceeded | Capacité d'écriture provisionnée du GSI |
| Les deux | IndexWriteKeyRangeThroughputExceeded | Une seule partition de GSI surchargée |
| À la demande | IndexWriteMaxOnDemandThroughputExceeded | Plafond max à la demande configuré du GSI |
| À la demande | IndexWriteAccountLimitExceeded | Limite de débit du compte/de la région |
Source : Understanding GSI write throttling and back pressure.
La raison KeyRange est le signe révélateur du cas de partition surchargée ci-dessus : la
capacité globale du GSI peut paraître correcte alors qu'une seule plage de clés est saturée.
Comment le corriger
Donne de l'air au GSI. La cause la plus simple est le sous-provisionnement. Un GSI a sa propre capacité de lecture et d'écriture, entièrement séparée de la table — voir GSI vs LSI.
Si tu as provisionné la table généreusement mais laissé le GSI à sec, augmente la capacité d'écriture du GSI (ou son max à la demande).
Corrige la clé de partition. La capacité ne sauvera pas une clé à faible cardinalité — tu ne peux pas sur-provisionner ta sortie d'une seule partition surchargée. Choisis une clé de partition de GSI qui répartit.
Compose-la : order_state#shard où shard est un petit suffixe aléatoire, ou intègre la
date (PENDING#2026-06-23). Les écritures se répartissent entre les partitions et tu peux
toujours Query un état en interrogeant les shards.
Projette moins d'attributs. Chaque écriture de GSI copie les attributs projetés. Une
projection KEYS_ONLY ou un INCLUDE serré signifie des écritures d'index plus petites et
moins de pression que ALL. Ne projette pas ce que tu ne liras jamais depuis l'index.
Supprime le GSI s'il ne sert qu'au reporting. Si « les commandes par état » est une question d'administration occasionnelle, pas un chemin critique, un scan périodique avec filtre peut battre un index en permanence surchargé — pèse ça face à Query vs Scan.
Quand tu interroges cet index, l'
Expression Builder écrit la
KeyConditionExpression pour toi — par ex. #s = :state AND begins_with(SK, :prefix) —
avec les noms et valeurs échappés correctement :
KeyConditionExpression "#s = :state AND begins_with(SK, :prefix)"
ExpressionAttributeNames { "#s": "order_state" }
ExpressionAttributeValues { ":state": { "S": "PENDING" }, ":prefix": { "S": "ORD#2026-06-23" } }
Le piège à retenir
L'instinct relationnel — « les index ne ralentissent qu'un peu les écritures » — ne se transpose pas. Un GSI DynamoDB est une dépendance de débit, pas une structure passive. Sous-dimensionne-le ou choisis une clé qui s'agglutine, et il exerce une contre-pression sur la table qu'il sert.
Surveille ConsumedWriteCapacityUnits et WriteThrottleEvents sur la dimension du GSI, pas
seulement sur celle de la table, et utilise Contributor Insights pour trouver les clés
surchargées.
Étapes suivantes
- GSI vs LSI — pourquoi un GSI a sa propre capacité et une clé de partition différente.
- Single-table design — surcharger un seul GSI pour servir plusieurs patterns sans multiplier les index surchargés.
- Query vs Scan — quand un index ne vaut pas son coût d'écriture.
Essaie DynoTable pour inspecter chaque GSI de tes tables — schéma de clés et nombre d'items — et interroger tes index avant qu'une vente ne les fasse virer au rouge.