ActiveRecord 3.1.0 multiple databases

2020-02-26 08:18发布

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?

2条回答
够拽才男人
2楼-- · 2020-02-26 08:52

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
查看更多
啃猪蹄的小仙女
3楼-- · 2020-02-26 08:54

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.

查看更多
登录 后发表回答