diff options
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/abstract')
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 |