70

In the logs I'm seeing a ROLLBACK, but no exception is logged. Is there a way to find out what caused the ROLLBACK?

Here's the excerpt of the log:

  Phone Load (0.4ms)  SELECT "phones".* FROM "phones" WHERE "phones"."id" = $1 LIMIT 1  [["id", 980190963]]
   (0.2ms)  BEGIN
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."phone_id" = 980190963 LIMIT 1
   (0.2ms)  ROLLBACK
  Phone Load (0.4ms)  SELECT "phones".* FROM "phones" WHERE "phones"."id" = $1 LIMIT 1  [["id", 980190963]]
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."phone_id" = 980190963 LIMIT 1
m33lky
  • 7,055
  • 9
  • 41
  • 48
  • 1
    What is in the log around that ROLLBACK? – mu is too short Jan 30 '12 at 06:26
  • 1
    No errors. Also, it's weird that one time it uses a transaction, the next time it doesn't. Unless those two pieces are unrelated. – m33lky Jan 30 '12 at 07:23
  • Any idea what causes that SELECT inside the transaction? – mu is too short Jan 30 '12 at 07:32
  • May be you are doing some save/update. Can you paste the code corresponding to the log? – Nilesh Jan 30 '12 at 08:38
  • I cleaned up my code and this problem doesn't show up anymore. I don't know what it was. – m33lky Feb 19 '12 at 19:56
  • I have the same problem. There are no errors or exceptions. I've even tried catching an exception; nothing. As for details, this is happening inside a callback on a relationship with a through association. – chaostheory Oct 24 '12 at 06:42
  • 1
    Figured out my problem: rails doesn't like it when you modify an associated model that has a through association, during a callback whether its in the model or observer. There were no exceptions messages or anything; just rollbacks. – chaostheory Oct 24 '12 at 07:05

5 Answers5

112

save record with BANG '!' so it will make a run time error and you get know where does it happens

Nitin Jain
  • 3,053
  • 2
  • 24
  • 36
52

One way is to manually write information to the log. Try something like this from your controller:

Rails.logger.info(@your_object.errors.inspect) 

That should output the content of all validations that failed.

DanneManne
  • 21,107
  • 5
  • 57
  • 58
24

1) Disable before_create, before_save, before_update and check where it saves the day

2) If rollback was caused by one of those methods, check that those methods return true when you don't plan to rollback.

For example if you set default value for boolean field to avoid nil, you would probably do it this way

def set_defaults_before_create
  self.my_boolean_field ||= false
end

In this example method set_defaults_before_create always returns false and thus rollbacks your transaction. So refactor it to return true

def set_defaults_before_create
  self.my_boolean_field ||= false
  true
end
Sergiy Seletskyy
  • 16,236
  • 7
  • 69
  • 80
  • 3
    I spent about a day trying to debug this issue. Thanks a lot : ) – Subtletree Apr 17 '14 at 02:20
  • 4
    Second point solve mine. Methods needs to return true, I think it is used for validation purpose even when we are using it for other purpose. – Yakob Ubaidi Aug 26 '14 at 14:30
  • I had a similar issue when I defined a function `destroy` inside a model, to delete additional files related to the record from disk, then if the files where missing, the function returned not true, so I added the `true` in the last line and everything fine. Thanks! – Martin T. Jun 08 '21 at 14:35
10

The 3 methodologies (1 failed) I have come up with are to

  1. use an observer on active record on all the relevant save, validate methods

  2. to open up active record and put a debugger statement where ROLLBACKs are triggered and then run caller to pinpoint the code that triggered the error.

  3. Failed :override the active record methods and pause on exception. If I remember this method does not catch any exception because the save methods are wrapped in a transaction.

Notes: Only enable when the mode is not Rails.env.production?. Tested on Rails 3.2.13, using ruby 1.9.3.

  1. Observer: http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#observers

    class ActiveRecordObserver < ActiveRecord::Observer

    observe "ActiveRecord::Base"
    
    def after_validation(model)
        debugger if model.errors.messages.any?
        Rails.logger.error "after validation"
    end
    def before_update(model)
        debugger if !model.valid?
        Rails.logger.error "before update"
    end
    def after_update(model)
        debugger if !model.valid?
        Rails.logger.error "after update"
    end
    def before_save(model)
    
        debugger if model.errors.messages.any?
        Rails.logger.error "#{model}"
        Rails.logger.error "before save"
    end
    def after_save(model)
        debugger if model.errors.messages.any?
        Rails.logger.error "after save"
    end
    

    end

NOTE: To use Observers in recent Rails versions (from v.4 up to the most recent v.6), you need to bundle gem 'rails-observers' in Gemfile (best if enclosed inside a :development and :test group), put the class under app/observers/active_record_observer.rb, and register the observer in your current environment config file (e.g. config/environments/development.rb) by placing (or adding to) this line: config.active_record.observers = [:active_record_observer]

2) https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L231 cd `bundle show activerecord`

     Put a debugger statement when where the rollback is executed.
    /Users/<user>/.rvm/gems/ruby-1.9.3-<env>/gems/activerecord-3.2.14/lib/active_record/connection_adapters/abstract/database_statements.rb

   196              transaction_open = false
   197              decrement_open_transactions
   198              if open_transactions == 0
   199                rollback_db_transaction
   200                debugger
=> 201                rollback_transaction_records(true)
   202              else
   203                rollback_to_savepoint
   204                debugger
   205                rollback_transaction_records(false)

When the rails server or console hits the breakpoint type in caller to get the backtrace.

  1. Override AR in development mode. TODO: only override if !Rails.env.production? Put this in app/config/initializers/active_record_or_any_file_name.rb

    ActiveRecord::Base.class_eval do alias_method :old_save, :save alias_method :old_save!, :save! def save(*args) begin puts "#{self} save" Rails.logger.info "#{self} save" old_save(*args) rescue Exception => e debugger puts e end end def save!(*args) begin Rails.logger.info "#{self} save!" puts "#{self} save!" old_save!(*args) rescue Exception => e debugger puts e end end end

Claudio Floreani
  • 2,441
  • 28
  • 34
davidtingsu
  • 1,090
  • 1
  • 10
  • 17
  • I had a similar issue where rollback was not due to any exception and hence no rescue block would give a reason. Following @davidtimgsu's suggestion to put a debug point helped in localising it. In ActiveRecord 5.2.3, under `active_record/connection_adapters/abstract/transaction.rb` there is a line that reads `Thread.current.status == "aborting"`. This was the cause for rollbacks in mine because an external process was terminating the current thread and all transactions in ensure statements in current thread were being rolled back. – Darkfish May 29 '20 at 11:53
  • To use Observers in Rails over version 4 (up to the most recent v.6), you need to bundle `gem 'rails-observers'` in Gemfile, put the class under `app/observers/active_record_observer.rb`, and register the observer in your current environment config file (e.g. `config/environments/development.rb` by placing (or adding to) this line: `config.active_record.observers = [:active_record_observer]` – Claudio Floreani Feb 26 '22 at 12:05
0

In my case a has_many association was defined with dependent: :restrict_with_error instead of dependent: :destroy. It was defined in a concern so I didn't see it immediately. Anyway, it would be nice if Rails told us what causes a Rollback to happen :-)

Goulven
  • 777
  • 9
  • 20