API Datastore App Engine per servizi in bundle legacy

Nota: Gli sviluppatori che creano nuove applicazioni sono vivamente incoraggiati a utilizzare il Libreria client NDB, che offre numerosi vantaggi rispetto a questa libreria client, come la memorizzazione automatica nella cache delle entità tramite API. Se al momento utilizzi la libreria client DB precedente, leggi il Guida alla migrazione da DB a NDB

Questo documento descrive il modello dei dati per gli oggetti archiviati in Datastore, la struttura delle query con l'API e l'elaborazione delle transazioni.

Entità

Gli oggetti in Datastore sono denominati entità. Un'entità ha una o più proprietà denominate, ciascuna delle quali può avere uno o più valori. I valori delle proprietà possono appartenere a diversi tipi di dati, tra cui numeri interi e in virgola mobile, stringhe, date e dati binari. Una query su una proprietà con più valori verifica se uno qualsiasi dei valori soddisfa i criteri della query. Ciò rende queste proprietà utili per il test di appartenenza.

Tipi, chiavi e identificatori

Ogni entità Datastore è di un tipo particolare,che classifica l'entità ai fini delle query; ad esempio, un'applicazione per le risorse umane potrebbe rappresentare ogni dipendente di un'azienda con un'entità di tipo Employee. Inoltre, ogni entità ha la propria chiave, che la identifica in modo univoco. La chiave è costituita dai seguenti componenti:

  • Il tipo di entità
  • Un identificatore, che può essere
      .
    • una stringa nome chiave
    • un ID intero
  • Un percorso predecessore facoltativo che individua l'entità all'interno della gerarchia di Datastore

L'identificatore viene assegnato al momento della creazione dell'entità. Poiché fa parte della chiave dell'entità, è associato in modo permanente all'entità e non può essere modificato. Può essere assegnato in due modi:

  • L'applicazione può specificare la propria stringa del nome della chiave per l'entità.
  • Puoi fare in modo che Datastore assegni automaticamente all'entità un ID numerico intero.

Percorsi predecessori

Le entità in Cloud Datastore formano uno spazio strutturato gerarchicamente simile la struttura di directory di un file system. Quando crei un'entità, puoi facoltativamente designare un'altra entità come parent;; la nuova entità è una figlio dell'entità padre (tieni presente che, a differenza di un file system, l'entità padre non è necessario che esista effettivamente). Un'entità senza un elemento padre è una root . L'associazione tra un'entità e la relativa entità è permanente e non può essere modificato dopo la creazione dell'entità. Cloud Datastore non assegnerà mai lo stesso ID numerico a due entità con lo stesso elemento padre, oppure a due radici (quelle senza un elemento padre).

L'elemento principale di un'entità, il padre dell'elemento padre e così via in modo ricorsivo antenati; i relativi figli, figli dei bambini e così via, sono i discendenti. Un'entità base e tutti i suoi discendenti appartengono a all'interno dello stesso gruppo di entità. La sequenza di entità che inizia con una radice dell'entità e procedere da principale a figlio, portando a una determinata entità, costituiscono il percorso predecessore dell'entità. La chiave completa che identifica l'entità è composta da una sequenza di coppie tipo-identificatore che specifica percorso predecessore e termina con quelli dell'entità stessa:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

Per unentità base, il percorso predecessore è vuoto e la chiave è costituita esclusivamente da tipo e identificatore dell'entità:

[Person:GreatGrandpa]

Questo concetto è illustrato dal seguente diagramma:

Mostra la relazione tra l'entità base e quella secondaria
  entità nel gruppo di entità

Query e indici

Oltre a recuperare le entità da Datastore direttamente chiave, un'applicazione può eseguire query per recuperarle in base ai valori delle loro proprietà. La query opera su entità di una determinata kind; può specificare filtri sulle entità valori di proprietà, chiavi e predecessori e può restituire zero più entità come risultati. Una query può anche specificare ordinare per sequenziare i risultati in base ai valori delle proprietà. I risultati includono tutte entità con almeno (possibilmente null) valore per ogni proprietà menzionata nei filtri e nell'ordinamento e le cui i valori delle proprietà soddisfano tutti i criteri di filtro specificati. La query può restituire intere entità, entità previste, o semplicemente entità . chiavi.

Una query tipica include quanto segue:

  • Un tipo di entità a cui si applica la query
  • Zero o più filtri in base al set di dati delle entità, valori delle proprietà, chiavi e predecessori
  • Zero o più ordinare per sequenziare i risultati
Quando viene eseguita, la query recupera tutte le entità del tipo specificato che soddisfano tutti i filtri specificati, ordinati nell'ordine specificato. Le query vengono eseguite in sola lettura.

Nota: per risparmiare memoria e migliorare una query, quando possibile, deve specificare un limite al numero vengono restituiti i risultati.

Una query può anche includere un filtro predecessore che limita i risultati al solo gruppo di entità discendente da un predecessore specificato. Questa è nota come query predecessore. Per impostazione predefinita, le query dei predecessori restituiscono risultati a elevata coerenza, che sono garantiti essere aggiornati con le ultime modifiche apportate al e i dati di Google Cloud. Al contrario, le query non predecessori possono coprire l'intero Datastore anziché un singolo gruppo di entità, ma sono solo a coerenza finale e possono restituire risultati inattivi. Se per la tua applicazione è importante l'elevata coerenza, potresti dover tenerne conto quando struttura i dati, posizionando entità correlate nello stesso gruppo di entità in modo che possano essere recuperate con una query predecessore anziché con una query non predecessore. consulta la sezione Strutturare i dati per una elevata coerenza per ulteriori informazioni.

App Engine predefinisce un indice semplice su ogni proprietà di un'entità. Un'applicazione App Engine può definire ulteriori indici personalizzati in un indice di configurazione del deployment denominato index.yaml. Il server di sviluppo aggiunge suggerimenti a questo file quando rileva query che non possono essere eseguite con gli indici esistenti. Puoi ottimizzare manualmente gli indici modificando il file prima di caricare l'applicazione.

Nota: Il meccanismo di query basato su indice supporta un'ampia gamma di query ed è adatto a per la maggior parte delle applicazioni. Tuttavia, non supporta alcuni tipi di query comuni in altre tecnologie di database: in particolare, join e query aggregate non sono supportate nel motore di query di Datastore. Consulta Pagina Query Datastore per conoscere i limiti delle query Datastore.

Transazioni

Ogni tentativo di inserire, aggiornare o eliminare un'entità avviene nel contesto di una transazione. Una singola transazione può includere un numero illimitato di operazioni di questo tipo. Per mantenere la coerenza dei dati, la transazione garantisce che tutte le operazioni che contiene vengano applicate a Datastore come unità o, in caso di esito negativo di una qualsiasi operazione, che nessuna di esse venga applicata.

È possibile eseguire più azioni su un'entità all'interno di una singola transazione. Ad esempio, per incrementare un campo contatore in un oggetto, è necessario leggere il valore del contatore, calcolare il nuovo valore e quindi memorizzarlo di nuovo. Senza una transazione, è possibile che un altro processo incrementi il contatore tra il momento in cui leggi il valore e quello in cui lo aggiorni, facendo sì che l'applicazione sovrascriva il valore aggiornato. Eseguire le operazioni di lettura, calcolo e scrittura in un'unica transazione garantisce che nessun altro processo possa interferire con l'incremento.

Transazioni e gruppi di entità

All'interno di una transazione sono consentite solo le query dei predecessori, ovvero ogni query transazionale deve essere limitata a un singolo gruppo di entità. La transazione stessa può essere applicata a più entità che possono appartenere a un singolo gruppo di entità o (in caso di transazione tra gruppi) fino a un massimo di venticinque gruppi di entità diversi.

Datastore utilizza la contemporaneità ottimista per gestire le transazioni. Quando due o più transazioni tentano di modificare contemporaneamente lo stesso gruppo di entità (aggiornando le entità esistenti o creandone di nuove), la prima transazione per cui eseguire il commit avrà esito positivo e tutte le altre non andranno a buon fine al momento del commit. Queste altre transazioni possono essere ritentate con i dati aggiornati. Tieni presente che questo limita il numero di scritture simultanee che puoi eseguire su qualsiasi entità in un determinato gruppo di entità.

Transazioni tra gruppi

Una transazione su entità appartenenti a diversi gruppi di entità viene chiamata transazione tra gruppi (XG). La transazione può essere applicata a un massimo di venticinque gruppi di entità e avrà esito positivo purché nessuna transazione simultanea riguardi uno dei gruppi di entità a cui si applica. Questo ti offre una maggiore flessibilità nell'organizzazione dei dati, perché non sei costretto a inserire dati diversi sotto lo stesso predecessore solo per eseguire scritture atomiche.

Come nel caso di una transazione a gruppo singolo, non puoi eseguire una query non precedente in una transazione XG. Tuttavia, puoi eseguire query predecessore su gruppi di entità separati. Le query non transazionali (non predecessori) potrebbero visualizzare tutti, alcuni o nessuno dei risultati di una transazione di cui è stato eseguito il commit in precedenza. Per informazioni di base su questo problema, consulta Scritture e visibilità dei dati in Datastore. Tuttavia, queste query non transazionali hanno maggiori probabilità di ottenere i risultati di una transazione XG parzialmente impegnata rispetto a quelli di una transazione con un singolo gruppo con impegno parziale.

Una transazione XG che interessa un solo gruppo di entità ha esattamente le stesse prestazioni e lo stesso costo di una transazione XG singola, non XG. In una transazione XG che interessa più gruppi di entità, le operazioni hanno lo stesso costo di come se fossero state eseguite in una transazione non XG, ma potrebbero riscontrare una latenza maggiore.

Scritture e visibilità dei dati di Datastore

I dati vengono scritti in Datastore in due fasi:

  1. Nella fase di commit, i dati dell'entità vengono registrati nei log delle transazioni di gran parte delle repliche e tutte le repliche in cui non sono stati registrati sono contrassegnate come non aggiornate.
  2. La fase di applicazione avviene in modo indipendente in ogni replica ed è composta da due azioni eseguite in parallelo:
    • I dati dell'entità sono scritti in quella replica.
    • Le righe di indice dell'entità sono scritte in questa replica. Tieni presente che questa operazione può richiedere più tempo rispetto alla scrittura dei dati stessi.

L'operazione di scrittura restituisce immediatamente dopo la fase di commit e la fase di applicazione, quindi si svolge in modo asincrono, possibilmente in momenti diversi in ogni replica, e magari con ritardi di alcune centinaia di millisecondi o più dal completamento della fase di commit. Se si verifica un errore durante la fase di commit, esistono nuovi tentativi automatici. ma se gli errori persistono, Datastore restituisce un messaggio di errore che l'applicazione riceve come eccezione. Se la fase di commit ha esito positivo, ma l'applicazione non riesce in una determinata replica, viene eseguito il rollback dell'applicazione al completamento nella replica quando si verifica una delle seguenti condizioni:

  • Le scansioni periodiche di Datastore verificano la presenza di job di commit non completati e li applicano.
  • Alcune operazioni (get, put, delete e query predecessore) che utilizzano il gruppo di entità interessato causano il completamento delle modifiche di cui è stato eseguito il commit ma non ancora applicate nella replica in cui vengono eseguite prima di procedere con la nuova operazione.

Questo comportamento di scrittura può avere diverse implicazioni su come e quando i dati sono visibili all'applicazione in diverse parti delle fasi di commit e applicazione:

  • Se un'operazione di scrittura segnala un errore di timeout, non è possibile determinare (senza tentare di leggere i dati) se l'operazione è riuscita o meno.
  • Poiché le query Datastore e dei predecessori applicano le modifiche in sospeso alla replica su cui vengono eseguite, queste operazioni prevedono sempre una visione coerente di tutte le precedenti transazioni riuscite. Ciò significa che è garantito che un'operazione get (la ricerca di un'entità aggiornata in base alla sua chiave) visualizzi la versione più recente dell'entità.
  • Le query non predecessori potrebbero restituire risultati inattivi perché potrebbero essere in esecuzione su una replica su cui non sono ancora state applicate le ultime transazioni. Questo può verificarsi anche se è stata eseguita un'operazione per la quale è garantito che vengano applicate transazioni in sospeso, poiché la query potrebbe essere eseguita su una replica diversa rispetto all'operazione precedente.
  • I tempi delle modifiche simultanee possono influire sui risultati delle query non predecessori. Se un'entità soddisfa una query inizialmente, ma in seguito viene modificata in modo da non farlo più, l'entità può comunque essere inclusa nel set di risultati della query se le modifiche non erano ancora state applicate agli indici nella replica su cui è stata eseguita la query.=

Statistiche Datastore

Datastore gestisce le statistiche sui dati archiviati per un'applicazione, come quante entità esistono di un determinato tipo o quanto spazio viene utilizzato in base ai valori delle proprietà di un determinato tipo. Puoi visualizzare queste statistiche nel Console Google Cloud Pagina Dashboard di Datastore. Puoi anche utilizzare Datastore API per accedere a questi valori in modo programmatico dall'interno dell'applicazione eseguire query per entità con nomi specifici; consulta le statistiche del datastore in Python 2 per ulteriori informazioni.

Esempio di Datastore in Python 2

Nell'API Python, un model descrive un tipo di entità, inclusi i tipi e la configurazione delle sue proprietà. Un'applicazione definisce un modello utilizzando una classe Python, con attributi di classe che descrivono le proprietà. Il nome della classe diventa il nome del tipo di entità. Le entità del tipo specificato sono rappresentate da istanze della classe di modello, con gli attributi di istanza che rappresentano i valori delle proprietà. Per creare una nuova entità, devi creare un oggetto della classe desiderata, impostarne gli attributi e quindi salvarlo (chiamando un metodo come put()):

import datetime
from google.appengine.ext import db
from google.appengine.api import users


class Employee(db.Model):
  name = db.StringProperty(required=True)
  role = db.StringProperty(required=True,
                           choices=set(["executive", "manager", "producer"]))
  hire_date = db.DateProperty()
  new_hire_training_completed = db.BooleanProperty(indexed=False)
  email = db.StringProperty()


e = Employee(name="John",
             role="manager",
             email=users.get_current_user().email())
e.hire_date = datetime.datetime.now().date()
e.put()

L'API Datastore fornisce due interfacce per le query: un'interfaccia dell'oggetto query e un linguaggio di query di tipo SQL denominato GQL. Una query restituisce le entità sotto forma di istanze delle classi del modello:

training_registration_list = ["Alfred.Smith@example.com",
                              "jharrison@example.com",
                              "budnelson@example.com"]
employees_trained = db.GqlQuery("SELECT * FROM Employee WHERE email IN :1",
                                training_registration_list)
for e in employees_trained:
  e.new_hire_training_completed = True
  db.put(e)