class ActiveRecord::ConnectionAdapters::ConnectionHandler
determine the connection pool that they should use.
ActiveRecord::Base.connection_handler. Active Record models use this to
Normally there is only a single ConnectionHandler instance, accessible via
is not the same as the one used by Book/ScaryBook/GoodBook.
same connection pool. However, the connection pool used by Author/BankAccount
the same connection pool. Likewise, Author and BankAccount will use the
than the default database). Then Book, ScaryBook and GoodBook will all use
Suppose that Book is to connect to a separate database (i.e. one other
+– BankAccount
+– Author
| +– GoodBook
| +– ScaryBook
| |
+– Book
|
For example, suppose that you have 5 models, with the following hierarchy:
to different databases.
for keeping separate connection pools for Active Record models that connect
ConnectionHandler is a collection of ConnectionPool objects. It is used
def active_connections?
Returns true if there are any active connections among the connection
def active_connections? connection_pool_list.any?(&:active_connection?) end
def class_to_pool
def class_to_pool @class_to_pool[Process.pid] end
def clear_active_connections!
and also returns connections to the pool cached by threads that are no
Returns any connections in use by the current thread back to the pool,
def clear_active_connections! connection_pool_list.each(&:release_connection) end
def clear_all_connections!
def clear_all_connections! connection_pool_list.each(&:disconnect!) end
def clear_reloadable_connections!
def clear_reloadable_connections! connection_pool_list.each(&:clear_reloadable_connections!) end
def connected?(klass)
Returns true if a connection that's accessible to this class has
def connected?(klass) conn = retrieve_connection_pool(klass) conn && conn.connected? end
def connection_pool_list
def connection_pool_list owner_to_pool.values.compact end
def connection_pools
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.)" ) Hash[connection_pool_list.map { |pool| [pool.spec, pool] }] end
def establish_connection(owner, spec)
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
def initialize
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
def owner_to_pool
def owner_to_pool @owner_to_pool[Process.pid] end
def pool_for(owner)
def pool_for(owner) owner_to_pool.fetch(owner.name) { if ancestor_pool = pool_from_any_process_for(owner) # A connection was established in an ancestor process that must have # subsequently forked. We can't reuse the connection, but we can copy # the specification and establish a new connection with it. establish_connection owner, ancestor_pool.spec else owner_to_pool[owner.name] = nil end } end
def pool_from_any_process_for(owner)
def pool_from_any_process_for(owner) owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] } owner_to_pool && owner_to_pool[owner.name] end
def remove_connection(owner)
can be used as an argument for establish_connection, for easily
connection and the defined connection (if they exist). The result
Remove the connection for this class. This will close the active
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
def retrieve_connection(klass) #:nodoc:
for (not necessarily the current class).
opened and set as the active connection for the class it was defined
active or defined connection: if it is the latter, it will be
Locate the connection of the nearest super class. This can be an
def retrieve_connection(klass) #:nodoc: pool = retrieve_connection_pool(klass) (pool && pool.connection) or raise ConnectionNotEstablished end
def retrieve_connection_pool(klass)
take place, but that's ok since the nil case is not the common one that we wish
#fetch is significantly slower than #[]. So in the nil case, no caching will
However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
When a connection is established or removed, we invalidate the cache.
This makes retrieving the connection pool O(1) once the process is warm.
Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
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