rubysherpas / paranoia

acts_as_paranoid for Rails 5, 6 and 7
Other
2.89k stars 529 forks source link

paranoia `delete` throws exception when wrapped in a database cleaner transaction when running specs via rspec #274

Open ipd opened 8 years ago

ipd commented 8 years ago

This happens with v2.1.4 but not with v2.1.3. So it is caused by the changes introduced in the most recent release (v2.14). I discovered this after updating the gems in our Gemfile.

When running our specs using rspec and wrapping the specs using the DatabaseCleaner gem.

DatabaseCleaner.cleaning do
    example.run
end

I think it is caused because Paranoia is calling add_to_transaction but the transaction is an ActiveRecord::ConnectionAdapters::NullTransaction which apparently does not respond to state.

Here is the stack trace from one of my specs.

    Failure/Error: guest.delete
     NoMethodError:
       undefined method `state' for #<ActiveRecord::ConnectionAdapters::NullTransaction:0x007ff696a156c8>
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/activerecord-4.2.4/lib/active_record/connection_adapters/abstract/database_statements.rb:239:in `transaction_state'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/activerecord-4.2.4/lib/active_record/transactions.rb:336:in `add_to_transaction'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/paranoia-2.1.4/lib/paranoia.rb:96:in `delete'
     # ./spec/views/users/new.html.erb_spec.rb:9:in `block (2 levels) in <top (required)>'
     # ./spec/rails_helper.rb:104:in `block (3 levels) in <top (required)>'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/database_cleaner-1.5.1/lib/database_cleaner/generic/base.rb:16:in `cleaning'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/database_cleaner-1.5.1/lib/database_cleaner/base.rb:92:in `cleaning'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/database_cleaner-1.5.1/lib/database_cleaner/configuration.rb:86:in `block (2 levels) in cleaning'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/database_cleaner-1.5.1/lib/database_cleaner/configuration.rb:87:in `call'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/database_cleaner-1.5.1/lib/database_cleaner/configuration.rb:87:in `cleaning'
     # ./spec/rails_helper.rb:103:in `block (2 levels) in <top (required)>'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/rspec-retry-0.4.5/lib/rspec/retry.rb:98:in `block in run'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/rspec-retry-0.4.5/lib/rspec/retry.rb:88:in `loop'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/rspec-retry-0.4.5/lib/rspec/retry.rb:88:in `run'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/rspec-retry-0.4.5/lib/rspec_ext/rspec_ext.rb:12:in `run_with_retry'
     # /Users/ipd/.rvm/gems/ruby-2.2.2@huddle/gems/rspec-retry-0.4.5/lib/rspec/retry.rb:22:in `block (2 levels) in setup'
radar commented 8 years ago

Hi @ipd! What steps can I follow to reproduce this within a new app?

ipd commented 8 years ago

I'll see what i can come up with.

krainboltgreene commented 8 years ago

@ipd Any progress?

ipd commented 8 years ago

Sorry @krainboltgreene @radar I haven't had time to reproduce it. I was planning on making a new rails project and reproducing it there. I'll try to get to it today or tomorrow and will post a link to the repo with the rails project when i do.

krainboltgreene commented 8 years ago

No worries, if I don't hear back from you I'll give it a shot this weekend. On Nov 16, 2015 11:54 AM, "Ian Dickson" notifications@github.com wrote:

Sorry @krainboltgreene https://github.com/krainboltgreene @radar https://github.com/radar I haven't had time to reproduce it. I was planning on making a new rails project and reproducing it there. I'll try to get to it today or tomorrow and will post a link to the repo with the rails project when i do.

— Reply to this email directly or view it on GitHub https://github.com/radar/paranoia/issues/274#issuecomment-157151124.

412andrewmortimer commented 8 years ago

I'm using MiniTest -- I resolved this error by using the transaction strategy and stating my before and after actions like so:

DatabaseCleaner.strategy = :transaction

class MiniTest::Spec
  before :each do
    DatabaseCleaner.start
  end

  after :each do
    DatabaseCleaner.clean
  end
end
just3ws commented 8 years ago

I'm getting this issue while running a Rake task in production using Rails 4.2.5. I've tried wrapping the execution of the delete statement with an explicit transaction. Nada. Same error as described. :(

swapab commented 8 years ago

I came across this error while deleting a record and I have added a custom column(archived_at) instead of default(deleted_at).

class Room < ActiveRecord::Base
  acts_as_paranoid column: :archived_at
end

With acts_as_paranoid following error

2.1.4 :071 > room=Room.find(5)
  Room Load (0.5ms)  SELECT  `rooms`.* FROM `rooms` WHERE `rooms`.`archived_at` IS NULL AND `rooms`.`id` = 5 LIMIT 1
 => #<Room id: 5, ..... archived_at: nil> 
2.1.4 :072 > room.delete
NoMethodError: undefined method `state' for #<ActiveRecord::ConnectionAdapters::NullTransaction:0x00000004e08b58>
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activerecord-4.2.4/lib/active_record/connection_adapters/abstract/database_statements.rb:238:in `transaction_state'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activerecord-4.2.4/lib/active_record/transactions.rb:335:in `add_to_transaction'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/paranoia-2.1.4/lib/paranoia.rb:96:in `delete'
  from (irb):72
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/railties-4.2.4/lib/rails/commands/console.rb:110:in `start'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/railties-4.2.4/lib/rails/commands/console.rb:9:in `start'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:68:in `console'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/railties-4.2.4/lib/rails/commands.rb:17:in `<top (required)>'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:in `require'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:in `block in require'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:240:in `load_dependency'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:in `require'
  from /home/swapnil/Rails/myproject/bin/rails:8:in `<top (required)>'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:268:in `load'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:268:in `block in load'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:240:in `load_dependency'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:268:in `load'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/commands/rails.rb:6:in `call'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/command_wrapper.rb:38:in `call'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/application.rb:183:in `block in serve'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/application.rb:156:in `fork'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/application.rb:156:in `serve'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/application.rb:131:in `block in run'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/application.rb:125:in `loop'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/application.rb:125:in `run'
  from /home/swapnil/.rvm/gems/ruby-2.1.4@swap/gems/spring-1.4.1/lib/spring/application/boot.rb:18:in `<top (required)>'
  from /home/swapnil/.rvm/rubies/ruby-2.1.4/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:54:in `require'
  from /home/swapnil/.rvm/rubies/ruby-2.1.4/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:54:in `require'
  from -e:1:in `<main>'2.1.4 :073 > 

Without acts_as_paranoid in my model Room no error

class Room < ActiveRecord::Base
  # acts_as_paranoid column: :archived_at
end
2.1.4 :074 > reload!
Reloading...
 => true 
2.1.4 :076 > room=Room.find(6)
  Room Load (0.7ms)  SELECT  `rooms`.* FROM `rooms` WHERE `rooms`.`id` = 6 LIMIT 1
 => #<Room id: 6, ... archived_at: nil> 
2.1.4 :077 > room.delete
  SQL (56.5ms)  DELETE FROM `rooms` WHERE `rooms`.`id` = 6
 => #<Room id: 6, ... archived_at: nil> 
2.1.4 :078 > Room.find(6)
  Room Load (0.6ms)  SELECT  `rooms`.* FROM `rooms` WHERE `rooms`.`id` = 6 LIMIT 1
ActiveRecord::RecordNotFound: Couldn't find Room with 'id'=6

Something in paranoia is causing issue. Trying to figure-out whats going wrong.

agios commented 8 years ago

In the console it fails because it expects to be executed in a transaction, try this instead:

ActiveRecord::Base.transaction{ room.delete }

swapab commented 8 years ago

Okay. Yes it worked but I actually wanted to permanently delete a record.

Months ago I was able to do so by calling object.destroy twice. But now it just keeps updating archived_at. I have to use SQL instead.

room.really_destroy! it is.