简体   繁体   中英

How can I execute a Database Operation outside of a transaction in Rails / ActiveRecord

I need to execute some raw SQL in my Rails app. The query will do cause an implicit commit if it is performed within a transaction. We are using MySQL with InnoDB and the query will include eg create table.

Executing the query with ActiveRecord::Base.connection.execute triggers the implict commit which is a problem.

It feels like I just need a separate connection for performing my queries. Can ActiveRecord provide this? I've seen discussions of connecting to multiple databases but not multiple connections to the same database.

A solution doesn't have to involve ActiveRecord if there's a better way.

Our Rails and ActiveRecord version is 3.2.3.

Database connections are done on a per thread basis (this is basically required for thread safety), which you can use to your advantage: just execute your code in a separate thread, for example

ActiveRecord::Base.transaction do
  # ...
  Thread.new do
    ActiveRecord::Base.connection.execute "..." # in a new connection
  end.join
end

As of rails 4, activerecord no longer reaps connections created in this way automatically. To avoid leaking connections you need to return them to the pool. As Matt Connelly suggests, the easiest way to do this is to use the with_connection method which will check the connection back in at the end of the block, for example

Thread.new do
  ActiveRecord::Base.connection_pool.with_connection do
    ...
  end
end

It is important that if you use a connection in a thread that you return the connection to the connection pool when done. The easiest way to do it is like this:

Thread.new do
  ActiveRecord::Base.connection_pool.with_connection do |connection|
    connection.execute "..." 
    # ensures the connection is returned to the pool when the thread is done.        
  end
end.join

DDl and some more queries fire implicit commit so they cannot be rolled back as per the mysql docs

http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html

These implicitly end any transaction active in the current session, as if you had done a COMMIT before executing the statement.

If there are no such queries in between then you can use SAVEPOINT feature. ( This does not work with DDL statements )

There is a option in active-record which helps create sub transactions which uses save points

   ActiveRecord::Base.transaction do
     # ...
   ActiveRecord::Base.transaction(:requires_new => true) do #creates save point
       # perform task 
       # if error occurs rollbacks only till the save point.
     end
   end

Check rails doc for more details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM