web-dev-qa-db-fra.com

"Transaction.atomic" est-il identique à "transaction.commit_on_success"?

Django 1.6 propose @transaction.atomic dans le cadre de la refonte de la gestion des transactions à partir du 1.5.

J'ai une fonction qui est appelée par une commande de gestion Django qui est à son tour appelée par cron, c'est-à-dire aucune requête HTTP déclenchant des transactions dans ce cas.

from Django.db import transaction

@transaction.commit_on_success
def my_function():
    # code here

Dans le bloc de code ci-dessus commit_on_success utilise une seule transaction pour tout le travail effectué dans my_function.

Remplace @transaction.commit_on_success avec @transaction.atomic entraîne le même comportement? @transaction.atomicétat des documents :

L'atomicité est la propriété qui définit les transactions de base de données. atomic nous permet de créer un bloc de code au sein duquel l'atomicité sur la base de données est garantie. Si le bloc de code est terminé avec succès, les modifications sont validées dans la base de données. S'il existe une exception, les modifications sont annulées.

Je suppose qu'ils entraînent le même comportement; correct?

61
Joseph Victor Zammit

Oui. Vous devez utiliser atomic aux endroits où vous avez déjà utilisé commit_on_success.

Étant donné que le nouveau système de transactions est conçu pour être plus robuste et cohérent, il est possible que vous puissiez voir un comportement différent. Par exemple, si vous détectez des erreurs de base de données et essayez de continuer, vous verrez un TransactionManagementError, alors que le comportement précédent n'était pas défini et dépendait probablement de la casse.

Mais si vous faites les choses correctement, tout devrait continuer de fonctionner de la même manière.

46

Sur la base de la documentation que j'ai lue sur le sujet, il y a une différence significative lorsque ces décorateurs sont imbriqués.

L'imbrication de deux blocs atomic ne fonctionne pas de la même manière que l'imbrication de deux blocs commit_on_success blocs.

Le problème est qu'il y a deux garanties que vous voudriez avoir de ces blocs.

  • Vous souhaitez que le contenu du bloc soit atomique, soit tout ce qui se trouve à l'intérieur du bloc est validé, soit rien n'est validé.
  • Vous aimeriez la durabilité, une fois que vous avez quitté le bloc sans exception, vous êtes assuré que tout ce que vous avez écrit à l'intérieur du bloc est persistant.

Il est impossible de fournir les deux garanties lorsque les blocs sont imbriqués. Si une exception est levée après avoir quitté le bloc le plus à l'intérieur mais avant de quitter le bloc le plus à l'extérieur, vous devrez échouer de l'une des deux manières suivantes:

  • Ne parviennent pas à assurer la durabilité du bloc le plus intérieur.
  • Impossible de fournir l'atomicité pour le bloc le plus à l'extérieur.

Voici où vous trouvez la différence. En utilisant commit_on_success donnerait une durabilité pour le bloc le plus à l'intérieur, mais aucune atomicité pour le bloc le plus à l'extérieur. L'utilisation de atomic donnerait une atomicité pour le bloc le plus à l'extérieur, mais aucune durabilité pour le bloc le plus à l'intérieur.

Le simple fait de lever une exception en cas d'imbrication pourrait vous empêcher de rencontrer le problème. Le bloc le plus intérieur soulèverait toujours une exception, il ne promet donc jamais de durabilité. Mais cela perd une certaine flexibilité.

Une meilleure solution serait d'avoir plus de granularité sur ce que vous demandez. Si vous pouvez demander séparément l'atomicité et la durabilité, vous pouvez effectuer l'imbrication. Il vous suffit de vous assurer que chaque bloc demandant la durabilité est en dehors de ceux qui demandent l'atomicité. La demande de durabilité à l'intérieur d'un bloc demandant l'atomicité devrait déclencher une exception.

atomic est censé fournir la partie atomicité. Autant que je sache Django 1.6.1 n'a pas de décorateur, ce qui peut demander de la durabilité. J'ai essayé d'en écrire un, et publié sur codereview.

61
kasperd