Pourquoi un GSI throttle les écritures de la table de base dans DynamoDB
Tu écris dans ta table. L'écriture échoue avec une exception de débit — mais l'exception nomme un Global Secondary Index, pas la table. La table a de la capacité de reste.
En venant de SQL, c'est absurde : un index secondaire ne peut pas bloquer un
INSERT. Dans DynamoDB il le peut, et le mécanisme s'appelle la back-pressure 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 est aussi répliquée vers chaque GSI, et si une partition de GSI ne peut pas absorber sa part, DynamoDB applique une back-pressure pour empêcher l'index de prendre un retard permanent. Ainsi une clé de GSI sous-provisionnée ou à faible cardinalité devient un plafond strict sur ton débit d'écriture de table de base.
- Une écriture sur 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 permanent. (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 partition d'index chaude 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 sur la table. - La solution, c'est la capacité ou la conception de clé, pas des boucles de retry — augmente le débit du GSI, ou choisis une clé de partition de GSI qui répartit.
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éploie en N écritures physiques — la
table plus chaque index.
Cette réplication n'est ni gratuite ni optionnelle. Le GSI doit suivre, sinon l'index dérive davantage de la table à chaque opération.
Pour stopper cette dérive, DynamoDB applique la back-pressure : il throttle l'écriture source pour que l'index ne devienne jamais indéfiniment périmé.
Ainsi la capacité d'écriture du GSI est un plafond strict sur ton débit d'écriture de la table de base — même si 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 forte cardinalité, donc
les écritures de commandes se répartissent équitablement sur les partitions de base.
Pas de clé chaude, plein de capacité.
Maintenant tu ajoutes un GSI pour répondre à « montre-moi chaque commande 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 de viser toute ta tempête d'écritures dessus.
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 back-pressure — écriture de base équilibrée, écriture d'index concentrée :
Le throttle remonte à l'envers : une partition de GSI chaude rejette l'écriture de la table de base qui l'a alimentée.
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 |
| Provisionné | IndexWriteKeyRangeThroughputExceeded | Une seule partition de GSI chaude |
| On-demand | IndexWriteMaxOnDemandThroughputExceeded | Plafond on-demand max configuré du GSI |
| On-demand | IndexWriteAccountLimitExceeded | Limite de débit du compte/de la région |
Source : Understanding GSI write throttling and back pressure.
La raison KeyRange est l'indice révélateur du cas de partition chaude ci-dessus : la
capacité globale du GSI peut sembler correcte tandis qu'une seule plage de clé est
saturée.
Comment corriger ça
Donne de l'espace au GSI. La cause la plus simple est le sous-provisionnement. Un GSI a sa propre capacité de lecture et d'écriture, totalement séparée de la table — voir GSI vs LSI.
Si tu as provisionné la table généreusement et laissé le GSI maigre, augmente la capacité d'écriture du GSI (ou son max on-demand).
Corrige la clé de partition. La capacité ne sauvera pas une clé à faible cardinalité — tu ne peux pas sur-provisionner une seule partition chaude. 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 sur les partitions et tu
fais toujours un Query d'un état en requêtant 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 « commandes par état » est une question d'admin occasionnelle, pas un chemin chaud, un scan périodique avec un filtre peut battre un index en permanence chaud — pèse ça face à Query vs Scan.
Quand tu requêtes cet index, l'
Expression Builder écrit le
KeyConditionExpression pour toi — p. ex. #s = :state AND begins_with(SK, :prefix) —
avec les noms et les valeurs échappés correctement :
KeyConditionExpression "#s = :state"
ExpressionAttributeNames { "#s": "order_state" }
ExpressionAttributeValues { ":state": { "S": "PENDING" } }
Le piège à retenir
L'instinct relationnel — « les index ne ralentissent les écritures qu'un peu » — ne se transfère 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 fera back-pressure sur la table qu'il sert.
Surveille le ConsumedWriteCapacityUnits et les ThrottledRequests du GSI par
partition, pas seulement ceux de la table.
Étapes suivantes
- GSI vs LSI — pourquoi un GSI a sa propre capacité et une clé de partition différente.
- Single-table design — surcharger un GSI pour servir plusieurs modes sans multiplier les index chauds.
- Query vs Scan — quand un index ne vaut pas son coût d'écriture.
Essaie DynoTable pour observer la capacité consommée et les événements de throttle d'un GSI sur tes propres tables avant qu'une vente ne les fasse virer au rouge.