aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/abstract
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/abstract')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb96
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb20
5 files changed, 86 insertions, 82 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 347d794fa3..7a3d9bfd3e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -496,40 +496,40 @@ module ActiveRecord
# ActiveRecord::Base.connection_handler. Active Record models use this to
# determine that connection pool that they should use.
class ConnectionHandler
- def initialize(pools = Hash.new { |h,k| h[k] = {} })
- @connection_pools = pools
- @class_to_pool = Hash.new { |h,k| h[k] = {} }
+ def initialize
+ @owner_to_pool = Hash.new { |h,k| h[k] = {} }
+ @class_to_pool = Hash.new { |h,k| h[k] = {} }
end
def connection_pools
- @connection_pools[Process.pid]
+ owner_to_pool.values.compact
end
- def establish_connection(name, spec)
- set_pool_for_spec spec, ConnectionAdapters::ConnectionPool.new(spec)
- set_class_to_pool name, connection_pools[spec]
+ def establish_connection(owner, spec)
+ @class_to_pool.clear
+ owner_to_pool[owner] = ConnectionAdapters::ConnectionPool.new(spec)
end
# Returns true if there are any active connections among the connection
# pools that the ConnectionHandler is managing.
def active_connections?
- connection_pools.values.any? { |pool| pool.active_connection? }
+ connection_pools.any?(&:active_connection?)
end
# 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.
def clear_active_connections!
- connection_pools.each_value {|pool| pool.release_connection }
+ connection_pools.each(&:release_connection)
end
# Clears the cache which maps classes.
def clear_reloadable_connections!
- connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
+ connection_pools.each(&:clear_reloadable_connections!)
end
def clear_all_connections!
- connection_pools.each_value {|pool| pool.disconnect! }
+ connection_pools.each(&:disconnect!)
end
# Locate the connection of the nearest super class. This can be an
@@ -552,56 +552,62 @@ module ActiveRecord
# 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.
- def remove_connection(klass)
- pool = class_to_pool.delete(klass.name)
- return nil unless pool
-
- connection_pools.delete pool.spec
- pool.automatic_reconnect = false
- pool.disconnect!
- pool.spec.config
+ def remove_connection(owner)
+ if pool = owner_to_pool.delete(owner)
+ @class_to_pool.clear
+ pool.automatic_reconnect = false
+ pool.disconnect!
+ pool.spec.config
+ end
end
+ # 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[klass] may sometimes be nil.
+ # However, benchmarking (https://gist.github.com/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.
def retrieve_connection_pool(klass)
- if !(klass < Model::Tag)
- get_pool_for_class('ActiveRecord::Model') # default connection
- else
- pool = get_pool_for_class(klass.name)
- pool || retrieve_connection_pool(klass.superclass)
+ class_to_pool[klass] ||= begin
+ until pool = pool_for(klass)
+ klass = klass.superclass
+ break unless klass < Model::Tag
+ end
+
+ class_to_pool[klass] = pool || pool_for(ActiveRecord::Model)
end
end
private
- def class_to_pool
- @class_to_pool[Process.pid]
- end
-
- def set_pool_for_spec(spec, pool)
- @connection_pools[Process.pid][spec] = pool
+ def owner_to_pool
+ @owner_to_pool[Process.pid]
end
- def set_class_to_pool(name, pool)
- @class_to_pool[Process.pid][name] = pool
- pool
+ def class_to_pool
+ @class_to_pool[Process.pid]
end
- def get_pool_for_class(klass)
- @class_to_pool[Process.pid].fetch(klass) {
- c_to_p = @class_to_pool.values.find { |class_to_pool|
- class_to_pool[klass]
- }
-
- if c_to_p
- pool = c_to_p[klass]
- pool = ConnectionAdapters::ConnectionPool.new pool.spec
- set_pool_for_spec pool.spec, pool
- set_class_to_pool klass, pool
+ def pool_for(owner)
+ owner_to_pool.fetch(owner) {
+ 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
- set_class_to_pool klass, nil
+ owner_to_pool[owner] = nil
end
}
end
+
+ def pool_from_any_process_for(owner)
+ owner_to_pool = @owner_to_pool.values.find { |v| v[owner] }
+ owner_to_pool && owner_to_pool[owner]
+ end
end
class ConnectionManagement
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index b0b51f540c..4e1f0e1d62 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -1,6 +1,12 @@
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseStatements
+ def initialize
+ super
+ @_current_transaction_records = []
+ @transaction_joinable = nil
+ end
+
# Converts an arel AST to SQL
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
@@ -167,31 +173,23 @@ module ActiveRecord
def transaction(options = {})
options.assert_valid_keys :requires_new, :joinable
- last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
- if options.has_key?(:joinable)
- @transaction_joinable = options[:joinable]
- else
- @transaction_joinable = true
- end
- requires_new = options[:requires_new] || !last_transaction_joinable
-
- transaction_open = false
- @_current_transaction_records ||= []
+ last_transaction_joinable = @transaction_joinable
+ @transaction_joinable = options.fetch(:joinable, true)
+ requires_new = options[:requires_new] || !last_transaction_joinable
+ transaction_open = false
begin
- if block_given?
- if requires_new || open_transactions == 0
- if open_transactions == 0
- begin_db_transaction
- elsif requires_new
- create_savepoint
- end
- increment_open_transactions
- transaction_open = true
- @_current_transaction_records.push([])
+ if requires_new || open_transactions == 0
+ if open_transactions == 0
+ begin_db_transaction
+ elsif requires_new
+ create_savepoint
end
- yield
+ increment_open_transactions
+ transaction_open = true
+ @_current_transaction_records.push([])
end
+ yield
rescue Exception => database_transaction_rollback
if transaction_open && !outside_transaction?
transaction_open = false
@@ -225,7 +223,7 @@ module ActiveRecord
@_current_transaction_records.last.concat(save_point_records)
end
end
- rescue Exception => database_transaction_rollback
+ rescue Exception
if open_transactions == 0
rollback_db_transaction
rollback_transaction_records(true)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index a6e16da730..be6fda95b4 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -65,6 +65,7 @@ module ActiveRecord
end
private
+
def cache_sql(sql, binds)
result =
if @query_cache[sql].key?(binds)
@@ -85,11 +86,7 @@ module ActiveRecord
end
def locked?(arel)
- if arel.respond_to?(:locked)
- arel.locked
- else
- false
- end
+ arel.respond_to?(:locked) && arel.locked
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index ef17dfbbc5..dca355aa93 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/object/blank'
require 'date'
require 'set'
require 'bigdecimal'
@@ -271,7 +270,7 @@ module ActiveRecord
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
# <tt>:updated_at</tt> to the table.
def timestamps(*args)
- options = { :null => false }.merge(args.extract_options!)
+ options = args.extract_options!
column(:created_at, :datetime, options)
column(:updated_at, :datetime, options)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 65c7ef0153..86d6266af9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1,4 +1,3 @@
-require 'active_support/deprecation/reporting'
require 'active_record/migration/join_table'
module ActiveRecord
@@ -57,7 +56,6 @@ module ActiveRecord
# Checks to see if a column exists in a given table.
#
- # === Examples
# # Check a column exists
# column_exists?(:suppliers, :name)
#
@@ -65,7 +63,10 @@ module ActiveRecord
# column_exists?(:suppliers, :name, :string)
#
# # Check a column exists with a specific definition
- # column_exists?(:suppliers, :name, :string, :limit => 100)
+ # column_exists?(:suppliers, :name, :string, limit: 100)
+ # column_exists?(:suppliers, :name, :string, default: 'default')
+ # column_exists?(:suppliers, :name, :string, null: false)
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
def column_exists?(table_name, column_name, type = nil, options = {})
columns(table_name).any?{ |c| c.name == column_name.to_s &&
(!type || c.type == type) &&
@@ -202,11 +203,14 @@ module ActiveRecord
join_table_name = find_join_table_name(table_1, table_2, options)
column_options = options.delete(:column_options) || {}
- column_options.reverse_merge!({:null => false})
+ column_options.reverse_merge!(null: false)
- create_table(join_table_name, options.merge!(:id => false)) do |td|
- td.integer :"#{table_1.to_s.singularize}_id", column_options
- td.integer :"#{table_2.to_s.singularize}_id", column_options
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
+
+ create_table(join_table_name, options.merge!(id: false)) do |td|
+ td.integer t1_column, column_options
+ td.integer t2_column, column_options
+ yield td if block_given?
end
end
@@ -485,7 +489,7 @@ module ActiveRecord
def dump_schema_information #:nodoc:
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- ActiveRecord::SchemaMigration.order('version').all.map { |sm|
+ ActiveRecord::SchemaMigration.order('version').map { |sm|
"INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
}.join "\n\n"
end