Releasing a connection in Rails - ruby-on-rails

I'm using Rails 2.3.8.
What's the best way to release a connection on a model to another database?
Let's say I have ModelB.establish_connection("server_b")
Would ModelB.remove_connection do the trick? How would I verify that I've successfully removed the connection?

Looks as though remove_connection is what you're looking for. To verify that you've successfully removed the connection, you could wrap a find method within a rescue block like:
begin
ModelB.find(1)
rescue ConnectionNotEstablished
# if we're here, then we have no connection, which is good in this case
else
# if we're here, then we still have a connection, which is bad...
end

Related

ActiveRecord exception caught after being eaten

I've got a GCP pubsub listener that does some work and then saves to ActiveRecord. I don't want to do that work if the DB connection is down, so I've added a pre-flight check. The pre-flight check checks the DB connection, and if it fails, eats the error and raises a RuntimeError. The DB is flighty though, and to account for the scenario where the pre-flight succeeds, but the DB connection dies while the work is being done, I have the caller rescuing ActiveRecord::ActiveRecordError and PG::Error, so we can log that the work was done, but the receipt couldn't be persisted. It's more important that this work not be duplicated than for the receipt to be persisted, so RuntimeError isn't caught, (causing a retry), but the DB errors are. It looks like this (snipping significantly):
# Service
def process
begin
WorkReceipt.do_work
rescue ActiveRecord::ActiveRecordError, PG::Error
Rails.logger.error("Work was done successfully, but not persisted")
end
end
# Model
class WorkReceipt < ActiveRecord::Base
def self.do_work
if !ActiveRecord::Base.connection.active?
Rails.logger.error("DB connection is inactive. Reconnecting...")
begin
ActiveRecord::Base.connection.reconnect!
rescue => e
Rails.logger.error("Could not reestablish connection: #{e}")
raise "Could not connect to database"
end
end
# Lots of hard work
self.create!(
# Some args
)
end
end
Where things get weird is, while testing this, I brought down the DB and fired off 4 of these tasks. The first one handles correctly ("Could not reestablish connection: server closed the connection unexpectedly"), but then the other 3 get "DB connection is inactive. Reconnecting..." (good) followed by "Work was done successfully, but not persisted" (what?!). Even weirder, is that the work has logging and side-effects which I don't see happening. The pre-flight appears to correctly prevent the work from being done, but the database error is showing up in the outer rescue, preventing the retry and making me sad. There is no database access other than the create at the end.
What is going on here? Why does it seem like the database error is skipping past the inner rescue to be caught by the outer one?
Maybe I don't understand how Ruby works, but changing raise "Could not connect to database" to raise RuntimeError.new "Could not connect to database" fixes the problem. I was under the impression that providing a message to raise caused it to emit a RuntimeError without needing to be explicit about it, but here we are.

Is it possible to duck type an ActiveRecord ORM?

In an app I'm working on, the production database is Oracle and the development db is sqlite.
Most of the app code is high level ActiveRecord, but there is some custom sql for reporting. This sql varies depending on the backend db.
Rather than extending the ORM and adapters, or writing if statements throughout the application, is it possible to duck type the connection such that something like the below code is possible:
if Archive.connection.supports_function?("EXTRACT")
Archive.select("extract(year from created_at)")...
else
Archive.select("strftime('%Y', created_at)")...
end
I might be completely misunderstanding your requirement but you can check the adapter and change the code used for a method easily enough.
If you want to add a new extract method to activerecord that behaves in two ways for example:
# config/initializers/active_record_extract.rb
class ActiveRecord::Base
def self.extract_agnostic(oracle_column, default_column)
if ActiveRecord::Base.connection.instance_values["config"][:adapter].include?('oracle')
return self.select("extract(#{column1} from created_at)")...
end
self.select("strftime(#{default_column}, created_at)")...
end
end
# Usage:
Archive.extract_agnostic("year", "%Y")
Obviously this isn't perfect but should get you started?
I don't think rails can tell you if your adapter understands a command, but you could always try wrapping the command you want in a begin/rescue:
begin
self.select("extract(year from created_at)")...
rescue # the above failed, try something else
self.select("strftime('%Y', created_at)")...
end
Why can't you run an Oracle database for your development environment..? Scratch that - I don't want to know.
Use create_function to plug an extract() method into your SQLite:
http://rdoc.info/github/luislavena/sqlite3-ruby/SQLite3/Database#create_function-instance_method
(And good luck doing THAT in Oracle!;)

Trying to do something with ActiveRecord::Base.connection_pool.with_connection

So I know how that this works like this
ActiveRecord::Base.connection_pool.with_connection do |conn|
conn.execute(sql)
end
but I'm trying to use the connection with actual Activerecord models, so something like
conn.Url.first
is there a way to do something like that?
Found out this isn't possible, but within the with_connection block any ActiveRecord calls should use the connection that is checked out from the Rails connection pool
so in this example
ActiveRecord::Base.connection_pool.with_connection do |conn|
Url.first
end
It should check out a connection from the pool set aside for Rails in your database.yml :pool setting, let your active record call use it and then check it back in
However, this only works in rails 3+ ... you can see the code change that makes this possible here
Rails 2.3 (old way) http://apidock.com/rails/v2.3.8/ActiveRecord/ConnectionAdapters/ConnectionPool/with_connection
Rails 3 http://apidock.com/rails/v3.0.0/ActiveRecord/ConnectionAdapters/ConnectionPool/with_connection
This guy explains the patch in this blog post
http://coderrr.wordpress.com/2009/05/05/activerecords-with_connection-is-now-useful/

Handling connection refused from curb(-fu) call

How can I deal with
Curl::Easy.http_get("dev-server.example.com")
Curl::Err::ConnectionFailedError: Curl::Err::ConnectionFailedError
from a curb or curb-fu call? Our dev servers are up and down so I need to be able to handle this better than getting an exception, the documentation seems very light.
I came across rescue which gave me what I needed
Curl::Easy.http_get("dev-server.example.com") rescue <some action>

How can I prevent ActiveRecord from writing to the DB?

I have a somewhat special use case, where I'd like to create a method that accepts a block, such that anything that happens inside that block is not written to the DB.
The obvious answer is to use transactions like so:
def no_db
ActiveRecord::Base.transaction do
yield
raise ActiveRecord::Rollback
end
end
But the trouble is that if my no_db method is used inside of another transaction block, then I'll ned up in the case of nested transactions. The drawback here is that nested transactions are only supported by MySQL, and I need support for PG, but more importantly SQLite (for tests). (I understand that PG is supported via savepoints, how reliable is that? performance hit?).
The other problem with this type of approach is that it seems really inefficient, writing things to a DB, and then rolling them back. It would be better if I could do something like this:
def no_db_2
# ActiveRecord::Base.turn_off_database
yield
# ActiveRecord::Base.turn_on_database
end
Is there such a method? Or a similar approach to what I'm looking for? I think it needs to be fairly low level..
(Rails version is 3.0.5, but I would be happy if there were an elegant solution for Rails 3.1)
This might be one way to do it:
class Book < ActiveRecord::Base
# the usual stuff
end
# Seems like a hack but you'll get the
# transaction behavior this way...
class ReadOnly < ActiveRecord::Base
establish_connection "#{Rails.env}_readonly"
end
I would think that this...
ReadOnly.transaction do
Book.delete_all
end
...should fail.
Finally, add another connection to config/database.yml
development:
username: fullaccess
development_readonly:
username: readonly
One downside is the lack of support for a read-only mode in the sqlite3-ruby driver. You'll notice that the mode parameter doesn't do anything yet according to the documentation. http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html#M000071

Resources