Strutturazione dei dati per una coerenza elevata

Nota: gli sviluppatori che creano nuove applicazioni sono vivamente incoraggiati a utilizzare la libreria client NDB, che presenta diversi vantaggi rispetto alla libreria client, ad esempio la memorizzazione automatica delle cache tramite l'API Memcache. Se attualmente utilizzi la libreria client DB precedente, consulta la Guida alla migrazione da DB a NDB

Datastore fornisce alta disponibilità, scalabilità e durabilità distribuendo i dati su molte macchine e utilizzando la replica sincrona in un'ampia area geografica. Tuttavia, il design presenta un compromesso, ovvero la velocità effettiva di scrittura per ogni singolo gruppo di entità è limitata a circa un commit al secondo e sono presenti limitazioni per le query o le transazioni che coprono più gruppi di entità. In questa pagina vengono descritte in dettaglio queste limitazioni e vengono descritte le best practice per strutturare i dati in modo da garantire un'elevata coerenza pur rispettando i requisiti di velocità effettiva di scrittura dell'applicazione.

Le letture a elevata coerenza restituiscono sempre dati attuali e, se eseguite all'interno di una transazione, sembrano provenire da un singolo snapshot coerente. Tuttavia, le query devono specificare un filtro predecessore per garantire elevata coerenza o partecipazione a una transazione. Le transazioni possono includere al massimo 25 gruppi di entità. Le letture a coerenza finale non hanno queste limitazioni e, in molti casi, sono adeguate. L'utilizzo di letture a coerenza finale ti consente di distribuire i dati tra un numero maggiore di gruppi di entità, in modo da ottenere una maggiore velocità effettiva di scrittura eseguendo i commit in parallelo su gruppi di entità diversi. Tuttavia, devi comprendere le caratteristiche di letture a coerenza finale per determinare se sono adatte alla tua applicazione:

  • I risultati di queste letture potrebbero non riflettere le ultime transazioni. Ciò può accadere perché queste letture non assicurano che la replica su cui sono in esecuzione sia aggiornata. Utilizzano invece tutti i dati disponibili su quella replica al momento dell'esecuzione della query. La latenza di replica è quasi sempre inferiore a pochi secondi.
  • Una transazione impegnata su più entità potrebbe sembrare essere stata applicata ad alcune entità e non ad altre. Tuttavia, tieni presente che una transazione non sarà mai stata applicata parzialmente a una singola entità.
  • I risultati della query possono includere entità che non avrebbero dovuto essere incluse secondo i criteri di filtro e potrebbero escludere entità che avrebbero dovuto essere incluse. Questo può accadere perché gli indici possono essere letti in una versione diversa rispetto all'entità stessa.

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

import webapp2
from google.appengine.ext import db

class Guestbook(webapp2.RequestHandler):
  def post(self):
    greeting = Greeting()
    ...

Quindi esegue una query sull'entità Greeting per i dieci saluti più recenti.

import webapp2
from google.appengine.ext import db

class MainPage(webapp2.RequestHandler):
  def get(self):
    self.response.out.write('<html><body>')
    greetings = db.GqlQuery("SELECT * "
                            "FROM Greeting "
                            "ORDER BY date DESC LIMIT 10")

Tuttavia, poiché utilizzi una query non predecessore, la replica utilizzata per eseguire la query in questo schema potrebbe non aver visto il nuovo saluto al momento dell'esecuzione della query. Tuttavia, nel giro di pochi secondi saranno disponibili quasi tutte le operazioni di scrittura per le query senza predecessori. Per molte applicazioni, una soluzione che fornisce i risultati di una query non predecessore nel contesto delle modifiche dell'utente corrente sarà solitamente sufficiente per rendere completamente accettabili queste latenze di replica.

Se per l'applicazione è importante un'elevata coerenza, un approccio alternativo è la scrittura di entità con un percorso predecessore che identifichi la stessa entità base in tutte le entità che devono essere lette in una singola query predecessore a elevata coerenza:

import webapp2
from google.appengine.ext import db

class Guestbook(webapp2.RequestHandler):
  def post(self):
    guestbook_name=self.request.get('guestbook_name')
    greeting = Greeting(parent=guestbook_key(guestbook_name))
    ...

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

import webapp2
from google.appengine.ext import db

class MainPage(webapp2.RequestHandler):
  def get(self):
    self.response.out.write('<html><body>')
    guestbook_name=self.request.get('guestbook_name')

    greetings = db.GqlQuery("SELECT * "
                            "FROM Greeting "
                            "WHERE ANCESTOR IS :1 "
                            "ORDER BY date DESC LIMIT 10",
                            guestbook_key(guestbook_name))

Questo approccio raggiunge una elevata coerenza scrivendo a 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 la tua applicazione ha maggiori probabilità di avere un utilizzo di scrittura più elevato, potresti prendere in considerazione l'utilizzo di altri mezzi: ad esempio, puoi inserire i post recenti in una memcache con una scadenza e visualizzare un mix di post recenti di memcache e Datastore oppure 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 esegue la pubblicazione nella tua applicazione. Ricorda che, se esegui un get, una query di predecessore o un'operazione all'interno di una transazione, vedrai sempre i dati scritti più di recente.