edge badge

ConnectionHandler is a collection of ConnectionPool objects. It is used for keeping separate connection pools for Active Record models that connect to different databases.

For example, suppose that you have 5 models, with the following hierarchy:

class Author < ActiveRecord::Base
end

class BankAccount < ActiveRecord::Base
end

class Book < ActiveRecord::Base
  establish_connection "library_db"
end

class ScaryBook < Book
end

class GoodBook < Book
end

And a database.yml that looked like this:

development:
  database: my_application
  host: localhost

library_db:
  database: library
  host: some.library.org

Your primary database in the development environment is “my_application” but the Book model connects to a separate database called “library_db” (this can even be a database on a different machine).

Book, ScaryBook and GoodBook will all use the same connection pool to “library_db” while Author, BankAccount, and any other models you create will use the default connection pool to “my_application”.

The various connection pools are managed by a single instance of ConnectionHandler accessible via ActiveRecord::Base.connection_handler. All Active Record models use this handler to determine the connection pool that they should use.

Methods
A
C
E
N
R
Class Public methods
new()
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 505
def initialize
  # These caches are keyed by klass.name, NOT klass. Keying them by klass
  # alone would lead to memory leaks in development mode as all previous
  # instances of the class would stay in memory.
  @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
    h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
  end
  @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
    h[k] = ThreadSafe::Cache.new
  end
end
Instance Public methods
active_connections?()

Returns true if there are any active connections among the connection pools that the ConnectionHandler is managing.

# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 538
def active_connections?
  connection_pool_list.any?(&:active_connection?)
end
clear_active_connections!()

Returns any connections in use by the current thread back to the pool, and also returns connections to the pool cached by threads that are no longer alive.

# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 545
def clear_active_connections!
  connection_pool_list.each(&:release_connection)
end
clear_all_connections!()
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 554
def clear_all_connections!
  connection_pool_list.each(&:disconnect!)
end
clear_reloadable_connections!()

Clears the cache which maps classes.

# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 550
def clear_reloadable_connections!
  connection_pool_list.each(&:clear_reloadable_connections!)
end
connected?(klass)

Returns true if a connection that's accessible to this class has already been opened.

# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 572
def connected?(klass)
  conn = retrieve_connection_pool(klass)
  conn && conn.connected?
end
connection_pool_list()
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 517
def connection_pool_list
  owner_to_pool.values.compact
end
connection_pools()
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 521
      def connection_pools
        ActiveSupport::Deprecation.warn("          In the next release, this will return the same as `#connection_pool_list`.
          (An array of pools, rather than a hash mapping specs to pools.)
".squish)

        Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
      end
establish_connection(owner, spec)
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 530
def establish_connection(owner, spec)
  @class_to_pool.clear
  raise RuntimeError, "Anonymous class is not allowed." unless owner.name
  owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
end
remove_connection(owner)

Remove the connection for this class. This will close the active connection and the defined connection (if they exist). The result can be used as an argument for #establish_connection, for easily re-establishing the connection.

# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 581
def remove_connection(owner)
  if pool = owner_to_pool.delete(owner.name)
    @class_to_pool.clear
    pool.automatic_reconnect = false
    pool.disconnect!
    pool.spec.config
  end
end
retrieve_connection_pool(klass)

Retrieving the connection pool happens a lot so we cache it in @class_to_pool. This makes retrieving the connection pool O(1) once the process is warm. When a connection is established or removed, we invalidate the cache.

Ideally we would use fetch here, as class_to_pool may sometimes be nil. However, benchmarking (gist.github.com/jonleighton/3552829) showed that fetch is significantly slower than []. So in the nil case, no caching will take place, but that's ok since the nil case is not the common one that we wish to optimise for.

# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 599
def retrieve_connection_pool(klass)
  class_to_pool[klass.name] ||= begin
    until pool = pool_for(klass)
      klass = klass.superclass
      break unless klass <= Base
    end

    class_to_pool[klass.name] = pool
  end
end