Perché un GSI va in throttling sulle scritture della tabella base in DynamoDB
Scrivi sulla tua tabella. La scrittura fallisce con un'eccezione di throughput — ma l'eccezione nomina un Global Secondary Index, non la tabella. La tabella ha capacità libera.
Venendo da SQL, è un controsenso: un indice secondario non può bloccare un INSERT. In
DynamoDB può, e il meccanismo si chiama back-pressure del GSI.
Perché un GSI di DynamoDB va in throttling sulle scritture della tabella base?
DynamoDB mette in throttling la scrittura della tabella base perché ogni scrittura viene replicata anche su ciascun GSI e, se una partizione del GSI non riesce ad assorbire la propria quota, DynamoDB applica back-pressure per impedire all'indice di restare permanentemente indietro. Quindi una chiave di GSI sotto-provisioned o a bassa cardinalità diventa un tetto rigido sul tasso di scrittura della tua tabella base.
- Una scrittura sulla tabella base scrive anche su ogni GSI. Se un GSI non riesce ad assorbire la sua quota, DynamoDB va in throttling sulla scrittura della tabella base per impedire all'indice di restare permanentemente indietro. (AWS docs)
- Una tabella base uniforme non ti salva. Il GSI è partizionato dalla propria chiave.
Una chiave di GSI a bassa cardinalità (come
status) crea una hot index partition anche quando le scritture della tabella base sono distribuite perfettamente. - L'eccezione mente sulla vittima. Il
ResourceArnpunta al GSI; l'operazione che viene effettivamente messa in throttling è la tua scrittura sulla tabella. - La soluzione è capacità o design della chiave, non loop di retry — alza il throughput del GSI, oppure scegli una chiave di partizione del GSI che distribuisca.
Come una singola scrittura tocca l'indice
Un PutItem sulla tabella base non è una sola scrittura. DynamoDB replica gli attributi
proiettati dell'Item in ogni GSI in modo asincrono, con un modello eventualmente coerente.
Una scrittura logica si distribuisce in N scritture fisiche — tabella più ogni indice.
Quella replica non è gratuita né opzionale. Il GSI deve stare al passo, oppure l'indice si allontana ulteriormente dalla tabella a ogni operazione.
Per fermare quella deriva, DynamoDB applica back-pressure: mette in throttling la scrittura sorgente così l'indice non diventa mai illimitatamente obsoleto.
Quindi la write capacity del GSI è un tetto rigido sul tuo tasso di scrittura della tabella base — anche se non scrivi mai direttamente sul GSI.
Un esempio pratico: una tabella ordini
Diciamo che gestisci una tabella ordini. L'Item 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 |
Le scritture della tabella base sono in salute. CUST#... ha alta cardinalità, quindi le scritture degli ordini
si distribuiscono uniformemente tra le partizioni base. Nessuna hot key, capacità abbondante.
Ora aggiungi un GSI per rispondere a "mostrami ogni ordine in un dato stato":
| field | value | note |
|---|---|---|
| GSI-PK | order_state | "PENDING" | "PROCESSING" | "SHIPPED" | "CANCELED" |
| GSI-SK | SK |
Quattro possibili valori di chiave di partizione. Durante una vendita flash, quasi ogni nuovo ordine
finisce in order_state = "PENDING". Ognuna di quelle scritture colpisce la stessa
partizione del GSI.
Quella partizione ha un limite di throughput per partizione, e hai appena puntato l'intera tempesta di scritture su di essa.
La tabella base sta bene. La partizione PENDING del GSI è in fiamme. DynamoDB
mette in throttling il PutItem della tabella base per proteggere l'indice.
Il flusso che ti morde
Ecco il percorso del back-pressure — scrittura base bilanciata, scrittura indice concentrata:
Il throttling viaggia all'indietro: una hot partition del GSI rifiuta la scrittura della tabella base che l'ha alimentata.
Leggi l'eccezione, non l'istinto
Il tipo di eccezione ti dice esattamente quale tetto hai raggiunto. Il ResourceArn
nomina il GSI; l'operazione in throttling è comunque la scrittura sulla tabella.
| Modalità | Reason code | Cosa si è esaurito |
|---|---|---|
| Provisioned | IndexWriteProvisionedThroughputExceeded | Write capacity provisioned del GSI |
| Provisioned | IndexWriteKeyRangeThroughputExceeded | Una singola hot partition del GSI |
| On-demand | IndexWriteMaxOnDemandThroughputExceeded | Tetto on-demand max configurato del GSI |
| On-demand | IndexWriteAccountLimitExceeded | Confine di throughput account/region |
Fonte: Understanding GSI write throttling and back pressure.
Il motivo KeyRange è l'indizio rivelatore del caso hot-partition di sopra: la capacità
complessiva del GSI può sembrare a posto mentre un range di chiavi è saturo.
Come risolverlo
Dai spazio al GSI. La causa più semplice è il sotto-provisioning. Un GSI ha la propria read e write capacity, del tutto separata dalla tabella — vedi GSI vs LSI.
Se hai provisionato la tabella generosamente e lasciato il GSI scarno, alza la write capacity del GSI (o il suo max on-demand).
Correggi la chiave di partizione. La capacità non salverà una chiave a bassa cardinalità — non puoi out-provisionare una singola hot partition. Scegli una chiave di partizione del GSI che distribuisca.
Componila: order_state#shard dove shard è un piccolo suffisso casuale, oppure incorpora
la data (PENDING#2026-06-23). Le scritture si distribuiscono tra le partizioni e puoi comunque
fare una Query su uno stato interrogando gli shard.
Proietta meno attributi. Ogni scrittura del GSI copia gli attributi proiettati. Una
proiezione KEYS_ONLY o INCLUDE stretta significa scritture dell'indice più piccole e meno
pressione rispetto a ALL. Non proiettare ciò che non leggerai mai dall'indice.
Elimina il GSI se serve solo per reporting. Se "ordini per stato" è una domanda admin occasionale, non un percorso caldo, uno scan periodico con un filtro può battere un indice permanentemente caldo — pesalo contro Query vs Scan.
Quando interroghi quell'indice, l'
Expression Builder scrive la
KeyConditionExpression per te — es. #s = :state AND begins_with(SK, :prefix) —
con nomi e valori escapati correttamente:
KeyConditionExpression "#s = :state"
ExpressionAttributeNames { "#s": "order_state" }
ExpressionAttributeValues { ":state": { "S": "PENDING" } }
La trappola da ricordare
L'istinto relazionale — "gli indici rallentano le scritture solo un po'" — non si trasferisce. Un GSI di DynamoDB è una dipendenza di throughput, non una struttura passiva. Sottodimensionalo o scegli una chiave che si accalca, e fa back-pressure sulla tabella che serve.
Osserva i ConsumedWriteCapacityUnits e i ThrottledRequests del GSI per
partizione, non solo quelli della tabella.
Prossimi passi
- GSI vs LSI — perché un GSI ha la propria capacità e una chiave di partizione diversa.
- Single-table design — sovraccaricare un GSI per servire molti pattern senza moltiplicare gli indici caldi.
- Query vs Scan — quando un indice non vale il suo costo di scrittura.
Prova DynoTable per osservare la capacità consumata di un GSI e gli eventi di throttle sulle tue tabelle prima che una vendita le faccia diventare rosse.