Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

ActiveRecord::Rollback Does Not Work In Nested Transactions

#1951

While I was working on the latest SQL Server adapter for rails 2.3 I noticed this in the docs and that it does not work at all in all DBs/core.


   # == Nested transactions
    #
    # #transaction calls can be nested. By default, this makes all database
    # statements in the nested transaction block become part of the parent
    # transaction. For example:
    #
    #   User.transaction do
    #     User.create(:username => 'Kotori')
    #     User.transaction do
    #       User.create(:username => 'Nemu')
    #       raise ActiveRecord::Rollback
    #     end
    #   end
    #   
    #   User.find(:all)  # => empty

I can totally see the issue but can not think of a good way to fix #transaction to let one block let another know this was set and hence all the block that finally answers true to transaction_open to do the work. I would love for some feedback for a way I might be able to patch this and/or if the docs should be changed to say that that exception class can only be used in the first transaction block.

BTW, there is a test in transaction_test.rb called #test_manually_rolling_back_a_transaction that I had copied but could never make it pass.


  def test_manually_rolling_back_a_nested_transaction
    Topic.transaction do
      @first.approved  = true
      @first.save
      Topic.transaction do
        @second.approved = false
        @second.save
        raise ActiveRecord::Rollback
      end
    end
    assert !Topic.find(1).approved?, "First shouldn't have been approved"
    assert Topic.find(2).approved?, "Second should still be approved"
  end

I believe Michael added this exception class, so I assigned him the ticket?

Reported by Ken Collins · February 12th, 2009 @ 02:29 PM

State: resolved
Milestone: 2.x
Assigned to: Pratik Pratik
Importance: Low

Activity

  1. Michael Koziarski
    Michael Koziarski

    Yeah, that's not possible, and can't be possible so the docs need updating.

    If you raise another exception it'll rollback, but obviously AR::Rollback doesn't, and shouldn't, propagate.

    February 13th, 2009 @ 08:53 AM

  2. Michael Koziarski
    Michael Koziarski
    • Assigned user changed from Michael Koziarski to Pratik

    Needs fixing in docrails

    February 13th, 2009 @ 08:54 AM

  3. Ken Collins
    Ken Collins

    Agreed, it was looking very hackish in my code attempts to implement this. And indeed the raising of some other exception does do the job.

    I'll add the doc changes to support this and submit a patch soon.

    February 13th, 2009 @ 02:19 PM

  4. Pratik
    Pratik

    Hey Ken,

    Just lemme know your github username and I'll give you commit access to docrails.

    February 13th, 2009 @ 02:23 PM

  5. Ken Collins
    Ken Collins

    Ah duh, I think I might already be on, it's "metaskills".

    February 13th, 2009 @ 02:25 PM

  6. Pratik
    Pratik

    Yup, you're already there :)

    February 13th, 2009 @ 02:33 PM

  7. CancelProfileIsBroken
    CancelProfileIsBroken
    • State changed from new to resolved

    Fixed in docrails

    April 22nd, 2009 @ 10:29 PM

  8. Ken Collins
    Ken Collins

    Thanks to whomever got that in docrails, I've been a bit too busy lately.

    April 23rd, 2009 @ 12:38 AM

  9. Jason Yuen
    Jason Yuen
    • Tag changed from 2.3, activerecord, nested, rollback, transaction to 2, activerecord, nested, rollback, transaction

    @Michael: I was wondering about your comment that "obviously AR::Rollback doesn't, and shouldn't, propagate." We are nesting our calls to #transaction without using :require_new. In this situation, we are observing that when we raise ActiveRecord::Rollback in our inner transaction block, the database transaction does not get rolled back.

    User.transaction do
      User.create(:username => 'Kotori')
      User.transaction do
        User.create(:username => 'Nemu')
        raise ActiveRecord::Rollback
      end
    end
    

    In this example, neither 'Kotori' nor 'Nemu' should exist in the database but they do. It seems like this is a bug so I've attached a patch (for 2.3.4 but it doesn't seem like the code has changed in master) which addresses this issue (unless of course, there is a reason why this isn't actually an issue). Basically, the rollback exception is always re-raised until we get to the outermost transaction block.

    January 8th, 2010 @ 11:28 PM

  10. Jason Yuen
    Jason Yuen
    • Tag changed from 2, activerecord, nested, rollback, transaction to 2.3, activerecord, nested, rollback, transaction

    January 8th, 2010 @ 11:29 PM

  11. Jason Yuen
    Jason Yuen
    • Tag changed from 2.3, activerecord, nested, rollback, transaction to 2, activerecord, nested, rollback, transaction

    Sorry guys - I think I was a little overzealous with the patch and I hadn't fully thought through all the implications (e.g. how it affects the validation lifecycle). I have an operation in a nested transaction block that causes an ActiveRecord::StaleObjectError. Instead of letting the stale object error propagate, I was rescuing it and re-raising an ActiveRecord::Rollback, thinking that the rollback would propagate across transaction boundaries and rollback operations in outer transaction blocks. I guess I was just being lazy, hoping that I wouldn't have to explicitly handle the stale object error :)

    Sorry again for the noise!

    Jason

    January 14th, 2010 @ 06:36 PM

  12. Jason Yuen
    Jason Yuen
    • Tag changed from 2, activerecord, nested, rollback, transaction to 2.3.x, activerecord, nested, rollback, transaction

    January 14th, 2010 @ 06:37 PM

  13. Ryan Bigg
    Ryan Bigg
    • Tag cleared.
    • Importance changed from to Low

    Automatic cleanup of spam.

    October 9th, 2010 @ 09:56 PM

  14. Ivan Evtuhovich
    Ivan Evtuhovich

    You fix this only in docrails, but rdoc documentation is wrong (patch attached)

    And i wonder why by default nested transaction rollback do not works?

    December 8th, 2010 @ 09:11 PM

  15. bingbing