3

Pourquoi l'annulation ActiveRecord ne change-t-elle pas dans les transactions imbriquées après l'apparition d'une exception dans un bloc enfant?Rails 3. Transactions imbriquées. Exception dans un bloc enfant

Voici quelques exemples:

1.

 

>> Client.transaction do 
?> Client.create(:name => 'Pavel') 
>> Client.transaction do 
?>  Client.create(:name => 'Elena') 
>>  raise ActiveRecord::Rollback 
>> end 
>> end 
=> nil 
>> Client.all.map(&:name) 
=> ["Pavel", "Elena"] # instead of [] 
 

2.

 

>> Client.transaction do 
?> Client.create(:name => 'Pavel') 
>> Client.transaction(:requires_new => true) do 
?>  Client.create(:name => 'Elena') 
>>  raise ActiveRecord::Rollback 
>> end 
>> end 
=> nil 
>> Client.all.map(&:name) 
=> ["Pavel", "Elena"] # instead of ["Pavel"] 
 

Merci.

Debian GNU/Linux 5.0.6;

Ruby 1.9.2;

Ruby on Rails 3.0.1;

SQLite 3.7.3.

+0

est ici une explication assez cool de ce comportement: http://stackoverflow.com/questions/22413599/why-does-rails-ignore-a-rollback- in-a-pseudonested-transaction – Ich

Répondre

3

J'ai le même problème, et je peux reproduire exactement votre résultat. Si j'élève ActiveRecord :: Rollback dans le bloc externe, alors la transaction entière revient en arrière, mais sinon, rien ne sera annulé. Apparemment, la version actuelle d'ActiveRecord ne sait pas comment faire des transactions imbriquées avec SQLite3, même si ActiveRecord est supposé implémenter des transactions imbriquées en utilisant des points de sauvegarde, et SQLite a pris en charge les points de sauvegarde depuis 3.6.8.

Comme preuve supplémentaire que cela est tout simplement pas pris en charge par ActiveRecord encore, essayez ceci ...

> List.connection.supports_savepoints? 
=> false 

Ubuntu 11.04 - la Natty Narwhal; Ruby 1.8.7 (2010-04-19 patchlevel 253) [i486-linux], MBARI 0x8770, Ruby Enterprise Edition 2010.02;

Ruby on Rails 3.0.3;

gem sqlite3 1.3.3

SQLite 3.7.2;

+0

Je viens de soumettre un ticket pour ce problème: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6307 –

+0

Oui, il semble qu'ils ne sont pas implémentés encore pour SQLite3 malgré qu'il supporte les points de sauvegarde. Cependant, ce fait n'est pas reflété dans la documentation [Rails] (http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html), d'où ma confusion. Merci pour le billet. – Shamaoke

+0

J'ai trouvé le même comportement avec MySQL dans 3.0.8 – Dex

0

L'implémentation de la transaction Rails n'utilise pas les points d'arrêt (ou technologies similaires) utilisés par les bases de données pour prendre en charge les transactions imbriquées. Les vraies transactions imbriquées ne sont pas supoprted par les bases de données elles-mêmes.

exemple:

begin -- starts transaction 1 
    begin -- start transaction 2 

    insert into something (foo) values ('bar'); 

    commit -- ends transaction 1 
rollback -- is ignored 

La première commit ou rollback ferme toujours le hors plus transaction.

Il existe un moyen par lequel les bases de données peuvent réellement effectuer l'imbrication. ceci utiliserait les points de sauvegarde précédemment mentionnés. exemple

begin -- starts transaction 1 
    savepoint foo -- starts "transaction" 2 

    insert into something (foo) values ('bar'); 

    release -- commit for transaction 2 
rollback -- roll back the data of the savepoint and everything else within transaction 1 

Vous pouvez imbriquer autant savepoints que vous voulez dans l'autre, tant qu'une transaction est ouverte.

Pour les rails lui-même, il y a un problème: Les fonctions créent et se complètent dans une transaction. donc votre premier exemple produit le sql suivant

begin -- transaction.do 
    begin -- Client.create 
    insert into clients (name) values ('Pavel') -- Client.create 
    commit -- Client.create, closes the out-most transaction 
    begin -- transaction.do 
    begin -- Client.create 
     insert into clients (name) values ('Elena') -- Client.create 
    commit -- Client.create, closes the out-most transaction 

Donc votre Exception arrive juste trop tard.

Vous pouvez corriger ce problème, mais vous devez le faire pour chaque adaptateur de connexion.

PS: Vous pourriez être dérouté par le -- dans le sql. Ce sont des commentaires d'une seule ligne dans MySQL ..