Transactions NDB

Une transaction est une opération ou un ensemble d'opérations dont l'exécution est garantie atomique, ce qui signifie que les transactions ne sont jamais partiellement exécutées. Toutes les opérations d'une transaction sont exécutées ou aucune ne l'est. Les transactions ont une durée maximale de 60 secondes et un délai d'inactivité de 10 secondes après 30 secondes.

L'API asynchrone NDB permet à une application d'exécuter plusieurs transactions simultanément à condition qu'elles soient indépendantes. L'API synchrone est une API simplifiée basée sur le décorateur @ndb.transactional(). La fonction décorée est exécutée dans le contexte de la transaction.

@ndb.transactional
def insert_if_absent(note_key, note):
    fetch = note_key.get()
    if fetch is None:
        note.put()
        return True
    return False
note_key = ndb.Key(Note, note_title, parent=parent)
note = Note(key=note_key, content=note_text)
inserted = insert_if_absent(note_key, note)

Si la transaction entre en conflit avec une autre, elle échoue. NDB tente plusieurs fois d'exécuter les transactions échouées. La fonction peut être appelée plusieurs fois si la transaction est à nouveau tentée. Le nombre de tentatives d'exécution d'une transaction échouée est limité à 3 par défaut. Si la transaction n'aboutit toujours pas, NDB génère alors l'erreur TransactionFailedError. Vous pouvez modifier le nombre de tentatives en transmettant retries=N au décorateur transactional(). Un nombre de tentatives égal à 0 signifie que la transaction est tentée une fois, mais qu'elle ne sera pas tentée à nouveau si elle échoue. Un nombre de tentatives égal à N signifie que la transaction peut être tentée N fois +1 au total. Exemple :

@ndb.transactional(retries=1)
def insert_if_absent_2_retries(note_key, note):
    # do insert

Dans les transactions, seules les requêtes ancêtres sont autorisées. Par défaut, une transaction ne peut fonctionner qu'avec des entités du même groupe d'entités (entités dont les clés ont le même "ancêtre").

Vous pouvez spécifier des transactions entre plusieurs groupes (transactions "XG" qui autorisent jusqu'à 25 groupes d'entités) en transmettant xg=True : .

@ndb.transactional(xg=True)
def insert_if_absent_xg(note_key, note):
    # do insert

Ces transactions fonctionnent avec plusieurs groupes d'entités et se comportent comme des transactions au sein d'un seul groupe, mais n'échouent pas si le code tente de mettre à jour les entités de plusieurs groupes.

Si la fonction génère une exception, la transaction est immédiatement annulée. Dans ce cas, NDB génère à nouveau l'exception afin que le code appelant en tienne compte. Vous pouvez forcer une transaction à échouer sans notification en générant l'exception ndb.Rollback (auquel cas la fonction appelée renvoie la valeur None). Il n'existe pas de mécanisme pour forcer une nouvelle tentative.

Il peut y avoir une fonction que vous ne souhaitez pas toujours exécuter dans une transaction. Au lieu de décorer cette fonction avec @ndb.transactional, vous pouvez la transmettre en tant que fonction de rappel à ndb.transaction().

def insert_if_absent_sometimes(note_key, note):
    # do insert
inserted = ndb.transaction(lambda:
                           insert_if_absent_sometimes(note_key, note))

Pour vérifier si du code est en cours d'exécution dans une transaction, appelez la fonction in_transaction().

Vous pouvez spécifier la façon dont une fonction "transactionnelle" doit se comporter si elle est invoquée par un code déjà présent dans une transaction. Le décorateur @ndb.non_transactional indique qu'une fonction ne doit pas être exécutée dans une transaction. Si elle est appelée dans une transaction, elle s'exécute en dehors de celle-ci. Le décorateur @ndb.transactional et la fonction ndb.transaction prennent un argument de mot clé propagation. Par exemple, si une fonction doit démarrer une nouvelle transaction indépendante, décorez-la comme ceci :

@ndb.transactional(propagation=ndb.TransactionOptions.INDEPENDENT)
def insert_if_absent_indep(note_key, note):
    # do insert

Les types de propagation sont répertoriés avec les autres options de contexte et options de transaction.

Le comportement d'une transaction peut se combiner à celui de la mise en cache NDB et semer la confusion. Si vous modifiez une entité dans une transaction que vous n'avez pas encore validée, le cache contextuel NDB contient la valeur modifiée alors que la valeur non modifiée se trouve toujours dans le datastore sous-jacent.

Ajouter une tâche transactionnelle à la file d'attente

Dans le cadre d'une transaction Datastore, vous pouvez placer une tâche en file d'attente à condition que la transaction ait été validée avec succès. Si le commit échoue, la tâche n'est pas mise en file d'attente. Si le commit réussit, la tâche est mise en file d'attente. Une fois placée en file d'attente, la tâche ne s'exécute pas immédiatement. La tâche et la transaction ne sont donc pas atomiques. Cependant, une fois en file d'attente, la tâche renouvelle la tentative jusqu'à ce qu'elle réussisse. Cela s'applique à toute tâche mise en file d'attente pendant l'exécution d'une fonction décorée.

Les tâches transactionnelles sont utiles, car elles vous permettent de combiner dans une transaction des actions non liées à Cloud Datastore à une transaction qui dépend de la réussite de cette dernière (par exemple, envoyer un e-mail pour confirmer un achat). Vous pouvez également associer des actions Cloud Datastore à la transaction (par exemple, procéder au commit des modifications apportées aux groupes d'entités en dehors de la transaction uniquement si celle-ci aboutit).

Une application ne peut pas insérer plus de cinq tâches transactionnelles dans des files d'attente de tâches au cours d'une même transaction. Les tâches transactionnelles ne doivent pas porter de noms spécifiés par l'utilisateur.

from google.appengine.api import taskqueue
from google.appengine.ext import ndb
@ndb.transactional
def insert_if_absent_taskq(note_key, note):
    taskqueue.add(url=flask.url_for('taskq_worker'), transactional=True)
    # do insert
Cette page vous a-t-elle été utile ? Évaluez-la :

Envoyer des commentaires concernant…

Environnement standard App Engine pour Python