ActiveRecord 3.1.0 multiple databases - ruby

I'm trying to upgrade the ActiveRecord gem to the latest 3.1.0 release and seeing a lot of exceptions being raised, I think it's due to how we handle multiple databases.
For each of our databases we specify a separate base class which inherits from ActiveRecord::Base, and call establish_connection in there. There are no cross-database relations. This has worked fine for us up until now.
Having upgraded to ActiveRecord 3.1.0 I'm seeing that it fails with an ActiveRecord::ConnectionNotEstablished exception, when traversing relations (i.e. it will successfully pull a single entity or set of them from the DB, but fails when navigating to a related class).
The top line of the backtrace is C:/Ruby/lib/ruby/gems/1.9.1/gems/activerecord-3.1.0/lib/active_record/connection_adapters/abstract/connection_pool.rb:410:in 'retrieve_connection', so I dug into this a little. The method is defined as follows:
def retrieve_connection(klass) #:nodoc:
pool = retrieve_connection_pool(klass)
(pool && pool.connection) or raise ConnectionNotEstablished
end
My simple test (puts Customer.first.address) calls retrieve_connection 3 times. Twice with Customer as the klass parameter, and once with ActiveRecord::Base as the parameter - which is when it fails as establish_connection has not been called for ActiveRecord::Base.
To the actual question then - is there a new recommended way of handling multiple database connections in ActiveRecord? If so, what is it?
If not, what could be causing this problem?

I ran into the same issue yesterday while upgrading to ActiveRecord 3.1.0. I can't speak to whether there is a new recommended way of handling multiple database connections in ActiveRecord 3.1, but I did find a way to unblock myself.
It appears a connection must now be established on ActiveRecord::Base in order for it to determine the table name lengths/rules of the adapter. Along with the rest of my connections established in my database initializer, I now also have an ActiveRecord::Base connection established to one of my DBs (it doesn't matter which one).
I'd like to think there's a better solution to be found, but I'm happy to be unblocked for now.

I am using this solution - What I was seeing was that when establish_connection was called in each of the OtherDb classes - there seemed to be alot of overhead reloading table definitions and I would randomly see issues every time the class def was reloaded.
# The idea here is to specify that a given model should use another
# database without having to change the entire inheritance hierarchy
# declare model for table in primary connection
class Bar < ActiveRecord::Base
# assume we have logic here that we don't want to refactor into a module
# but we do want to inherit in OtherDb::Bar
end
module Foo
# base model in Foo namespace - uses another db
class BaseConnection < ActiveRecord::Base
# OtherDb::Title.database contains a db config hash
# This would probably go in the initializers
establish_connection OtherDb::Title.database
end
# module used to override db connection
module OtherDb::Base
def retrieve_connection
# connection_handler.retrieve_connection(self) # normal behavior
connection_handler.retrieve_connection(Foo::BaseConnection) # use db from Foo::BaseConnection
end
end
# Foo::Bar is identical to ::Bar but is in another db
class Bar < ::Bar
extend OtherDb::Base
end
end

Related

How to get access to Hanami connection instance

Hanami uses Sequel as default ORM
There are different plugins for Sequel
Sometimes to use it you need to access to database connection object
For example to use PostgreSQL advisory locking for mutexes
There are such gems for this: sequel-advisory-locking, sequel-pg_advisory_lock
First, you should load an extension for Sequel::Database instance
DB.extension(:advisory_locking)
Then use it in your app like this
DB.advisory_lock('my_key') do
# do stuff with lock
end
But there is problem to use such libraries with Hanami
There is no information about it in official guide
On database configuration page such info:
Hanami models use ROM as a low-level backend. This means that you can easily use any Sequel plugins in your app. For this you need to define a gateway block in your model configuration, add the extension by calling extension on gateway.connection and pass the extension name in:
# config/environment.rb
Hanami.configure do
model do
gateway do |g|
g.connection.extension(:connection_validator)
end
end
end
What I tried is define constant in this config
Hanami.configure do
model do
gateway do |g|
DB = g.connection
DB.extension(:connection_validator)
DB.extension(:advisory_locking)
end
end
end
And then use this DB somewhere in the app, for example as DB.advisory_lock('my_key') { do_stuff_with_lock }
But such constants define within a block looks not good
How to get connection instance in Hanami app?
There is config object in Hanami -- Hanami::Model.configuration
In fact it is the getter for #configuration instance variable
It is instance of Hanami::Model::Configuration and has such instance variables like #url, #entities, #logger and others. So it's possible to call getters to get them
It also has connection method. This method returns gateway.connection
And this gateway.connection is just what is needed. This is exactly the instance of connection to the database, on which you can call methods
For example
Hanami::Model.configuration.connection.advisory_lock('my_lock') do
# do stuff with lock
end
I want to note that all these methods are indicated by the comment # #api private. It means that their implementation may be changed in future versions.

Database connection too much with ActiveRecord::Base.establish_connection

I use multible databases with ActiveRecord. I must put establish_connection db in all models. But i want to call the connection of libraries file. While put establish_connection db in for all models Database connection count is too much . How can I do other methods?
My project is a Ruby on Sinatra.
Build a global hash of {db_name => connection_instance } for all possible connections you have and retrieve them from your models intelligently:
def get_or_establish_connection db
$connections[db] ||= establish_connection db
end
That way you’ll reuse all already established connections.
NB I wrote “global” because there is no way to suggest anything more robust without knowledge of your architecture details. In the real life one would put this hash as a class variable somewhere accessible from everywhere.
What we did in our projects that used multiple databases was creating one class per database, that establish the connection, and make models inherit from it. That way, only one connection is created per database.
class UsageTable < ActiveRecord::Base
self.abstract_class = true
establish_connection :usage
end
class User < UsageTable

How do I initialize a constant from a function?

I'm writing a module which will use Sequel ORM. I want to be able to set the DB constant so it's available in the global namespace but I can't figure out a non-hacky way to do so. Here's what I have so far:
require 'sequel'
module BB
class Dal
def initialize(db_url)
self.class.connect(db_url)
end
def self.connect(db_url)
#db = Sequel.connect( db_url )
end
end
BB::Dal.connect(ENV['DATABASE_URL']) if ENV['DATABASE_URL']
end
As you can see I'd like to enable two modes of initialization. Either through the ENV['DATABASE_URL'] variable or through a constructor. What I'd like to do now is replace #db with DB. Any ideas?
You can just kick that out of the module context if you want:
DB = BB::Dal.connect(ENV['DATABASE_URL']) if ENV['DATABASE_URL']
It's worth noting that jamming these things in the root namespace is probably bad form. You might want to have a database connection module to organize these:
module DB
def connection
# Lazy initializer pattern applied here
#connection ||= BB::Dal.connect(ENV['DATABASE_URL'])
end
end
# ...
# Will connect the first time it's called, recycle the connection
# for all subsequent calls.
DB.connection.do_stuff
It really depends on how this will be used. The advantage of a method is it's very easy to upgrade to a connection pool should the occasion arise, whereas a constant is very rigid in terms of function, it must return a singular value with no opportunity to run code first.

Rhomobile 4.1.1 - How to create local (non-sync) table

Following the documentation I am trying to create my first model in Rhomobile 4.1.1
It is advised to create a Ruby class, and said that the framework would create the necessary table. But I must have misunderstood because it is not working that way.
model in app/Auth/auth.rb
class Auth
include Rhom::FixedSchema
set :schema_version, '0.1'
property :session_token, :string
property :remember_token, :string
def self.auth_record
#auth_record ||= begin
if find(:count) == 0
create
else
find :first
end
end
end
end
As you can see I am trying to create a fixed schema single record table. I am not using RhoSync. As a result there is no table created, I am missing a step. Any hint appreciated. Thanks
My mistake was to have
require 'Auth/auth'
On top of one of my files.
The framework relies on const_missing to load and initialize the model (inject dependencies, create tables, ...). As I explicitly required the source file, the constant was already defined therefore Rhodes internal did not perform the needed initialization. Removing the require fixed the problem.
Lesson learned, also I would says that 1) this is not really solid coding and 2) this is completely undocumented.

Caching ActiveRecord model instance methods

Say I have a user model. It has an instance method called status. Status is not an association. It doesn't follow any active record pattern because it's a database already in production.
class User < ActiveRecord::Base
def status
Connection.where(machine_user_id: self.id).last
end
end
So I do this.
#users = User.all
First of all I can't eager load the status method.
#users.includes(:status).load
Second of all I can't cache that method within the array of users.
Rails.cache.write("user", #users)
The status method never gets called until the view layer it seems like.
What is the recommended way of caching this method.
Maybe this instance method is not what I want to do. I've looked at scope but it doesn't look like what I want to do.
Maybe I just need an association? Then I get the includes and I can cache.
But can associations handle complex logic. In this case the instance method is a simple query. What if I have complex logic in that instance method?
Thanks for any help.
Have You tried to encapsulate this logic inside some plain Ruby object like this (I wouldn't use this for very large sets though):
class UserStatuses
def self.users_and_statuses
Rails.cache.fetch "users_statuses", :expires_in => 30.minutes do
User.all.inject({}) {|hsh, u| hsh[u.id] = u.status; hsh }
end
end
end
After that You can use some helper method to access cached version
class User < ActiverRecord::Base
def cached_status
UserStatuses.users_and_statuses[id]
end
end
It doesn't solve Your eager loading problem, Rails doesn't have any cache warming up techniques built in. But by extracting like this, it's easily done by running rake task in Cron.
Also in this case I don't see any problems with using association. Rails associations allows You to submit different options including foreign and primary keys.

Resources