Reputation: 260
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 e.g. 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.
Upvotes: 12
Views: 7725
Reputation: 84114
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
Upvotes: 21
Reputation: 9847
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
Upvotes: 5
Reputation: 9146
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.
Upvotes: 2