Nota:gli sviluppatori che creano nuove applicazioni sono fortemente incoraggiati a utilizzare la libreria client NDB, che offre diversi vantaggi rispetto a questa libreria client, ad esempio la memorizzazione automatica nella cache delle entità tramite l'API Memcache. Se al momento utilizzi la libreria client DB precedente, leggi la guida alla migrazione da DB a NDB.
L'API Async Datastore consente di effettuare chiamate parallele e non bloccanti al datastore e di recuperare i risultati di queste chiamate in un secondo momento durante la gestione della richiesta. Questa documentazione descrive i seguenti aspetti dell'API Async Datastore:
Utilizzo del servizio Async Datastore
Con l'API Async Datastore, effettui chiamate Datastore utilizzando metodi del modulo *_async (ad esempio get_async() nel pacchetto google.appengine.ext.db). Il seguente esempio di codice mostra alcune semplici operazioni di datastore utilizzando i metodi asincroni:
from google.appengine.ext import db get_future = db.get_async(key) put_future = db.put_async(model) delete_future = db.delete_async(key) allocate_ids_future = db.allocate_ids_async(key, 10)
Queste funzioni eseguono le stesse operazioni delle versioni sincrone, tranne per il fatto che restituiscono immediatamente un oggetto asincrono che puoi utilizzare per ottenere il risultato reale in un secondo momento. Il seguente esempio di codice mostra come ottenere il risultato asincrono utilizzando get_result():
entity = get_future.get_result() key = put_future.get_result() range = allocate_ids_future.get_result() # Wait for the operation to complete without returning a value. # Exceptions that occurred in the call are thrown here. Calling # get_result() allows you to verify that the deletion succeeded. delete_future.get_result()
Nota:le eccezioni non vengono generate finché non chiami get_result(). La chiamata di questo metodo ti consente di verificare che l'operazione asincrona sia riuscita.
Utilizzo delle transazioni asincrone
Le chiamate API Async Datastore possono partecipare alle transazioni proprio come le chiamate sincrone. Ecco una funzione che regola lo stipendio di un Employee e scrive un'entità SalaryAdjustment aggiuntiva nello stesso gruppo di entità di Employee, il tutto in un'unica transazione.
def adjust_salary(employee_key, raise_ammount): def runner(): # Async call to lookup the Employee entity employee_entity_future = db.get_async(employee_key) # Create and put a SalaryAdjustment entity in parallel with the lookup adjustment_entity = SalaryAdjustment(parent=employeeKey) adjustment_entity.adjustment = raise_amount db.put_async(adjustmentEntity) # Fetch the result of our lookup to make the salary adjustment employee_entity = employee_entity_future.get_result() employee_entity.salary += raise_amount # Re-put the Employee entity with the adjusted salary. db.put_async(employee_entity) db.run_in_transaction(runner)
Questo esempio illustra un'importante differenza tra le chiamate asincrone senza transazioni e le chiamate asincrone all'interno delle transazioni. Quando non utilizzi una transazione, l'unico modo per assicurarti che una singola chiamata asincrona sia stata completata è recuperare il risultato dell'oggetto asincrono utilizzando una transazione. Il commit di questa transazione blocca il risultato di tutte le chiamate asincrone effettuate all'interno di una transazione.
Quindi, nel nostro esempio precedente, anche se la chiamata asincrona per inserire l'entità SalaryAdjustment potrebbe essere ancora in sospeso al termine di runner(), il commit non verrà eseguito finché l'inserimento non sarà completato.
Query asincrone
Al momento non esponiamo un'API esplicitamente asincrona per le query. Tuttavia, quando richiami Query.run(), la query viene restituita immediatamente e recupera in modo asincrono i risultati. In questo modo, l'applicazione può eseguire il lavoro in parallelo durante il recupero dei risultati della query.
# ... q1 = Salesperson.all().filter('date_of_hire <', one_month_ago) # Returns instantly, query is executing in the background. recent_hires = q1.run() q2 = Customer.all().filter('last_contact >', one_year_ago) # Also returns instantly, query is executing in the background. needs_followup = q2.run() schedule_phone_call(recent_hires, needs_followUp)
Purtroppo, Query.fetch() non ha lo stesso comportamento.
Quando utilizzare le chiamate Async Datastore
Quando chiami un metodo sincrono google.ext.db, ad esempio db.get(), il codice viene bloccato fino al completamento della chiamata al datastore. Se l'unica cosa che deve fare la tua applicazione è eseguire il rendering del risultato di get() in HTML, bloccare fino al completamento della chiamata è una cosa perfettamente ragionevole da fare. Tuttavia, se la tua applicazione ha bisogno del risultato di get() più il risultato di una query per eseguire il rendering della risposta e se get() e la query non hanno dipendenze dai dati, attendere il completamento di get() per avviare la query è una perdita di tempo. Ecco un esempio di codice che può essere migliorato utilizzando l'API asincrona:
# Read employee data from the Datastore employee = Employee.get_by_key_name('Max') # Blocking for no good reason! # Fetch payment history query = PaymentHistory.all().ancestor(employee.key()) history = query.fetch(10) render_html(employee, history)
Anziché attendere il completamento di get(), utilizza db.get_async() per eseguire la chiamata in modo asincrono:
employee_key = db.Key.from_path(Employee.kind(), 'Max') # Read employee data from the Datastore employee_future = db.get_async(employee_key) # Returns immediately! # Fetch payment history query = PaymentHistory.all().ancestor(employee_key) # Implicitly performs query asynchronously history_itr = query.run(config=datastore_query.QueryOptions(limit=10)) employee = employee_future.get_result() render_html(employee, history_itr)
Le versioni sincrona e asincrona di questo codice utilizzano quantità simili di CPU (dopo tutto, entrambe eseguono la stessa quantità di lavoro), ma poiché la versione asincrona consente l'esecuzione parallela delle due operazioni del datastore, la latenza è inferiore. In generale, se devi eseguire più operazioni datastore che non hanno dipendenze dai dati, l'API asincrona può migliorare significativamente la latenza.