Strutturazione dei dati per una coerenza elevata

Datastore offre alta disponibilità, scalabilità e durabilità distribuendo i dati su più macchine e utilizzando la replica sincrona su un'ampia area geografica. Tuttavia, questa struttura presenta un compromesso, ovvero la velocità effettiva di scrittura per ogni singolo gruppo di entità è limitata a circa un commit al secondo e sono previste limitazioni alle query o alle transazioni che coprono più gruppi di entità. Questa pagina descrive queste limitazioni in modo più dettagliato e le best practice per strutturare i dati in modo da supportare un'elevata coerenza pur soddisfacendo i requisiti di velocità effettiva di scrittura della tua applicazione.

Le letture a elevata coerenza restituiscono sempre i dati correnti e, se eseguite all'interno di una transazione, sembrano provenire da un unico snapshot coerente. Tuttavia, le query devono specificare un filtro dei predecessori per essere fortemente coerenti o per partecipare a una transazione e le transazioni possono coinvolgere al massimo 25 gruppi di entità. Le letture costantemente coerenti non presentano queste limitazioni e sono adeguate in molti casi. L'utilizzo di letture a coerenza finale può consentirti di distribuire i dati tra un numero maggiore di gruppi di entità, consentendoti di ottenere una maggiore velocità effettiva di scrittura eseguendo i commit in parallelo su diversi gruppi di entità. Tuttavia, devi comprendere le caratteristiche delle letture eventualmente coerenti per determinare se sono adatte alla tua applicazione:

  • I risultati di queste letture potrebbero non riflettere le transazioni più recenti. Questo può accadere perché queste letture non assicurano che la replica su cui sono in esecuzione sia aggiornata. Utilizzano invece qualsiasi dato disponibile su quella replica al momento dell'esecuzione della query. La latenza di replica è quasi sempre inferiore a pochi secondi.
  • Potrebbe sembrare che una transazione impegnata che interessa più entità sia stata applicata ad alcune di queste e non ad altre. Tuttavia, tieni presente che una transazione non sembrerà mai stata applicata parzialmente all'interno di una singola entità.
  • I risultati della query possono includere entità che non avrebbero dovuto essere incluse in base ai criteri di filtro e potrebbero escludere entità che avrebbero dovuto essere incluse. Questo può accadere perché gli indici potrebbero essere letti con una versione diversa da quella in cui viene letta l'entità stessa.

Per comprendere come strutturare i dati per un'elevata coerenza, confronta due approcci diversi per una semplice applicazione guestbook. Il primo approccio crea una nuova entità base per ogni entità creata:

g := Greeting{ /* ... */ }
key := datastore.NewIncompleteKey(ctx, "Greeting", nil)

Quindi esegue una query sul tipo di entità Greeting per trovare i dieci saluti più recenti.

q := datastore.NewQuery("Greeting").Order("-Date").Limit(10)

Tuttavia, poiché utilizzi una query non predecessore, la replica utilizzata per eseguire la query in questo schema potrebbe non aver rilevato il nuovo saluto prima dell'esecuzione della query. Tuttavia, quasi tutte le scritture saranno disponibili per le query non sui predecessori entro pochi secondi dal commit. Per molte applicazioni, una soluzione che fornisca i risultati di una query non predecessore nel contesto delle modifiche dell'utente corrente di solito è sufficiente per rendere tali latenze di replica completamente accettabili.

Se per la tua applicazione è importante avere una coerenza elevata, un approccio alternativo consiste nel scrivere entità con un percorso predecessore che identifica la stessa entità base in tutte le entità che devono essere lette in una singola query predecessore a elevata coerenza:

g := Greeting{ /* ... */ }
key := datastore.NewIncompleteKey(ctx, "Greeting", guestbookKey(ctx))

Potrai quindi eseguire una query da predecessore a elevata coerenza all'interno del gruppo di entità identificato dall'entità base comune:

q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(ctx)).Order("-Date").Limit(10)

Questo approccio raggiunge una elevata coerenza scrivendo in un singolo gruppo di entità per guestbook, ma limita anche le modifiche al guestbook a non più di una scrittura al secondo (il limite supportato per i gruppi di entità). Se è probabile che la tua applicazione riscontri un utilizzo di scrittura più elevato, potresti prendere in considerazione l'uso di altri mezzi: ad esempio, potresti inserire i post recenti in un memcache con una scadenza e visualizzare una combinazione di post recenti di memcache e Datastore oppure puoi memorizzarli nella cache in un cookie, inserire uno stato nell'URL o qualcosa di completamente diverso. L'obiettivo è trovare una soluzione di memorizzazione nella cache che fornisca i dati per l'utente corrente per il periodo di tempo in cui l'utente sta pubblicando un post sulla tua applicazione. Ricorda che, se esegui una richiesta get, una query dei predecessori o una qualsiasi operazione all'interno di una transazione, visualizzerai sempre i dati scritti più recenti.