API Async Datastore (Python)

Remarque : Les développeurs qui créent des applications sont vivement encouragés à utiliser la bibliothèque cliente NDB qui présente plusieurs avantages supplémentaires par rapport à cette bibliothèque cliente, tels que la mise en cache automatique des entités via l'API Memcache. Si vous utilisez actuellement l'ancienne bibliothèque cliente DB, consultez le guide de migration de DB vers NDB.

L'API Async Datastore permet d'effectuer des appels parallèles non bloquants vers le datastore et de récupérer les résultats de ces appels à un stade ultérieur du traitement de la requête. Ce document décrit plusieurs aspects de l'API Async Datastore.

Utilisation de l'API Async Datastore

L'API Async Datastore permet d'appeler le datastore à l'aide des méthodes du formulaire *_async (par exemple get_async() dans le package google.appengine.ext.db). L'exemple de code suivant illustre certaines opérations Datastore simples via des méthodes asynchrones :

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)

Ces fonctions réalisent les mêmes opérations que les versions synchrones, sauf qu'elles renvoient immédiatement un objet asynchrone que vous pouvez utiliser pour obtenir des résultats réels à un stade ultérieur. L'exemple de code suivant montre comment obtenir le résultat asynchrone à l'aide de 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()

Remarque : Les exceptions ne sont pas levées tant que la méthode get_result() n'est pas appelée. L'appel de cette méthode vous permet de vérifier que l'opération asynchrone a réussi.

Utiliser des transactions asynchrones

Les appels vers l'API Async Datastore peuvent être effectués dans des transactions, de la même façon que les appels synchrones. Voici une fonction qui ajuste le salaire d'un Employee et écrit une entité supplémentaire SalaryAdjustment dans le même groupe d'entités qu'Employee, le tout dans une même transaction.

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)

Cet exemple illustre une différence essentielle entre les appels asynchrones avec et sans transactions. Si vous n'utilisez aucune transaction, le seul moyen de vérifier l'aboutissement d'un appel asynchrone spécifique consiste à récupérer le résultat de l'objet asynchrone qui utilise une transaction. La validation de cette transaction exerce un blocage dans l'attente du résultat de tous les appels asynchrones effectués dans le cadre d'une transaction.

Ainsi, dans notre exemple ci-dessus, même s'il est possible que notre appel asynchrone destiné à insérer l'entité SalaryAdjustment soit encore en cours lorsque runner() se termine, la validation ne se produira pas tant que l'opération d'insertion n'est pas terminée.

Requêtes asynchrones

Pour le moment, nous ne proposons pas d'API explicitement asynchrone pour les requêtes. Toutefois, lorsque vous appelez Query.run(), la requête renvoie immédiatement et récupère de manière asynchrone les résultats. Cela permet à votre application d'exécuter des tâches en parallèle pendant la récupération des résultats de la requête.

# ...

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)

Malheureusement, Query.fetch() n'a pas le même comportement.

Utiliser des appels vers l'API Async Datastore

Lorsque vous appelez une méthode google.ext.db synchrone, telle que db.get(), votre code est bloqué jusqu'à la fin de l'appel au datastore. Si la seule opération requise de la part de votre application consiste à restituer le résultat de la méthode get () au format HTML, le blocage du code jusqu'à l'aboutissement de l'appel se révèle parfaitement judicieux. Cependant, si votre application a besoin du résultat de get () et du résultat d'une requête pour restituer la réponse, et si get () et la requête ne comportent aucune dépendance de données, le fait d'attendre que get () termine pour initier la requête est une perte de temps. Voici un exemple de code susceptible d'être amélioré par l'utilisation de l'API asynchrone :

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

Au lieu d'attendre l'aboutissement de la méthode get(), utilisez la méthode db.get_async() pour l'exécution asynchrone de l'appel :

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)

Bien que les versions synchrones et asynchrones de ce code exploitent autant le processeur l'une que l'autre (puisqu'elles effectuent le même volume de travail), la version asynchrone se caractérise par un temps de latence inférieur, car elle permet aux deux opérations du datastore de s'exécuter en parallèle. En général, si vous devez exécuter plusieurs opérations Datastore ne dépendant pas de données, l'API asynchrone peut considérablement améliorer la latence.