Intermedio8 min di lettura

Perché un DynamoDB GSI alla fine è coerente

Scrivi un elemento, esegui immediatamente una query su un indice secondario globale e ottieni niente in risposta — anche se la scrittura è riuscita e una tabella base GetItem restituisce l'articolo correttamente.

Niente è rotto. Hai centrato la proprietà più sorprendente di GSI: ogni lettura di un GSI è eventualmente consistente. C'è una breve finestra dopo la scrittura del dove l'indice non ha ancora raggiunto.

Alla fine DynamoDB GSI sono coerenti?

Sì, ogni lettura di un indice secondario globale alla fine è coerente, con no modo per rinunciare. La tua scrittura si impegna prima nella tabella di base, quindi si propaga in modo asincrono rispetto all'indice, quindi arilasciato subito dopo una scrittura può restituisce righe obsolete o mancanti. DynamoDB non offre alcun flag ConsistentRead per un GSI.

  • A GSI è una tabella separata, replicata in modo asincrono: i tuoi commit di scrittura prima alla tabella di base, quindi si propaga all'indice.
  • Non esiste alcun flag ConsistentRead per un GSI. A differenza della tabella di base, non è possibile forzare una lettura forte per colmare il divario.
  • Leggi le tue scritture dalla tabella di base, non da GSI. Hai già ilsubito dopo una scrittura.
  • Applica l'unicità con una scrittura condizionale, non con una query GSI. The il divario di propagazione diventa un "è stata presa?" partecipare a una gara.

Il sintomo: un'iscrizione che "non riesce a trovare se stessa"

Prendi una tabella "Membri" per un servizio di account utente. La tabella di base è codificata da un ID interno, ma gli utenti accedono tramite e-mail, quindi esiste una ricerca e-mail GSI:

Members (base table)
PKSKemaildisplayName
ACC#a1f9cPROFILEada@northwind.testAda L.
EmailIndex (GSI)
GSI1PKGSI1SK
ada@northwind.testACC#a1f9c

Il flusso di registrazione fa due cose consecutive: PutItem il nuovo membro, quindi Query EmailIndex WHERE GSI1PK = "ada@northwind.test" per controllare nessun altro ha rivendicato quell'indirizzo e caricare il profilo.

Esegui queste due chiamate a pochi millisecondi di distanza e Query può restituire zero elementi. Fallo di nuovo un secondo dopo e la riga è lì. La scrittura non è fallita

  • l'indice non era ancora stato aggiornato.

Perché ciò accade: GSI vengono replicati in modo asincrono

Una GSI è una tabella separata, gestita internamente con le proprie partizioni e i suoi proprio schema di chiavi. Non viene mantenuto all'interno della stessa transazione della tua scrittura della tabella base.

Quando PutItem, DynamoDB si impegna in modo duraturo nella tabella di base, riconosce il tuo scrive e quindi propaga in modo asincrono la modifica a ciascun GSI. Il AWS Documentazione GSI lo afferma chiaramente: GSIs supporta solo letture eventualmente coerenti.

Il ritardo di propagazione tra la scrittura della tabella di base e l'aggiornamento dell'indice è solitamente a frazione di secondo, ma non è garantito e non è limitato sotto carico. Progettare come se fosse delimitato è la trappola.

Questo ritardo è il compromesso del design originale di Dynamo. Il 2007 Documento su Amazon Dynamo ha scelto la disponibilità e la tolleranza delle partizioni rispetto alla coerenza elevata.

I GSI ereditano quel lignaggio. L'accoppiamento allentato è ciò che consente all'indice di ridimensionarsi e rimanere scrivibile indipendentemente dal tabella base.

EmailIndexTabella baseAppEmailIndexTabella baseAppasync propagationPutItem (new member)200 OKQuery by email0 items (stale)replicate changeQuery by email1 item (caught up)

Il divario tra "200 OK" e "modifica replica" è la finestra in cui si trova il file index leggere è obsoleto. Non esiste nessun flag di lettura coerente che lo chiuda.

A differenza della tabella di base, in cui si passa ConsistentRead = true per forzare a

GetItem/Query — a GSI rifiuta categoricamente questa opzione.

Un LSI può essere letto con forza perché condivide le partizioni della tabella base; vedere GSI vs LSI per il motivo per cui esiste questa distinzione.

Leggi il costo su GSI

Le query GSI fatturano il RCU su richiesta dell'indice in us-east-1 come qualsiasi altra lettura — 0,5 RCU per 4 KB blocco eventualmente coerente - e non puoi pagare il doppio per coerenza forte perché "ConsistentRead" viene rifiutato. Il ritardo di propagazione è gratuito; l'indice letto non lo è. Confronta i tassi di lettura della tabella base con quelli di GSI nel file calcolatore dei prezzi.

Una trappola più sottile: vecchi valori obsoleti, non solo la mancanza di nuovi

Il caso della riga mancante è quello ovvio. Il bug più silenzioso è leggere un file stantio valore precedente.

Supponiamo che Ada cambi la tua email da "ada@northwind.test" a "ada.l@northwind.test". Il la tabella base si aggiorna atomicamente, ma per un momento GSI può ancora restituire il vecchia voce di indice.

Una ricerca rispetto al nuovo valore fallisce, mentre il valore abbandonato viene comunque risolto.

Peggio ancora: se interroghi il GSI e rispondi in base a ciò che hai letto, puoi agire in base a valore che non esiste più. Tratta qualsiasi lettura GSI come un'istantanea che potrebbe ritardare la realtà.

Progetta attorno ad esso: non combatterlo

La finestra di propagazione è reale, quindi la correzione è architetturale, non una manopola per riprovare attivare/disattivare. Quattro modelli, più o meno in ordine di preferenza:

  1. Leggi le tue scritture dalla tabella di base. Subito dopo una scrittura sei già tieni la chiave primaria (ACC#a1f9c), quindi esegui un GetItem fortemente coerente su la tabella di base invece di interrogare GSI.

Il GSI è per il modello di accesso altro — "Ho un'e-mail, trova l'account" — non per confermare la scrittura che hai appena fatto.

  1. Applica l'unicità con un oggetto di guardia, non con GSI. Non fidarti mai di una query GSI per dimostrare che un'e-mail non è stata reclamata: il divario di propagazione la rende una gara due le iscrizioni simultanee possono perdere entrambe.

Scrivi invece un elemento di unicità dedicato inserito nell'e-mail stessa (PK = "EMAIL#ada@northwind.test") all'interno di un TransactWriteItems con un ConditionExpression di attributo_non_esiste(PK).

Le condizioni della tabella di base fortemente coerenti, applicate atomicamente, sono ciò che in realtà imporre l’unicità.

   TransactWriteItems:
     - Put member item    (PK = ACC#a1f9c, SK = PROFILE)
     - Put uniqueness item (PK = EMAIL#ada@northwind.test)
         ConditionExpression: attribute_not_exists(PK)

Se una seconda registrazione corre per lo stesso indirizzo, la tua condizione fallisce e il file interoviene rifiutato: no GSI, nessun ritardo di propagazione, nessuna doppia rivendicazione.

Crea e visualizza in anteprima la condizione "attribute_not_exists" con il file DynamoDB Expression Builder prima di collegarlo al codice.

  1. Tollerare il ritardo nella UX. Quando la lettura GSI è autenticamente lo strumento giusto (accedi tramite e-mail per un utente esistente), la finestra è inferiore al secondo e innocua — un resoconto consolidato propagato molto tempo fa.

Riservare il percorso della tabella di base fortemente coerente per il momento di lettura dopo scrittura solo.

  1. Rieseguire la query, non dare per scontato. Se un flusso di lavoro deve osservare un nuovo elemento il GSI, tratta un risultato vuoto come "non ancora visibile", non "non esiste" e ripetere la query dopo un breve backoff.

Ma preferisci i modelli 1 e 2, che eliminano completamente le congetture.

Scopri tu stesso il divario di propagazione

Il modo più veloce per sviluppare l’intuizione è guardarla accadere. In DynoTable inserisci un elemento nella tabella di base e interroga immediatamente GSI in una seconda scheda.

Quindi su una tabella caricata occasionalmente vedrai l'indice che segue i dati di base guardalo convergere al prossimo aggiornamento.

Vedere il ritardo con i tuoi dati rende "leggi le tue scritture dalla base "la regola della tabella" si applica molto meglio di qualsiasi diagramma.

Insidie e passaggi successivi

  • Non eseguire il gate della logica su una lettura dopo la scrittura GSI. Controlla l'unicità, "ho scritto land" e i cicli di lettura-modifica-scrittura appartengono fortemente tabella base coerente.
  • Non cercare ConsistentRead su un GSI — non è consentito e genererà un errore.
  • Non modellare un modello di accesso come GSI quando la chiave di base già risponde. Servi una lettura dalla chiave primaria e salti completamente la finestra di propagazione.

Scegliere la forma giusta della chiave è l'intero gioco design a tabella singola; sapere quando un Query batte a Scan ti tiene fuori dall'indice in primo luogo (Query vs Scan).

Costruisci e metti alla prova la tua unicità ConditionExpression nel DynamoDB Expression Builder. Poi prova DynoTable per osservare le scritture della tabella base propagarsi a un GSI in tempo reale tempo e progetta le tue chiavi in modo che la finestra di coerenza finale non ti morda mai.

Aggiornato