Nota: Se recomienda encarecidamente a los desarrolladores que creen aplicaciones nuevas que usen la biblioteca de cliente de NDB, que ofrece varias ventajas en comparación con esta biblioteca de cliente, como el almacenamiento automático en caché de entidades mediante la API Memcache. Si actualmente usas la biblioteca de cliente de DB anterior, consulta la guía de migración de DB a NDB.
El API de servicio asíncrono del almacén de datos permite realizar varias invocaciones a la vez, sin que se bloqueen entre sí, al almacén de datos y obtener los resultados de dichas invocaciones más adelante, cuando se procese la solicitud. En este documento se describen los siguientes aspectos del API de servicio asíncrono del almacén de datos:
Cómo utilizar el servicio asíncrono del almacén de datos
Con la API Async Datastore, puedes hacer llamadas a Datastore mediante métodos del tipo *_async
(como get_async() en el paquete google.appengine.ext.db
). En el código de ejemplo siguiente se muestran algunas sencillas operaciones del almacén de datos que utilizan 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, salvo que devuelven de forma inmediata un objeto asíncrono que puedes utilizar para obtener el resultado real más tarde. En el siguiente ejemplo de código se muestra cómo obtener 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: Las excepciones no se generan hasta que llamas a get_result(). Llamar a este método te permite verificar que la operación asíncrona se ha completado correctamente.
Cómo utilizar transacciones asíncronas
Las invocaciones del API de servicio asíncrono del almacén de datos pueden participar en las transacciones, al igual que las invocaciones sincronizadas. Aquí tienes una función que ajusta el salario de un Employee
y escribe una entidad SalaryAdjustment
adicional en el mismo grupo de entidades que el Employee
, todo ello en una sola 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)
En este ejemplo se muestra una diferencia importante entre las llamadas asíncronas sin transacciones y aquellas con transacciones. Si no utilizas ninguna transacción, la única forma de asegurarse de que una llamada asíncrona concreta se haya completado es extrayendo el resultado del objeto asíncrono mediante una transacción. Esta transacción bloquea el resultado de todas las llamadas asíncronas realizadas en una transacción.
Por lo tanto, en el ejemplo anterior, aunque la llamada asíncrona para insertar la entidad SalaryAdjustment
aún esté pendiente cuando finalice runner()
, la confirmación no se producirá hasta que se complete la inserción.
Consultas asíncronas
Actualmente no disponemos de un API de servicio asíncrono específica para las consultas. Sin embargo, cuando invocas Query.run(), la consulta se devuelve inmediatamente y preobtiene los resultados de forma asíncrona. Esto permite que tu aplicación realice trabajos en paralelo mientras se extraen 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)
Lamentablemente, Query.fetch() no se comporta de la misma forma.
Cuándo realizar invocaciones del almacén de datos usando el servicio asíncrono
Cuando llamas a un método google.ext.db
síncrono, como db.get(), tu código se bloquea hasta que se completa la llamada al almacén de datos. Si lo único que tiene que hacer tu aplicación es renderizar el resultado de get() en HTML, bloquear hasta que se complete la llamada es una opción perfectamente razonable. Sin embargo, si tu aplicación necesita el resultado de get() más el resultado de una consulta para renderizar la respuesta (y si get() y la consulta no tienen ninguna dependencia de datos), esperar a que se complete get() para iniciar la consulta es una pérdida de tiempo. A continuación, incluimos un ejemplo con código que puede mejorarse mediante el API de servicio asíncrono:
# 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 el método get() se complete, utiliza una instancia de db.get_async() para realizar la invocación de forma 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 una cantidad de CPU similar (después de todo, ambas llevan a cabo la misma cantidad de trabajo). Sin embargo, dado que la versión asíncrona permite que se ejecuten las dos operaciones del almacén de datos al mismo tiempo, la versión asíncrona tiene una latencia menor. En general, si necesitas realizar varias operaciones de Datastore que no tengan dependencias de datos, la API asíncrona puede mejorar significativamente la latencia.