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.rb82
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb6
6 files changed, 84 insertions, 41 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 2d62fd8d50..d17722adec 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -69,7 +69,7 @@ module ActiveRecord
# threads, which can occur if a programmer forgets to close a
# connection at the end of a thread or a thread dies unexpectedly.
# Regardless of this setting, the Reaper will be invoked before every
- # blocking wait. (Default nil, which means don't schedule the Reaper).
+ # blocking wait. (Default +nil+, which means don't schedule the Reaper).
#
#--
# Synchronization policy:
@@ -116,7 +116,7 @@ module ActiveRecord
end
end
- # If +element+ is in the queue, remove and return it, or nil.
+ # If +element+ is in the queue, remove and return it, or +nil+.
def delete(element)
synchronize do
@queue.delete(element)
@@ -135,7 +135,7 @@ module ActiveRecord
# If +timeout+ is not given, remove and return the head the
# queue if the number of available elements is strictly
# greater than the number of threads currently waiting (that
- # is, don't jump ahead in line). Otherwise, return nil.
+ # is, don't jump ahead in line). Otherwise, return +nil+.
#
# If +timeout+ is given, block if there is no element
# available, waiting up to +timeout+ seconds for an element to
@@ -171,14 +171,14 @@ module ActiveRecord
@queue.size > @num_waiting
end
- # Removes and returns the head of the queue if possible, or nil.
+ # Removes and returns the head of the queue if possible, or +nil+.
def remove
@queue.shift
end
# Remove and return the head the queue if the number of
# available elements is strictly greater than the number of
- # threads currently waiting. Otherwise, return nil.
+ # threads currently waiting. Otherwise, return +nil+.
def no_wait_poll
remove if can_remove_no_wait?
end
@@ -282,7 +282,7 @@ module ActiveRecord
end
# Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
- # A reaper instantiated with a nil frequency will never reap the
+ # A reaper instantiated with a +nil+ frequency will never reap the
# connection pool.
#
# Configure the frequency by setting "reaping_frequency" in your
@@ -307,6 +307,7 @@ module ActiveRecord
end
include MonitorMixin
+ include QueryCache::ConnectionPoolConfiguration
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
attr_reader :spec, :connections, :size, :reaper
@@ -349,8 +350,7 @@ module ActiveRecord
# currently in the process of independently establishing connections to the DB.
@now_connecting = 0
- # A boolean toggle that allows/disallows new connections.
- @new_cons_enabled = true
+ @threads_blocking_new_connections = 0
@available = ConnectionLeasingQueue.new self
end
@@ -422,7 +422,6 @@ module ActiveRecord
conn.disconnect!
end
@connections = []
- @available.clear
end
end
end
@@ -445,8 +444,6 @@ module ActiveRecord
# connections in the pool within a timeout interval (default duration is
# <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
def clear_reloadable_connections(raise_on_acquisition_timeout = true)
- num_new_conns_required = 0
-
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do
@connections.each do |conn|
@@ -457,24 +454,8 @@ module ActiveRecord
conn.disconnect! if conn.requires_reloading?
end
@connections.delete_if(&:requires_reloading?)
-
- @available.clear
-
- if @connections.size < @size
- # because of the pruning done by this method, we might be running
- # low on connections, while threads stuck in queue are helpless
- # (not being able to establish new connections for themselves),
- # see also more detailed explanation in +remove+
- num_new_conns_required = num_waiting_in_queue - @connections.size
- end
-
- @connections.each do |conn|
- @available.add conn
- end
end
end
-
- bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
end
# Clears the cache which maps classes and re-connects connections that
@@ -581,6 +562,24 @@ module ActiveRecord
@available.num_waiting
end
+ # Return connection pool's usage statistic
+ # Example:
+ #
+ # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
+ def stat
+ synchronize do
+ {
+ size: size,
+ connections: @connections.size,
+ busy: @connections.count { |c| c.in_use? && c.owner.alive? },
+ dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
+ idle: @connections.count { |c| !c.in_use? },
+ waiting: num_waiting_in_queue,
+ checkout_timeout: checkout_timeout
+ }
+ end
+ end
+
private
#--
# this is unfortunately not concurrent
@@ -681,13 +680,32 @@ module ActiveRecord
end
def with_new_connections_blocked
- previous_value = nil
synchronize do
- previous_value, @new_cons_enabled = @new_cons_enabled, false
+ @threads_blocking_new_connections += 1
end
+
yield
ensure
- synchronize { @new_cons_enabled = previous_value }
+ num_new_conns_required = 0
+
+ synchronize do
+ @threads_blocking_new_connections -= 1
+
+ if @threads_blocking_new_connections.zero?
+ @available.clear
+
+ num_new_conns_required = num_waiting_in_queue
+
+ @connections.each do |conn|
+ next if conn.in_use?
+
+ @available.add conn
+ num_new_conns_required -= 1
+ end
+ end
+ end
+
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
end
# Acquire a connection by one of 1) immediately removing one
@@ -739,7 +757,7 @@ module ActiveRecord
# and increment @now_connecting, to prevent overstepping this pool's @size
# constraint
do_checkout = synchronize do
- if @new_cons_enabled && (@connections.size + @now_connecting) < @size
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
@now_connecting += 1
end
end
@@ -833,7 +851,7 @@ module ActiveRecord
class ConnectionHandler
def initialize
# These caches are keyed by spec.name (ConnectionSpecification#name).
- @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h,k|
+ @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
h[k] = Concurrent::Map.new(initial_capacity: 2)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index 95c72f1e20..407e019326 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -46,7 +46,7 @@ module ActiveRecord
end
# Returns the maximum number of elements in an IN (x,y,z) clause.
- # nil means no limit.
+ # +nil+ means no limit.
def in_clause_length
nil
end
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 aa2dfdd573..faccd1d641 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -115,7 +115,7 @@ module ActiveRecord
# Executes an INSERT query and returns the new record's ID
#
- # +id_value+ will be returned unless the value is nil, in
+ # +id_value+ will be returned unless the value is +nil+, in
# which case the database will attempt to calculate the last inserted
# id and return that value.
#
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 2f8a89e88e..7eab7de5d3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -4,6 +4,9 @@ module ActiveRecord
class << self
def included(base) #:nodoc:
dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
+
+ base.set_callback :checkout, :after, :configure_query_cache!
+ base.set_callback :checkin, :after, :disable_query_cache!
end
def dirties_query_cache(base, *method_names)
@@ -18,11 +21,32 @@ module ActiveRecord
end
end
+ module ConnectionPoolConfiguration
+ def initialize(*)
+ super
+ @query_cache_enabled = Concurrent::Map.new { false }
+ end
+
+ def enable_query_cache!
+ @query_cache_enabled[connection_cache_key(Thread.current)] = true
+ connection.enable_query_cache! if active_connection?
+ end
+
+ def disable_query_cache!
+ @query_cache_enabled.delete connection_cache_key(Thread.current)
+ connection.disable_query_cache! if active_connection?
+ end
+
+ def query_cache_enabled
+ @query_cache_enabled[connection_cache_key(Thread.current)]
+ end
+ end
+
attr_reader :query_cache, :query_cache_enabled
def initialize(*)
super
- @query_cache = Hash.new { |h,sql| h[sql] = {} }
+ @query_cache = Hash.new { |h, sql| h[sql] = {} }
@query_cache_enabled = false
end
@@ -41,6 +65,7 @@ module ActiveRecord
def disable_query_cache!
@query_cache_enabled = false
+ clear_query_cache
end
# Disable the query cache within the block.
@@ -96,6 +121,10 @@ module ActiveRecord
def locked?(arel)
arel.respond_to?(:locked) && arel.locked
end
+
+ def configure_query_cache!
+ enable_query_cache! if pool.query_cache_enabled
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 06c89ca072..dabccc00bb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -35,7 +35,7 @@ module ActiveRecord
end
default = schema_default(column) if column.has_default?
- spec[:default] = default unless default.nil?
+ spec[:default] = default unless default.nil?
spec[:null] = "false" unless column.null
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 1df20a0c56..151629b02a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1116,7 +1116,7 @@ module ActiveRecord
end
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
- if column_name.is_a?(String) && /\W/ === column_name
+ if column_name.is_a?(String) && /\W/.match?(column_name)
column_names = column_name
else
column_names = Array(column_name)
@@ -1199,10 +1199,6 @@ module ActiveRecord
def index_name_for_remove(table_name, options = {})
return options[:name] if can_remove_index_by_name?(options)
- # if the adapter doesn't support the indexes call the best we can do
- # is return the default index name for the options provided
- return index_name(table_name, options) unless respond_to?(:indexes)
-
checks = []
if options.is_a?(Hash)