API Async Datastore (Python)

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 tramite Google Cloud CLI o tramite l'API Compute Engine. Se al momento utilizzi la libreria client DB precedente, leggi la guida alla migrazione da DB a NDB

L'API Async Datastore ti consente di effettuare chiamate non bloccanti in parallelo 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 datastore asincrona, puoi effettuare chiamate al datastore utilizzando metodi nel formato *_async (ad esempio get_async() nel pacchetto google.appengine.ext.db). Il seguente esempio di codice mostra alcune semplici operazioni del datastore che utilizzano 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, ma 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 consente di verificare che l'operazione asincrona sia riuscita.

Utilizzo delle transazioni asincrone

Le chiamate API datastore asincrone possono partecipare alle transazioni proprio come le chiamate sincrone. Di seguito è riportata una funzione che regola lo stipendio di un Employee e scrive un'entità SalaryAdjustment aggiuntiva nello stesso gruppo di entità dell'Employee, il tutto all'interno di 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 una differenza importante tra le chiamate asincrone senza transazioni e le chiamate asincrone all'interno delle transazioni. Quando non utilizzi una transazione, l'unico modo per garantire che una singola chiamata asincrona sia stata completata è recuperare il risultato dell'oggetto asincrono utilizzando una transazione. L'commit della transazione si blocca sul risultato di tutte le chiamate asincrone effettuate all'interno di una transazione.

Pertanto, 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 chiami Query.run(), la query restituisce immediatamente i risultati e li precompila in modo asincrono. 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 Datastore asincrone

Quando chiami un metodo google.ext.db sincrono, come db.get(), il codice si blocca fino al completamento della chiamata al datastore. Se l'unica cosa che l'applicazione deve fare è eseguire il rendering del risultato di get() in HTML, bloccare fino al completamento della chiamata è una cosa perfettamente ragionevole. Tuttavia, se la tua applicazione ha bisogno del risultato di get() più quello di una query per eseguire il rendering della risposta (e se get() e la query non hanno dipendenze per i 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)

La versione sincrona e asincrona di questo codice utilizzano quantità di CPU simili (dopo tutto, entrambe svolgono la stessa quantità di lavoro), ma poiché la versione asincrona consente l'esecuzione in parallelo di due operazioni sul datastore, la versione asincrona ha una latenza inferiore. In generale, se devi eseguire più operazioni di Datastore che non hanno dipendenze dai dati, l'API asincrona può migliorare notevolmente la latenza.