Any reason a TX in Rails should not have requires_new: true

2k views Asked by At

Related to this question: custom transaction doesn't work with database_cleaner in rspec

Is there any place one would not want to start a TX with requires_new? (which does nested TX)

ModelObject.transaction(requires_new: true)

If that's the case, should this have been the default.

BTW, this is a reason that an rspec test with a rollback will fail when the normal execution path of the code succeeds, when using the default transaction strategy.

Here's my DatabaseCleaner setup. Running Rails 4.

RSpec.configure do |config|
  config.add_setting(:seed_tables)
  config.seed_tables = %w(global_options shoot_types)

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation, except: config.seed_tables)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, js: true) do
    DatabaseCleaner.strategy = :truncation, {except: config.seed_tables}
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

end
2

There are 2 answers

3
Farley Knight On BEST ANSWER

In order to get a ROLLBACK for the nested transaction you may ask for a real sub-transaction by passing requires_new: true. If anything goes wrong, the database rolls back to the beginning of the sub-transaction without rolling back the parent transaction. If we add it to the previous example:

Most databases don’t support true nested transactions. At the time of writing, the only database that we’re aware of that supports true nested transactions, is MS-SQL. Because of this, Active Record emulates nested transactions by using savepoints on MySQL and PostgreSQL. See dev.mysql.com/doc/refman/5.6/en/savepoint.html for more information about savepoints.

http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

0
gvo On

The only reason I might see is performance on the DB side. Using too much savepoints (the way nested transaction are done), might impact performances, though it doesn't seem to be a very strong effect.

In cases of nested transaction, I think the important thing is understanding how your exceptions would flow or not to the outer transaction.

If it doesn't flow up to the outer transaction (Rollback or rescue in between), you likely want to rollback only the inner transaction, so requires_new is needed,

And if that's not the case (the Exception goes to the outer transaction), everything will be rollback, so doesn't make much of a difference whether you used it or not.

So definitely, it seems to me to be a safer default. :)