activerecord 7.2.4 → 8.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1013 -878
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +69 -27
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +2 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +36 -8
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +7 -3
- data/lib/active_record/associations/collection_proxy.rb +26 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency.rb +3 -2
- data/lib/active_record/associations/preloader/association.rb +3 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +18 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/base.rb +1 -2
- data/lib/active_record/callbacks.rb +1 -2
- data/lib/active_record/coders/column_serializer.rb +1 -1
- data/lib/active_record/coders/json.rb +16 -6
- data/lib/active_record/coders/yaml_column.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +429 -93
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +148 -74
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +124 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +129 -63
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +132 -65
- data/lib/active_record/connection_adapters/column.rb +14 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -66
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +79 -61
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +156 -68
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +15 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +104 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +41 -66
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +15 -18
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +25 -2
- data/lib/active_record/core.rb +33 -17
- data/lib/active_record/counter_cache.rb +35 -8
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +74 -9
- data/lib/active_record/database_configurations/url_config.rb +15 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +5 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +8 -8
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
- data/lib/active_record/encryption/encryptor.rb +28 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +33 -30
- data/lib/active_record/errors.rb +33 -9
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +76 -0
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +9 -4
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +31 -29
- data/lib/active_record/message_pack.rb +13 -0
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +56 -13
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +48 -42
- data/lib/active_record/model_schema.rb +40 -15
- data/lib/active_record/nested_attributes.rb +14 -12
- data/lib/active_record/persistence.rb +163 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +100 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +35 -30
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -38
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +54 -22
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +60 -43
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +73 -43
- data/lib/active_record/relation/merger.rb +12 -10
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +23 -8
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +200 -96
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +2 -9
- data/lib/active_record/relation.rb +127 -87
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +17 -10
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +44 -16
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +53 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +83 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +4 -58
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +18 -4
- data/lib/active_record/test_fixtures.rb +52 -6
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +56 -32
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +85 -50
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +4 -22
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +15 -12
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "thread"
|
|
4
3
|
require "concurrent/map"
|
|
5
4
|
require "monitor"
|
|
6
5
|
|
|
@@ -9,27 +8,22 @@ require "active_record/connection_adapters/abstract/connection_pool/reaper"
|
|
|
9
8
|
|
|
10
9
|
module ActiveRecord
|
|
11
10
|
module ConnectionAdapters
|
|
12
|
-
module AbstractPool # :nodoc:
|
|
13
|
-
end
|
|
14
|
-
|
|
15
11
|
class NullPool # :nodoc:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class NullConfig # :nodoc:
|
|
12
|
+
class NullConfig
|
|
19
13
|
def method_missing(...)
|
|
20
14
|
nil
|
|
21
15
|
end
|
|
22
16
|
end
|
|
23
|
-
NULL_CONFIG = NullConfig.new
|
|
17
|
+
NULL_CONFIG = NullConfig.new
|
|
24
18
|
|
|
25
19
|
def initialize
|
|
26
20
|
super()
|
|
27
|
-
@
|
|
21
|
+
@monitor = Monitor.new
|
|
28
22
|
@server_version = nil
|
|
29
23
|
end
|
|
30
24
|
|
|
31
25
|
def server_version(connection) # :nodoc:
|
|
32
|
-
@server_version || @
|
|
26
|
+
@server_version || @monitor.synchronize { @server_version ||= connection.get_database_version }
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
def schema_reflection
|
|
@@ -37,8 +31,8 @@ module ActiveRecord
|
|
|
37
31
|
end
|
|
38
32
|
|
|
39
33
|
def schema_cache; end
|
|
40
|
-
def connection_class; end
|
|
41
34
|
def query_cache; end
|
|
35
|
+
def connection_descriptor; end
|
|
42
36
|
def checkin(_); end
|
|
43
37
|
def remove(_); end
|
|
44
38
|
def async_executor; end
|
|
@@ -50,6 +44,11 @@ module ActiveRecord
|
|
|
50
44
|
def dirties_query_cache
|
|
51
45
|
true
|
|
52
46
|
end
|
|
47
|
+
|
|
48
|
+
def pool_transaction_isolation_level; end
|
|
49
|
+
def pool_transaction_isolation_level=(isolation_level)
|
|
50
|
+
raise NotImplementedError, "This method should never be called"
|
|
51
|
+
end
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
# = Active Record Connection Pool
|
|
@@ -102,13 +101,21 @@ module ActiveRecord
|
|
|
102
101
|
# There are several connection-pooling-related options that you can add to
|
|
103
102
|
# your database connection configuration:
|
|
104
103
|
#
|
|
105
|
-
# * +pool+: maximum number of connections the pool may manage (default 5).
|
|
106
|
-
# * +idle_timeout+: number of seconds that a connection will be kept
|
|
107
|
-
# unused in the pool before it is automatically disconnected (default
|
|
108
|
-
# 300 seconds). Set this to zero to keep connections forever.
|
|
109
104
|
# * +checkout_timeout+: number of seconds to wait for a connection to
|
|
110
105
|
# become available before giving up and raising a timeout error (default
|
|
111
106
|
# 5 seconds).
|
|
107
|
+
# * +idle_timeout+: number of seconds that a connection will be kept
|
|
108
|
+
# unused in the pool before it is automatically disconnected (default
|
|
109
|
+
# 300 seconds). Set this to zero to keep connections forever.
|
|
110
|
+
# * +keepalive+: number of seconds between keepalive checks if the
|
|
111
|
+
# connection has been idle (default 600 seconds).
|
|
112
|
+
# * +max_age+: number of seconds the pool will allow the connection to
|
|
113
|
+
# exist before retiring it at next checkin. (default Float::INFINITY).
|
|
114
|
+
# * +max_connections+: maximum number of connections the pool may manage (default 5).
|
|
115
|
+
# Set to +nil+ or -1 for unlimited connections.
|
|
116
|
+
# * +min_connections+: minimum number of connections the pool will open and maintain (default 0).
|
|
117
|
+
# * +pool_jitter+: maximum reduction factor to apply to +max_age+ and
|
|
118
|
+
# +keepalive+ intervals (default 0.2; range 0.0-1.0).
|
|
112
119
|
#
|
|
113
120
|
#--
|
|
114
121
|
# Synchronization policy:
|
|
@@ -116,6 +123,7 @@ module ActiveRecord
|
|
|
116
123
|
# * access to these instance variables needs to be in +synchronize+:
|
|
117
124
|
# * @connections
|
|
118
125
|
# * @now_connecting
|
|
126
|
+
# * @maintaining
|
|
119
127
|
# * private methods that require being called in a +synchronize+ blocks
|
|
120
128
|
# are now explicitly documented
|
|
121
129
|
class ConnectionPool
|
|
@@ -126,9 +134,9 @@ module ActiveRecord
|
|
|
126
134
|
else
|
|
127
135
|
class WeakThreadKeyMap # :nodoc:
|
|
128
136
|
# FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
|
|
129
|
-
# but it currently
|
|
137
|
+
# but it currently causes GC crashes: https://github.com/byroot/rails/pull/3
|
|
130
138
|
def initialize
|
|
131
|
-
@map =
|
|
139
|
+
@map = Concurrent::Map.new
|
|
132
140
|
end
|
|
133
141
|
|
|
134
142
|
def clear
|
|
@@ -140,7 +148,9 @@ module ActiveRecord
|
|
|
140
148
|
end
|
|
141
149
|
|
|
142
150
|
def []=(key, value)
|
|
143
|
-
@map.
|
|
151
|
+
@map.each_pair do |thread, _|
|
|
152
|
+
@map.delete(thread) unless thread&.alive?
|
|
153
|
+
end
|
|
144
154
|
@map[key] = value
|
|
145
155
|
end
|
|
146
156
|
end
|
|
@@ -172,21 +182,30 @@ module ActiveRecord
|
|
|
172
182
|
end
|
|
173
183
|
end
|
|
174
184
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
if RUBY_ENGINE == "ruby"
|
|
186
|
+
# Thanks to the GVL, the LeaseRegistry doesn't need to be synchronized on MRI
|
|
187
|
+
class LeaseRegistry < WeakThreadKeyMap # :nodoc:
|
|
188
|
+
def [](context)
|
|
189
|
+
super || (self[context] = Lease.new)
|
|
190
|
+
end
|
|
179
191
|
end
|
|
192
|
+
else
|
|
193
|
+
class LeaseRegistry # :nodoc:
|
|
194
|
+
def initialize
|
|
195
|
+
@mutex = Mutex.new
|
|
196
|
+
@map = WeakThreadKeyMap.new
|
|
197
|
+
end
|
|
180
198
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
199
|
+
def [](context)
|
|
200
|
+
@mutex.synchronize do
|
|
201
|
+
@map[context] ||= Lease.new
|
|
202
|
+
end
|
|
184
203
|
end
|
|
185
|
-
end
|
|
186
204
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
205
|
+
def clear
|
|
206
|
+
@mutex.synchronize do
|
|
207
|
+
@map.clear
|
|
208
|
+
end
|
|
190
209
|
end
|
|
191
210
|
end
|
|
192
211
|
end
|
|
@@ -218,10 +237,10 @@ module ActiveRecord
|
|
|
218
237
|
|
|
219
238
|
include MonitorMixin
|
|
220
239
|
prepend QueryCache::ConnectionPoolConfiguration
|
|
221
|
-
include ConnectionAdapters::AbstractPool
|
|
222
240
|
|
|
223
241
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
|
224
|
-
attr_reader :db_config, :
|
|
242
|
+
attr_reader :db_config, :max_connections, :min_connections, :max_age, :keepalive, :reaper, :pool_config, :async_executor, :role, :shard
|
|
243
|
+
alias :size :max_connections
|
|
225
244
|
|
|
226
245
|
delegate :schema_reflection, :server_version, to: :pool_config
|
|
227
246
|
|
|
@@ -241,7 +260,10 @@ module ActiveRecord
|
|
|
241
260
|
|
|
242
261
|
@checkout_timeout = db_config.checkout_timeout
|
|
243
262
|
@idle_timeout = db_config.idle_timeout
|
|
244
|
-
@
|
|
263
|
+
@max_connections = db_config.max_connections
|
|
264
|
+
@min_connections = db_config.min_connections
|
|
265
|
+
@max_age = db_config.max_age
|
|
266
|
+
@keepalive = db_config.keepalive
|
|
245
267
|
|
|
246
268
|
# This variable tracks the cache of threads mapped to reserved connections, with the
|
|
247
269
|
# sole purpose of speeding up the +connection+ method. It is not the authoritative
|
|
@@ -263,6 +285,12 @@ module ActiveRecord
|
|
|
263
285
|
# currently in the process of independently establishing connections to the DB.
|
|
264
286
|
@now_connecting = 0
|
|
265
287
|
|
|
288
|
+
# Sometimes otherwise-idle connections are temporarily held by the Reaper for
|
|
289
|
+
# maintenance. This variable tracks the number of connections currently in that
|
|
290
|
+
# state -- if a thread requests a connection and there are none available, it
|
|
291
|
+
# will await any in-maintenance connections in preference to creating a new one.
|
|
292
|
+
@maintaining = 0
|
|
293
|
+
|
|
266
294
|
@threads_blocking_new_connections = 0
|
|
267
295
|
|
|
268
296
|
@available = ConnectionLeasingQueue.new self
|
|
@@ -273,13 +301,17 @@ module ActiveRecord
|
|
|
273
301
|
|
|
274
302
|
@schema_cache = nil
|
|
275
303
|
|
|
304
|
+
@activated = false
|
|
305
|
+
@original_context = ActiveSupport::IsolatedExecutionState.context
|
|
306
|
+
|
|
307
|
+
@reaper_lock = Monitor.new
|
|
276
308
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
|
277
309
|
@reaper.run
|
|
278
310
|
end
|
|
279
311
|
|
|
280
312
|
def inspect # :nodoc:
|
|
281
|
-
name_field = " name=#{
|
|
282
|
-
shard_field = " shard=#{
|
|
313
|
+
name_field = " name=#{name_inspect}" if name_inspect
|
|
314
|
+
shard_field = " shard=#{shard_inspect}" if shard_inspect
|
|
283
315
|
|
|
284
316
|
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
|
285
317
|
end
|
|
@@ -309,6 +341,14 @@ module ActiveRecord
|
|
|
309
341
|
InternalMetadata.new(self)
|
|
310
342
|
end
|
|
311
343
|
|
|
344
|
+
def activate
|
|
345
|
+
@activated = true
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def activated?
|
|
349
|
+
@activated
|
|
350
|
+
end
|
|
351
|
+
|
|
312
352
|
# Retrieve the connection associated with the current thread, or call
|
|
313
353
|
# #checkout to obtain one if necessary.
|
|
314
354
|
#
|
|
@@ -325,14 +365,6 @@ module ActiveRecord
|
|
|
325
365
|
connection_lease.sticky.nil?
|
|
326
366
|
end
|
|
327
367
|
|
|
328
|
-
def connection
|
|
329
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
|
330
|
-
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
|
331
|
-
and will be removed in Rails 8.0. Use #lease_connection instead.
|
|
332
|
-
MSG
|
|
333
|
-
lease_connection
|
|
334
|
-
end
|
|
335
|
-
|
|
336
368
|
def pin_connection!(lock_thread) # :nodoc:
|
|
337
369
|
@pinned_connection ||= (connection_lease&.connection || checkout)
|
|
338
370
|
@pinned_connections_depth += 1
|
|
@@ -377,8 +409,8 @@ module ActiveRecord
|
|
|
377
409
|
clean
|
|
378
410
|
end
|
|
379
411
|
|
|
380
|
-
def
|
|
381
|
-
pool_config.
|
|
412
|
+
def connection_descriptor # :nodoc:
|
|
413
|
+
pool_config.connection_descriptor
|
|
382
414
|
end
|
|
383
415
|
|
|
384
416
|
# Returns true if there is an open connection being used for the current thread.
|
|
@@ -399,6 +431,8 @@ module ActiveRecord
|
|
|
399
431
|
# #lease_connection or #with_connection methods, connections obtained through
|
|
400
432
|
# #checkout will not be automatically released.
|
|
401
433
|
def release_connection(existing_lease = nil)
|
|
434
|
+
return if self.discarded?
|
|
435
|
+
|
|
402
436
|
if conn = connection_lease.release
|
|
403
437
|
checkin conn
|
|
404
438
|
return true
|
|
@@ -424,18 +458,36 @@ module ActiveRecord
|
|
|
424
458
|
begin
|
|
425
459
|
yield lease.connection
|
|
426
460
|
ensure
|
|
427
|
-
lease.sticky = sticky_was if prevent_permanent_checkout
|
|
461
|
+
lease.sticky = sticky_was if prevent_permanent_checkout
|
|
428
462
|
end
|
|
429
463
|
else
|
|
430
464
|
begin
|
|
431
465
|
yield lease.connection = checkout
|
|
432
466
|
ensure
|
|
433
|
-
lease.sticky = sticky_was if prevent_permanent_checkout
|
|
467
|
+
lease.sticky = sticky_was if prevent_permanent_checkout
|
|
434
468
|
release_connection(lease) unless lease.sticky
|
|
435
469
|
end
|
|
436
470
|
end
|
|
437
471
|
end
|
|
438
472
|
|
|
473
|
+
def with_pool_transaction_isolation_level(isolation_level, transaction_open, &block) # :nodoc:
|
|
474
|
+
if !ActiveRecord.default_transaction_isolation_level.nil?
|
|
475
|
+
begin
|
|
476
|
+
if transaction_open && self.pool_transaction_isolation_level != ActiveRecord.default_transaction_isolation_level
|
|
477
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set default isolation level while transaction is open"
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
old_level = self.pool_transaction_isolation_level
|
|
481
|
+
self.pool_transaction_isolation_level = isolation_level
|
|
482
|
+
yield
|
|
483
|
+
ensure
|
|
484
|
+
self.pool_transaction_isolation_level = old_level
|
|
485
|
+
end
|
|
486
|
+
else
|
|
487
|
+
yield
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
439
491
|
# Returns true if a connection has already been opened.
|
|
440
492
|
def connected?
|
|
441
493
|
synchronize { @connections.any?(&:connected?) }
|
|
@@ -463,18 +515,32 @@ module ActiveRecord
|
|
|
463
515
|
# connections in the pool within a timeout interval (default duration is
|
|
464
516
|
# <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
|
|
465
517
|
def disconnect(raise_on_acquisition_timeout = true)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
518
|
+
@reaper_lock.synchronize do
|
|
519
|
+
return if self.discarded?
|
|
520
|
+
|
|
521
|
+
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
|
|
522
|
+
synchronize do
|
|
523
|
+
return if self.discarded?
|
|
524
|
+
@connections.each do |conn|
|
|
525
|
+
if conn.in_use?
|
|
526
|
+
conn.steal!
|
|
527
|
+
|
|
528
|
+
ActiveSupport.error_reporter.handle(source: "active_record.connection_pool") do
|
|
529
|
+
checkin conn
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
ActiveSupport.error_reporter.handle(source: "active_record.connection_pool") do
|
|
534
|
+
conn.disconnect!
|
|
535
|
+
end
|
|
472
536
|
end
|
|
473
|
-
|
|
537
|
+
@connections = @pinned_connection ? [@pinned_connection] : []
|
|
538
|
+
@leases.clear
|
|
539
|
+
@available.clear
|
|
540
|
+
|
|
541
|
+
# Stop maintaining the minimum size until reactivated
|
|
542
|
+
@activated = false
|
|
474
543
|
end
|
|
475
|
-
@connections = []
|
|
476
|
-
@leases.clear
|
|
477
|
-
@available.clear
|
|
478
544
|
end
|
|
479
545
|
end
|
|
480
546
|
end
|
|
@@ -495,12 +561,15 @@ module ActiveRecord
|
|
|
495
561
|
#
|
|
496
562
|
# See AbstractAdapter#discard!
|
|
497
563
|
def discard! # :nodoc:
|
|
498
|
-
synchronize do
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
564
|
+
@reaper_lock.synchronize do
|
|
565
|
+
synchronize do
|
|
566
|
+
return if self.discarded?
|
|
567
|
+
Reaper.discard_pool(self)
|
|
568
|
+
@connections.each do |conn|
|
|
569
|
+
conn.discard!
|
|
570
|
+
end
|
|
571
|
+
@connections = @available = @leases = nil
|
|
502
572
|
end
|
|
503
|
-
@connections = @available = @leases = nil
|
|
504
573
|
end
|
|
505
574
|
end
|
|
506
575
|
|
|
@@ -508,7 +577,17 @@ module ActiveRecord
|
|
|
508
577
|
@connections.nil?
|
|
509
578
|
end
|
|
510
579
|
|
|
511
|
-
|
|
580
|
+
def maintainable? # :nodoc:
|
|
581
|
+
synchronize do
|
|
582
|
+
@connections&.size&.> 0 || (activated? && @min_connections > 0)
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def reaper_lock(&block) # :nodoc:
|
|
587
|
+
@reaper_lock.synchronize(&block)
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
# Clears reloadable connections from the pool and re-connects connections that
|
|
512
591
|
# require reloading.
|
|
513
592
|
#
|
|
514
593
|
# Raises:
|
|
@@ -531,7 +610,7 @@ module ActiveRecord
|
|
|
531
610
|
end
|
|
532
611
|
end
|
|
533
612
|
|
|
534
|
-
# Clears
|
|
613
|
+
# Clears reloadable connections from the pool and re-connects connections that
|
|
535
614
|
# require reloading.
|
|
536
615
|
#
|
|
537
616
|
# The pool first tries to gain ownership of all connections. If unable to
|
|
@@ -591,11 +670,7 @@ module ActiveRecord
|
|
|
591
670
|
conn.lock.synchronize do
|
|
592
671
|
synchronize do
|
|
593
672
|
connection_lease.clear(conn)
|
|
594
|
-
|
|
595
|
-
conn._run_checkin_callbacks do
|
|
596
|
-
conn.expire
|
|
597
|
-
end
|
|
598
|
-
|
|
673
|
+
conn.expire
|
|
599
674
|
@available.add conn
|
|
600
675
|
end
|
|
601
676
|
end
|
|
@@ -613,7 +688,7 @@ module ActiveRecord
|
|
|
613
688
|
@available.delete conn
|
|
614
689
|
|
|
615
690
|
# @available.any_waiting? => true means that prior to removing this
|
|
616
|
-
# conn, the pool was at its max size (@connections.size == @
|
|
691
|
+
# conn, the pool was at its max size (@connections.size == @max_connections).
|
|
617
692
|
# This would mean that any threads stuck waiting in the queue wouldn't
|
|
618
693
|
# know they could checkout_new_connection, so let's do it for them.
|
|
619
694
|
# Because condition-wait loop is encapsulated in the Queue class
|
|
@@ -621,14 +696,14 @@ module ActiveRecord
|
|
|
621
696
|
# that are "stuck" there are helpless. They have no way of creating
|
|
622
697
|
# new connections and are completely reliant on us feeding available
|
|
623
698
|
# connections into the Queue.
|
|
624
|
-
needs_new_connection = @available.
|
|
699
|
+
needs_new_connection = @available.num_waiting > @maintaining
|
|
625
700
|
end
|
|
626
701
|
|
|
627
702
|
# This is intentionally done outside of the synchronized section as we
|
|
628
703
|
# would like not to hold the main mutex while checking out new connections.
|
|
629
704
|
# Thus there is some chance that needs_new_connection information is now
|
|
630
705
|
# stale, we can live with that (bulk_make_new_connections will make
|
|
631
|
-
# sure not to exceed the pool's @
|
|
706
|
+
# sure not to exceed the pool's @max_connections limit).
|
|
632
707
|
bulk_make_new_connections(1) if needs_new_connection
|
|
633
708
|
end
|
|
634
709
|
|
|
@@ -661,11 +736,27 @@ module ActiveRecord
|
|
|
661
736
|
def flush(minimum_idle = @idle_timeout)
|
|
662
737
|
return if minimum_idle.nil?
|
|
663
738
|
|
|
664
|
-
|
|
739
|
+
removed_connections = synchronize do
|
|
665
740
|
return if self.discarded?
|
|
666
|
-
|
|
741
|
+
|
|
742
|
+
idle_connections = @connections.select do |conn|
|
|
667
743
|
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
|
668
|
-
end.
|
|
744
|
+
end.sort_by { |conn| -conn.seconds_idle } # sort longest idle first
|
|
745
|
+
|
|
746
|
+
# Don't go below our configured pool minimum unless we're flushing
|
|
747
|
+
# everything
|
|
748
|
+
idles_to_retain =
|
|
749
|
+
if minimum_idle > 0
|
|
750
|
+
@min_connections - (@connections.size - idle_connections.size)
|
|
751
|
+
else
|
|
752
|
+
0
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
if idles_to_retain > 0
|
|
756
|
+
idle_connections.pop idles_to_retain
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
idle_connections.each do |conn|
|
|
669
760
|
conn.lease
|
|
670
761
|
|
|
671
762
|
@available.delete conn
|
|
@@ -673,22 +764,111 @@ module ActiveRecord
|
|
|
673
764
|
end
|
|
674
765
|
end
|
|
675
766
|
|
|
676
|
-
|
|
767
|
+
removed_connections.each do |conn|
|
|
677
768
|
conn.disconnect!
|
|
678
769
|
end
|
|
679
770
|
end
|
|
680
771
|
|
|
681
772
|
# Disconnect all currently idle connections. Connections currently checked
|
|
682
|
-
# out are unaffected.
|
|
773
|
+
# out are unaffected. The pool will stop maintaining its minimum size until
|
|
774
|
+
# it is reactivated (such as by a subsequent checkout).
|
|
683
775
|
def flush!
|
|
684
776
|
reap
|
|
685
777
|
flush(-1)
|
|
778
|
+
|
|
779
|
+
# Stop maintaining the minimum size until reactivated
|
|
780
|
+
@activated = false
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
# Ensure that the pool contains at least the configured minimum number of
|
|
784
|
+
# connections.
|
|
785
|
+
def prepopulate
|
|
786
|
+
need_new_connections = nil
|
|
787
|
+
|
|
788
|
+
synchronize do
|
|
789
|
+
return if self.discarded?
|
|
790
|
+
|
|
791
|
+
# We don't want to start prepopulating until we know the pool is wanted,
|
|
792
|
+
# so we can avoid maintaining full pools in one-off scripts etc.
|
|
793
|
+
return unless @activated
|
|
794
|
+
|
|
795
|
+
need_new_connections = @connections.size < @min_connections
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
if need_new_connections
|
|
799
|
+
while new_conn = try_to_checkout_new_connection { @connections.size < @min_connections }
|
|
800
|
+
new_conn.allow_preconnect = true
|
|
801
|
+
checkin(new_conn)
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
def retire_old_connections(max_age = @max_age)
|
|
807
|
+
max_age ||= Float::INFINITY
|
|
808
|
+
|
|
809
|
+
sequential_maintenance -> c { c.connection_age&.>= c.pool_jitter(max_age) } do |conn|
|
|
810
|
+
# Disconnect, then return the adapter to the pool. Preconnect will
|
|
811
|
+
# handle the rest.
|
|
812
|
+
conn.disconnect!
|
|
813
|
+
end
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
# Preconnect all connections in the pool. This saves pool users from
|
|
817
|
+
# having to wait for a connection to be established when first using it
|
|
818
|
+
# after checkout.
|
|
819
|
+
def preconnect
|
|
820
|
+
reaper_lock do
|
|
821
|
+
sequential_maintenance -> c { (!c.connected? || !c.verified?) && c.allow_preconnect } do |conn|
|
|
822
|
+
conn.connect!
|
|
823
|
+
rescue
|
|
824
|
+
# Wholesale rescue: there's nothing we can do but move on. The
|
|
825
|
+
# connection will go back to the pool, and the next consumer will
|
|
826
|
+
# presumably try to connect again -- which will either work, or
|
|
827
|
+
# fail and they'll be able to report the exception.
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
# Prod any connections that have been idle for longer than the configured
|
|
833
|
+
# keepalive time. This will incidentally verify the connection is still
|
|
834
|
+
# alive, but the main purpose is to show the server (and any intermediate
|
|
835
|
+
# network hops) that we're still here and using the connection.
|
|
836
|
+
def keep_alive(threshold = @keepalive)
|
|
837
|
+
return if threshold.nil?
|
|
838
|
+
|
|
839
|
+
sequential_maintenance -> c { (c.seconds_since_last_activity || 0) > c.pool_jitter(threshold) } do |conn|
|
|
840
|
+
# conn.active? will cause some amount of network activity, which is all
|
|
841
|
+
# we need to provide a keepalive signal.
|
|
842
|
+
#
|
|
843
|
+
# If it returns false, the connection is already broken; disconnect,
|
|
844
|
+
# so it can be found and repaired.
|
|
845
|
+
conn.disconnect! unless conn.active?
|
|
846
|
+
end
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
# Immediately mark all current connections as due for replacement,
|
|
850
|
+
# equivalent to them having reached +max_age+ -- even if there is
|
|
851
|
+
# no +max_age+ configured.
|
|
852
|
+
def recycle!
|
|
853
|
+
synchronize do
|
|
854
|
+
return if self.discarded?
|
|
855
|
+
|
|
856
|
+
@connections.each do |conn|
|
|
857
|
+
conn.force_retirement
|
|
858
|
+
end
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
retire_old_connections
|
|
686
862
|
end
|
|
687
863
|
|
|
688
864
|
def num_waiting_in_queue # :nodoc:
|
|
689
865
|
@available.num_waiting
|
|
690
866
|
end
|
|
691
867
|
|
|
868
|
+
def num_available_in_queue # :nodoc:
|
|
869
|
+
@available.size
|
|
870
|
+
end
|
|
871
|
+
|
|
692
872
|
# Returns the connection pool's usage statistic.
|
|
693
873
|
#
|
|
694
874
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
|
@@ -719,6 +899,16 @@ module ActiveRecord
|
|
|
719
899
|
raise ex.set_pool(self)
|
|
720
900
|
end
|
|
721
901
|
|
|
902
|
+
def pool_transaction_isolation_level
|
|
903
|
+
isolation_level_key = "activerecord_pool_transaction_isolation_level_#{db_config.name}"
|
|
904
|
+
ActiveSupport::IsolatedExecutionState[isolation_level_key]
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
def pool_transaction_isolation_level=(isolation_level)
|
|
908
|
+
isolation_level_key = "activerecord_pool_transaction_isolation_level_#{db_config.name}"
|
|
909
|
+
ActiveSupport::IsolatedExecutionState[isolation_level_key] = isolation_level
|
|
910
|
+
end
|
|
911
|
+
|
|
722
912
|
private
|
|
723
913
|
def connection_lease
|
|
724
914
|
@leases[ActiveSupport::IsolatedExecutionState.context]
|
|
@@ -728,7 +918,9 @@ module ActiveRecord
|
|
|
728
918
|
case ActiveRecord.async_query_executor
|
|
729
919
|
when :multi_thread_pool
|
|
730
920
|
if @db_config.max_threads > 0
|
|
921
|
+
name_with_shard = [name_inspect, shard_inspect].join("-").tr("_", "-")
|
|
731
922
|
Concurrent::ThreadPoolExecutor.new(
|
|
923
|
+
name: "ActiveRecord-#{name_with_shard}-async-query-executor",
|
|
732
924
|
min_threads: @db_config.min_threads,
|
|
733
925
|
max_threads: @db_config.max_threads,
|
|
734
926
|
max_queue: @db_config.max_queue,
|
|
@@ -740,11 +932,109 @@ module ActiveRecord
|
|
|
740
932
|
end
|
|
741
933
|
end
|
|
742
934
|
|
|
935
|
+
# Perform maintenance work on pool connections. This method will
|
|
936
|
+
# select a connection to work on by calling the +candidate_selector+
|
|
937
|
+
# proc while holding the pool lock. If a connection is selected, it
|
|
938
|
+
# will be checked out for maintenance and passed to the
|
|
939
|
+
# +maintenance_work+ proc. The connection will always be returned to
|
|
940
|
+
# the pool after the proc completes.
|
|
941
|
+
#
|
|
942
|
+
# If the pool has async threads, all work will be scheduled there.
|
|
943
|
+
# Otherwise, this method will block until all work is complete.
|
|
944
|
+
#
|
|
945
|
+
# Each connection will only be processed once per call to this method,
|
|
946
|
+
# but (particularly in the async case) there is no protection against
|
|
947
|
+
# a second call to this method starting to work through the list
|
|
948
|
+
# before the first call has completed. (Though regular pool behavior
|
|
949
|
+
# will prevent two instances from working on the same specific
|
|
950
|
+
# connection at the same time.)
|
|
951
|
+
def sequential_maintenance(candidate_selector, &maintenance_work)
|
|
952
|
+
# This hash doesn't need to be synchronized, because it's only
|
|
953
|
+
# used by one thread at a time: the +perform_work+ block gives
|
|
954
|
+
# up its right to +connections_visited+ when it schedules the
|
|
955
|
+
# next iteration.
|
|
956
|
+
connections_visited = Hash.new(false)
|
|
957
|
+
connections_visited.compare_by_identity
|
|
958
|
+
|
|
959
|
+
perform_work = lambda do
|
|
960
|
+
connection_to_maintain = nil
|
|
961
|
+
|
|
962
|
+
synchronize do
|
|
963
|
+
unless self.discarded?
|
|
964
|
+
if connection_to_maintain = @connections.select { |conn| !conn.in_use? }.select(&candidate_selector).sort_by(&:seconds_idle).find { |conn| !connections_visited[conn] }
|
|
965
|
+
checkout_for_maintenance connection_to_maintain
|
|
966
|
+
end
|
|
967
|
+
end
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
if connection_to_maintain
|
|
971
|
+
connections_visited[connection_to_maintain] = true
|
|
972
|
+
|
|
973
|
+
# If we're running async, we can schedule the next round of work
|
|
974
|
+
# as soon as we've grabbed a connection to work on.
|
|
975
|
+
@async_executor&.post(&perform_work)
|
|
976
|
+
|
|
977
|
+
begin
|
|
978
|
+
maintenance_work.call connection_to_maintain
|
|
979
|
+
ensure
|
|
980
|
+
return_from_maintenance connection_to_maintain
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
true
|
|
984
|
+
end
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
if @async_executor
|
|
988
|
+
@async_executor.post(&perform_work)
|
|
989
|
+
else
|
|
990
|
+
nil while perform_work.call
|
|
991
|
+
end
|
|
992
|
+
end
|
|
993
|
+
|
|
994
|
+
# Directly check a specific connection out of the pool. Skips callbacks.
|
|
995
|
+
#
|
|
996
|
+
# The connection must later either #return_from_maintenance or
|
|
997
|
+
# #remove_from_maintenance, or the pool will hang.
|
|
998
|
+
def checkout_for_maintenance(conn)
|
|
999
|
+
synchronize do
|
|
1000
|
+
@maintaining += 1
|
|
1001
|
+
@available.delete(conn)
|
|
1002
|
+
conn.lease
|
|
1003
|
+
conn
|
|
1004
|
+
end
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
# Return a connection to the pool after it has been checked out for
|
|
1008
|
+
# maintenance. Does not update the connection's idle time, and skips
|
|
1009
|
+
# callbacks.
|
|
1010
|
+
#--
|
|
1011
|
+
# We assume that a connection that has required maintenance is less
|
|
1012
|
+
# desirable (either it's been idle for a long time, or it was just
|
|
1013
|
+
# created and hasn't been used yet). We'll put it at the back of the
|
|
1014
|
+
# queue.
|
|
1015
|
+
def return_from_maintenance(conn)
|
|
1016
|
+
synchronize do
|
|
1017
|
+
conn.expire(false)
|
|
1018
|
+
@available.add_back(conn)
|
|
1019
|
+
@maintaining -= 1
|
|
1020
|
+
end
|
|
1021
|
+
end
|
|
1022
|
+
|
|
1023
|
+
# Remove a connection from the pool after it has been checked out for
|
|
1024
|
+
# maintenance. It will be automatically replaced with a new connection if
|
|
1025
|
+
# necessary.
|
|
1026
|
+
def remove_from_maintenance(conn)
|
|
1027
|
+
synchronize do
|
|
1028
|
+
@maintaining -= 1
|
|
1029
|
+
remove conn
|
|
1030
|
+
end
|
|
1031
|
+
end
|
|
1032
|
+
|
|
743
1033
|
#--
|
|
744
1034
|
# this is unfortunately not concurrent
|
|
745
1035
|
def bulk_make_new_connections(num_new_conns_needed)
|
|
746
1036
|
num_new_conns_needed.times do
|
|
747
|
-
# try_to_checkout_new_connection will not exceed pool's @
|
|
1037
|
+
# try_to_checkout_new_connection will not exceed pool's @max_connections limit
|
|
748
1038
|
if new_conn = try_to_checkout_new_connection
|
|
749
1039
|
# make the new_conn available to the starving threads stuck @available Queue
|
|
750
1040
|
checkin(new_conn)
|
|
@@ -757,9 +1047,11 @@ module ActiveRecord
|
|
|
757
1047
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
|
758
1048
|
# to be performed outside of the main +synchronize+ block.
|
|
759
1049
|
def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
1050
|
+
@reaper_lock.synchronize do
|
|
1051
|
+
with_new_connections_blocked do
|
|
1052
|
+
attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
|
|
1053
|
+
yield
|
|
1054
|
+
end
|
|
763
1055
|
end
|
|
764
1056
|
end
|
|
765
1057
|
|
|
@@ -879,13 +1171,13 @@ module ActiveRecord
|
|
|
879
1171
|
# <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
|
|
880
1172
|
# and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
|
|
881
1173
|
# of the said methods and avoid an additional +synchronize+ overhead.
|
|
882
|
-
if conn = @available.poll || try_to_checkout_new_connection
|
|
1174
|
+
if conn = @available.poll || try_to_queue_for_background_connection(checkout_timeout) || try_to_checkout_new_connection
|
|
883
1175
|
conn
|
|
884
1176
|
else
|
|
885
1177
|
reap
|
|
886
1178
|
# Retry after reaping, which may return an available connection,
|
|
887
1179
|
# remove an inactive connection, or both
|
|
888
|
-
if conn = @available.poll || try_to_checkout_new_connection
|
|
1180
|
+
if conn = @available.poll || try_to_queue_for_background_connection(checkout_timeout) || try_to_checkout_new_connection
|
|
889
1181
|
conn
|
|
890
1182
|
else
|
|
891
1183
|
@available.poll(checkout_timeout)
|
|
@@ -895,6 +1187,32 @@ module ActiveRecord
|
|
|
895
1187
|
raise ex.set_pool(self)
|
|
896
1188
|
end
|
|
897
1189
|
|
|
1190
|
+
#--
|
|
1191
|
+
# If new connections are already being established in the background,
|
|
1192
|
+
# and there are fewer threads already waiting than the number of
|
|
1193
|
+
# upcoming connections, we can just get in queue and wait to be handed a
|
|
1194
|
+
# connection. This avoids us overshooting the required connection count
|
|
1195
|
+
# by starting a new connection ourselves, and is likely to be faster
|
|
1196
|
+
# too (because at least some of the time it takes to establish a new
|
|
1197
|
+
# connection must have already passed).
|
|
1198
|
+
#
|
|
1199
|
+
# If background connections are available, this method will block and
|
|
1200
|
+
# return a connection. If no background connections are available, it
|
|
1201
|
+
# will immediately return +nil+.
|
|
1202
|
+
def try_to_queue_for_background_connection(checkout_timeout)
|
|
1203
|
+
return unless @maintaining > 0
|
|
1204
|
+
|
|
1205
|
+
synchronize do
|
|
1206
|
+
return unless @maintaining > @available.num_waiting
|
|
1207
|
+
|
|
1208
|
+
# Maintenance work (e.g. a keepalive ping) can stall for reasons outside
|
|
1209
|
+
# our control, such as a slow or unresponsive database server, so this
|
|
1210
|
+
# wait must stay bounded by the caller's own checkout_timeout rather than
|
|
1211
|
+
# assume maintenance always finishes quickly.
|
|
1212
|
+
@available.poll(checkout_timeout)
|
|
1213
|
+
end
|
|
1214
|
+
end
|
|
1215
|
+
|
|
898
1216
|
#--
|
|
899
1217
|
# if owner_thread param is omitted, this must be called in synchronize block
|
|
900
1218
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
|
@@ -904,17 +1222,26 @@ module ActiveRecord
|
|
|
904
1222
|
end
|
|
905
1223
|
alias_method :release, :remove_connection_from_thread_cache
|
|
906
1224
|
|
|
907
|
-
# If the pool is not at a <tt>@
|
|
1225
|
+
# If the pool is not at a <tt>@max_connections</tt> limit, establish new connection. Connecting
|
|
908
1226
|
# to the DB is done outside main synchronized section.
|
|
1227
|
+
#
|
|
1228
|
+
# If a block is supplied, it is an additional constraint (checked while holding the
|
|
1229
|
+
# pool lock) on whether a new connection should be established.
|
|
909
1230
|
#--
|
|
910
1231
|
# Implementation constraint: a newly established connection returned by this
|
|
911
1232
|
# method must be in the +.leased+ state.
|
|
912
1233
|
def try_to_checkout_new_connection
|
|
913
1234
|
# first in synchronized section check if establishing new conns is allowed
|
|
914
|
-
# and increment @now_connecting, to prevent overstepping this pool's @
|
|
1235
|
+
# and increment @now_connecting, to prevent overstepping this pool's @max_connections
|
|
915
1236
|
# constraint
|
|
916
1237
|
do_checkout = synchronize do
|
|
917
|
-
if
|
|
1238
|
+
return if self.discarded?
|
|
1239
|
+
|
|
1240
|
+
if @threads_blocking_new_connections.zero? && (@max_connections.nil? || (@connections.size + @now_connecting) < @max_connections) && (!block_given? || yield)
|
|
1241
|
+
if @connections.size > 0 || @original_context != ActiveSupport::IsolatedExecutionState.context
|
|
1242
|
+
@activated = true
|
|
1243
|
+
end
|
|
1244
|
+
|
|
918
1245
|
@now_connecting += 1
|
|
919
1246
|
end
|
|
920
1247
|
end
|
|
@@ -925,12 +1252,16 @@ module ActiveRecord
|
|
|
925
1252
|
conn = checkout_new_connection
|
|
926
1253
|
ensure
|
|
927
1254
|
synchronize do
|
|
1255
|
+
@now_connecting -= 1
|
|
928
1256
|
if conn
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1257
|
+
if self.discarded?
|
|
1258
|
+
conn.discard!
|
|
1259
|
+
else
|
|
1260
|
+
adopt_connection(conn)
|
|
1261
|
+
# returned conn needs to be already leased
|
|
1262
|
+
conn.lease
|
|
1263
|
+
end
|
|
932
1264
|
end
|
|
933
|
-
@now_connecting -= 1
|
|
934
1265
|
end
|
|
935
1266
|
end
|
|
936
1267
|
end
|
|
@@ -953,15 +1284,20 @@ module ActiveRecord
|
|
|
953
1284
|
end
|
|
954
1285
|
|
|
955
1286
|
def checkout_and_verify(c)
|
|
956
|
-
c.
|
|
957
|
-
c.clean!
|
|
958
|
-
end
|
|
959
|
-
c
|
|
1287
|
+
c.clean!
|
|
960
1288
|
rescue Exception
|
|
961
1289
|
remove c
|
|
962
1290
|
c.disconnect!
|
|
963
1291
|
raise
|
|
964
1292
|
end
|
|
1293
|
+
|
|
1294
|
+
def name_inspect
|
|
1295
|
+
db_config.name.inspect unless db_config.name == "primary"
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
def shard_inspect
|
|
1299
|
+
shard.inspect unless shard == :default
|
|
1300
|
+
end
|
|
965
1301
|
end
|
|
966
1302
|
end
|
|
967
1303
|
end
|