API de Async Datastore (Python)

Nota: Se recomienda de manera enfática a los desarrolladores que compilan aplicaciones nuevas que usen la biblioteca cliente de NDB, ya que tiene muchas ventajas en comparación con esta biblioteca cliente, como el almacenamiento en caché automático de entidades mediante la API de Memcache. Si por el momento usas la biblioteca cliente de DB anterior, lee la Guía de migración de DB a NDB.

La API de Async Datastore permite realizar llamadas paralelas y sin bloqueo al almacén de datos y recuperar los resultados de estas llamadas más adelante. En esta documentación, se describen los siguientes aspectos de la API de Async Datastore:

Trabaja con el servicio de Async Datastore

Con la API de Async Datastore, puedes realizar llamadas al almacén de datos mediante métodos con el formato *_async (como get_async() en el paquete google.appengine.ext.db). En la siguiente muestra de código, se presentan algunas operaciones simples del almacén de datos mediante el uso de métodos asíncronos:

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)

Estas funciones realizan las mismas operaciones que las versiones síncronas, excepto que muestran inmediatamente un objeto asíncrono que puedes usar para obtener el resultado real en algún momento en el futuro. En la siguiente muestra de código, se obtiene el resultado asíncrono mediante 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: No se mostrarán excepciones hasta que llames a get_result(). Llamar a este método te permite verificar que la operación asíncrona se realizó de forma correcta.

Trabaja con transacciones asíncronas

Las llamadas a la API de Async Datastore pueden participar en transacciones al igual que las llamadas síncronas. Aquí te mostramos una función que ajusta el sueldo de un Employee y escribe una entidad SalaryAdjustment adicional en el mismo grupo de entidades que el Employee, todo dentro de una misma transacción.

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)

Este ejemplo muestra una diferencia importante entre las llamadas asíncronas sin transacciones y las llamadas asíncronas dentro de transacciones. Cuando no usas una transacción, la única forma de garantizar que una llamada asíncrona individual se haya completado es recuperar el resultado del objeto asíncrono usando una transacción. Confirmar esa transacción bloquea el resultado de todas las llamadas asíncronas realizadas dentro de una transacción.

Por lo tanto, en nuestro ejemplo anterior, a pesar de que la llamada asíncrona para insertar la entidad SalaryAdjustment aún puede estar pendiente cuando runner() finaliza, la confirmación no se producirá hasta que la inserción se complete.

Consultas asíncronas

Actualmente no exponemos una API asíncrona de forma explícita para consultas. Sin embargo, cuando invocas Query.run(), la consulte muestra inmediatamente y recupera previamente los resultados de manera asíncrona. Esto le permite a tu aplicación realizar trabajos en paralelo mientras se recuperan los resultados de la consulta.

# ...

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)

Desafortunadamente, Query.fetch() no tiene el mismo comportamiento.

Cuándo usar llamadas de Async Datastore

Cuando llamas a un método google.ext.db síncrono, como db.get(), el código se bloquea hasta que se completa la llamada al almacén de datos. Si lo único que la aplicación debe hacer es procesar el resultado de get() en HTML, mantener el bloqueo hasta que la llamada se complete es muy razonable. Sin embargo, si la aplicación necesita el resultado de get() y el resultado de una consulta con el fin procesar la respuesta, y si get() y la consulta no tienen ninguna dependencia de datos, es una pérdida de tiempo esperar hasta que get() se complete para iniciar la consulta. Aquí mostramos un ejemplo de un código que se puede mejorar mediante el uso de la API asíncrona:

# 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)

En lugar de esperar a que se complete get(), usa db.get_async() para ejecutar la llamada de manera asíncrona:

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)

Las versiones síncronas y asíncronas de este código usan cantidades similares de CPU (después de todo, ambas realizan la misma cantidad de trabajo), pero, ya que la versión asíncrona permite que las dos operaciones del almacén de datos se ejecuten en paralelo, la versión asíncrona tiene una latencia menor. En general, si necesitas realizar varias operaciones de almacén de datos que no tienen dependencias de datos, la API asíncrona puede mejorar la latencia de manera significativa.