activerecord 7.2.2.1 → 8.1.2
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 +564 -753
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- 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 +10 -8
- data/lib/active_record/associations/collection_proxy.rb +22 -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/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- 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 +17 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +91 -39
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/coders/json.rb +14 -5
- 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 +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +458 -117
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +136 -74
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +44 -11
- 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 +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -29
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +175 -87
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +77 -58
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
- 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 +73 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -11
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
- 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/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 +28 -45
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +140 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -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 +13 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +112 -42
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +2 -19
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +37 -10
- data/lib/active_record/core.rb +61 -25
- data/lib/active_record/counter_cache.rb +34 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +19 -19
- 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 +9 -9
- data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
- data/lib/active_record/encryption/encryptor.rb +49 -28
- 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 +46 -42
- data/lib/active_record/errors.rb +36 -12
- 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 +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +13 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- 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 +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +44 -11
- 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 +50 -43
- data/lib/active_record/model_schema.rb +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +104 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +12 -12
- data/lib/active_record/railtie.rb +37 -32
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -37
- 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 +53 -21
- 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 +80 -63
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +54 -37
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -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 +22 -7
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +156 -95
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +122 -80
- 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_dumper.rb +47 -22
- 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/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +47 -18
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +51 -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 +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- 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 +39 -16
- 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 +15 -2
- data/lib/active_record/type/serialized.rb +11 -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 +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -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/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/table.rb +3 -7
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +6 -22
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +17 -17
- 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,18 +8,13 @@ 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()
|
|
@@ -37,7 +31,8 @@ module ActiveRecord
|
|
|
37
31
|
end
|
|
38
32
|
|
|
39
33
|
def schema_cache; end
|
|
40
|
-
def
|
|
34
|
+
def query_cache; end
|
|
35
|
+
def connection_descriptor; end
|
|
41
36
|
def checkin(_); end
|
|
42
37
|
def remove(_); end
|
|
43
38
|
def async_executor; end
|
|
@@ -49,6 +44,11 @@ module ActiveRecord
|
|
|
49
44
|
def dirties_query_cache
|
|
50
45
|
true
|
|
51
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
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# = Active Record Connection Pool
|
|
@@ -101,13 +101,21 @@ module ActiveRecord
|
|
|
101
101
|
# There are several connection-pooling-related options that you can add to
|
|
102
102
|
# your database connection configuration:
|
|
103
103
|
#
|
|
104
|
-
# * +pool+: maximum number of connections the pool may manage (default 5).
|
|
105
|
-
# * +idle_timeout+: number of seconds that a connection will be kept
|
|
106
|
-
# unused in the pool before it is automatically disconnected (default
|
|
107
|
-
# 300 seconds). Set this to zero to keep connections forever.
|
|
108
104
|
# * +checkout_timeout+: number of seconds to wait for a connection to
|
|
109
105
|
# become available before giving up and raising a timeout error (default
|
|
110
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).
|
|
111
119
|
#
|
|
112
120
|
#--
|
|
113
121
|
# Synchronization policy:
|
|
@@ -115,27 +123,34 @@ module ActiveRecord
|
|
|
115
123
|
# * access to these instance variables needs to be in +synchronize+:
|
|
116
124
|
# * @connections
|
|
117
125
|
# * @now_connecting
|
|
126
|
+
# * @maintaining
|
|
118
127
|
# * private methods that require being called in a +synchronize+ blocks
|
|
119
128
|
# are now explicitly documented
|
|
120
129
|
class ConnectionPool
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
# Prior to 3.3.5, WeakKeyMap had a use after free bug
|
|
131
|
+
# https://bugs.ruby-lang.org/issues/20688
|
|
132
|
+
if ObjectSpace.const_defined?(:WeakKeyMap) && RUBY_VERSION >= "3.3.5"
|
|
133
|
+
WeakThreadKeyMap = ObjectSpace::WeakKeyMap
|
|
134
|
+
else
|
|
135
|
+
class WeakThreadKeyMap # :nodoc:
|
|
136
|
+
# FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
|
|
137
|
+
# but it currently causes GC crashes: https://github.com/byroot/rails/pull/3
|
|
138
|
+
def initialize
|
|
139
|
+
@map = {}
|
|
140
|
+
end
|
|
127
141
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
def clear
|
|
143
|
+
@map.clear
|
|
144
|
+
end
|
|
131
145
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
def [](key)
|
|
147
|
+
@map[key]
|
|
148
|
+
end
|
|
135
149
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
def []=(key, value)
|
|
151
|
+
@map.select! { |c, _| c&.alive? }
|
|
152
|
+
@map[key] = value
|
|
153
|
+
end
|
|
139
154
|
end
|
|
140
155
|
end
|
|
141
156
|
|
|
@@ -165,21 +180,30 @@ module ActiveRecord
|
|
|
165
180
|
end
|
|
166
181
|
end
|
|
167
182
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
183
|
+
if RUBY_ENGINE == "ruby"
|
|
184
|
+
# Thanks to the GVL, the LeaseRegistry doesn't need to be synchronized on MRI
|
|
185
|
+
class LeaseRegistry < WeakThreadKeyMap # :nodoc:
|
|
186
|
+
def [](context)
|
|
187
|
+
super || (self[context] = Lease.new)
|
|
188
|
+
end
|
|
172
189
|
end
|
|
190
|
+
else
|
|
191
|
+
class LeaseRegistry # :nodoc:
|
|
192
|
+
def initialize
|
|
193
|
+
@mutex = Mutex.new
|
|
194
|
+
@map = WeakThreadKeyMap.new
|
|
195
|
+
end
|
|
173
196
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
197
|
+
def [](context)
|
|
198
|
+
@mutex.synchronize do
|
|
199
|
+
@map[context] ||= Lease.new
|
|
200
|
+
end
|
|
177
201
|
end
|
|
178
|
-
end
|
|
179
202
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
203
|
+
def clear
|
|
204
|
+
@mutex.synchronize do
|
|
205
|
+
@map.clear
|
|
206
|
+
end
|
|
183
207
|
end
|
|
184
208
|
end
|
|
185
209
|
end
|
|
@@ -211,10 +235,10 @@ module ActiveRecord
|
|
|
211
235
|
|
|
212
236
|
include MonitorMixin
|
|
213
237
|
prepend QueryCache::ConnectionPoolConfiguration
|
|
214
|
-
include ConnectionAdapters::AbstractPool
|
|
215
238
|
|
|
216
239
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
|
217
|
-
attr_reader :db_config, :
|
|
240
|
+
attr_reader :db_config, :max_connections, :min_connections, :max_age, :keepalive, :reaper, :pool_config, :async_executor, :role, :shard
|
|
241
|
+
alias :size :max_connections
|
|
218
242
|
|
|
219
243
|
delegate :schema_reflection, :server_version, to: :pool_config
|
|
220
244
|
|
|
@@ -234,7 +258,10 @@ module ActiveRecord
|
|
|
234
258
|
|
|
235
259
|
@checkout_timeout = db_config.checkout_timeout
|
|
236
260
|
@idle_timeout = db_config.idle_timeout
|
|
237
|
-
@
|
|
261
|
+
@max_connections = db_config.max_connections
|
|
262
|
+
@min_connections = db_config.min_connections
|
|
263
|
+
@max_age = db_config.max_age
|
|
264
|
+
@keepalive = db_config.keepalive
|
|
238
265
|
|
|
239
266
|
# This variable tracks the cache of threads mapped to reserved connections, with the
|
|
240
267
|
# sole purpose of speeding up the +connection+ method. It is not the authoritative
|
|
@@ -256,6 +283,12 @@ module ActiveRecord
|
|
|
256
283
|
# currently in the process of independently establishing connections to the DB.
|
|
257
284
|
@now_connecting = 0
|
|
258
285
|
|
|
286
|
+
# Sometimes otherwise-idle connections are temporarily held by the Reaper for
|
|
287
|
+
# maintenance. This variable tracks the number of connections currently in that
|
|
288
|
+
# state -- if a thread requests a connection and there are none available, it
|
|
289
|
+
# will await any in-maintenance connections in preference to creating a new one.
|
|
290
|
+
@maintaining = 0
|
|
291
|
+
|
|
259
292
|
@threads_blocking_new_connections = 0
|
|
260
293
|
|
|
261
294
|
@available = ConnectionLeasingQueue.new self
|
|
@@ -266,13 +299,17 @@ module ActiveRecord
|
|
|
266
299
|
|
|
267
300
|
@schema_cache = nil
|
|
268
301
|
|
|
302
|
+
@activated = false
|
|
303
|
+
@original_context = ActiveSupport::IsolatedExecutionState.context
|
|
304
|
+
|
|
305
|
+
@reaper_lock = Monitor.new
|
|
269
306
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
|
270
307
|
@reaper.run
|
|
271
308
|
end
|
|
272
309
|
|
|
273
310
|
def inspect # :nodoc:
|
|
274
|
-
name_field = " name=#{
|
|
275
|
-
shard_field = " shard=#{
|
|
311
|
+
name_field = " name=#{name_inspect}" if name_inspect
|
|
312
|
+
shard_field = " shard=#{shard_inspect}" if shard_inspect
|
|
276
313
|
|
|
277
314
|
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
|
278
315
|
end
|
|
@@ -302,6 +339,14 @@ module ActiveRecord
|
|
|
302
339
|
InternalMetadata.new(self)
|
|
303
340
|
end
|
|
304
341
|
|
|
342
|
+
def activate
|
|
343
|
+
@activated = true
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def activated?
|
|
347
|
+
@activated
|
|
348
|
+
end
|
|
349
|
+
|
|
305
350
|
# Retrieve the connection associated with the current thread, or call
|
|
306
351
|
# #checkout to obtain one if necessary.
|
|
307
352
|
#
|
|
@@ -309,22 +354,15 @@ module ActiveRecord
|
|
|
309
354
|
# held in a cache keyed by a thread.
|
|
310
355
|
def lease_connection
|
|
311
356
|
lease = connection_lease
|
|
312
|
-
lease.sticky = true
|
|
313
357
|
lease.connection ||= checkout
|
|
358
|
+
lease.sticky = true
|
|
359
|
+
lease.connection
|
|
314
360
|
end
|
|
315
361
|
|
|
316
362
|
def permanent_lease? # :nodoc:
|
|
317
363
|
connection_lease.sticky.nil?
|
|
318
364
|
end
|
|
319
365
|
|
|
320
|
-
def connection
|
|
321
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
|
322
|
-
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
|
323
|
-
and will be removed in Rails 8.0. Use #lease_connection instead.
|
|
324
|
-
MSG
|
|
325
|
-
lease_connection
|
|
326
|
-
end
|
|
327
|
-
|
|
328
366
|
def pin_connection!(lock_thread) # :nodoc:
|
|
329
367
|
@pinned_connection ||= (connection_lease&.connection || checkout)
|
|
330
368
|
@pinned_connections_depth += 1
|
|
@@ -336,6 +374,7 @@ module ActiveRecord
|
|
|
336
374
|
end
|
|
337
375
|
|
|
338
376
|
@pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
|
|
377
|
+
@pinned_connection.pinned = true
|
|
339
378
|
@pinned_connection.verify! # eagerly validate the connection
|
|
340
379
|
@pinned_connection.begin_transaction joinable: false, _lazy: false
|
|
341
380
|
end
|
|
@@ -358,6 +397,7 @@ module ActiveRecord
|
|
|
358
397
|
end
|
|
359
398
|
|
|
360
399
|
if @pinned_connection.nil?
|
|
400
|
+
connection.pinned = false
|
|
361
401
|
connection.steal!
|
|
362
402
|
connection.lock_thread = nil
|
|
363
403
|
checkin(connection)
|
|
@@ -367,8 +407,8 @@ module ActiveRecord
|
|
|
367
407
|
clean
|
|
368
408
|
end
|
|
369
409
|
|
|
370
|
-
def
|
|
371
|
-
pool_config.
|
|
410
|
+
def connection_descriptor # :nodoc:
|
|
411
|
+
pool_config.connection_descriptor
|
|
372
412
|
end
|
|
373
413
|
|
|
374
414
|
# Returns true if there is an open connection being used for the current thread.
|
|
@@ -389,6 +429,8 @@ module ActiveRecord
|
|
|
389
429
|
# #lease_connection or #with_connection methods, connections obtained through
|
|
390
430
|
# #checkout will not be automatically released.
|
|
391
431
|
def release_connection(existing_lease = nil)
|
|
432
|
+
return if self.discarded?
|
|
433
|
+
|
|
392
434
|
if conn = connection_lease.release
|
|
393
435
|
checkin conn
|
|
394
436
|
return true
|
|
@@ -426,6 +468,24 @@ module ActiveRecord
|
|
|
426
468
|
end
|
|
427
469
|
end
|
|
428
470
|
|
|
471
|
+
def with_pool_transaction_isolation_level(isolation_level, transaction_open, &block) # :nodoc:
|
|
472
|
+
if !ActiveRecord.default_transaction_isolation_level.nil?
|
|
473
|
+
begin
|
|
474
|
+
if transaction_open && self.pool_transaction_isolation_level != ActiveRecord.default_transaction_isolation_level
|
|
475
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set default isolation level while transaction is open"
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
old_level = self.pool_transaction_isolation_level
|
|
479
|
+
self.pool_transaction_isolation_level = isolation_level
|
|
480
|
+
yield
|
|
481
|
+
ensure
|
|
482
|
+
self.pool_transaction_isolation_level = old_level
|
|
483
|
+
end
|
|
484
|
+
else
|
|
485
|
+
yield
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
429
489
|
# Returns true if a connection has already been opened.
|
|
430
490
|
def connected?
|
|
431
491
|
synchronize { @connections.any?(&:connected?) }
|
|
@@ -453,18 +513,26 @@ module ActiveRecord
|
|
|
453
513
|
# connections in the pool within a timeout interval (default duration is
|
|
454
514
|
# <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
|
|
455
515
|
def disconnect(raise_on_acquisition_timeout = true)
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
516
|
+
@reaper_lock.synchronize do
|
|
517
|
+
return if self.discarded?
|
|
518
|
+
|
|
519
|
+
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
|
|
520
|
+
synchronize do
|
|
521
|
+
return if self.discarded?
|
|
522
|
+
@connections.each do |conn|
|
|
523
|
+
if conn.in_use?
|
|
524
|
+
conn.steal!
|
|
525
|
+
checkin conn
|
|
526
|
+
end
|
|
527
|
+
conn.disconnect!
|
|
462
528
|
end
|
|
463
|
-
|
|
529
|
+
@connections = []
|
|
530
|
+
@leases.clear
|
|
531
|
+
@available.clear
|
|
532
|
+
|
|
533
|
+
# Stop maintaining the minimum size until reactivated
|
|
534
|
+
@activated = false
|
|
464
535
|
end
|
|
465
|
-
@connections = []
|
|
466
|
-
@leases.clear
|
|
467
|
-
@available.clear
|
|
468
536
|
end
|
|
469
537
|
end
|
|
470
538
|
end
|
|
@@ -485,12 +553,14 @@ module ActiveRecord
|
|
|
485
553
|
#
|
|
486
554
|
# See AbstractAdapter#discard!
|
|
487
555
|
def discard! # :nodoc:
|
|
488
|
-
synchronize do
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
conn
|
|
556
|
+
@reaper_lock.synchronize do
|
|
557
|
+
synchronize do
|
|
558
|
+
return if self.discarded?
|
|
559
|
+
@connections.each do |conn|
|
|
560
|
+
conn.discard!
|
|
561
|
+
end
|
|
562
|
+
@connections = @available = @leases = nil
|
|
492
563
|
end
|
|
493
|
-
@connections = @available = @leases = nil
|
|
494
564
|
end
|
|
495
565
|
end
|
|
496
566
|
|
|
@@ -498,7 +568,17 @@ module ActiveRecord
|
|
|
498
568
|
@connections.nil?
|
|
499
569
|
end
|
|
500
570
|
|
|
501
|
-
|
|
571
|
+
def maintainable? # :nodoc:
|
|
572
|
+
synchronize do
|
|
573
|
+
@connections&.size&.> 0 || (activated? && @min_connections > 0)
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def reaper_lock(&block) # :nodoc:
|
|
578
|
+
@reaper_lock.synchronize(&block)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Clears reloadable connections from the pool and re-connects connections that
|
|
502
582
|
# require reloading.
|
|
503
583
|
#
|
|
504
584
|
# Raises:
|
|
@@ -521,7 +601,7 @@ module ActiveRecord
|
|
|
521
601
|
end
|
|
522
602
|
end
|
|
523
603
|
|
|
524
|
-
# Clears
|
|
604
|
+
# Clears reloadable connections from the pool and re-connects connections that
|
|
525
605
|
# require reloading.
|
|
526
606
|
#
|
|
527
607
|
# The pool first tries to gain ownership of all connections. If unable to
|
|
@@ -548,20 +628,25 @@ module ActiveRecord
|
|
|
548
628
|
# Raises:
|
|
549
629
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
|
550
630
|
def checkout(checkout_timeout = @checkout_timeout)
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
631
|
+
return checkout_and_verify(acquire_connection(checkout_timeout)) unless @pinned_connection
|
|
632
|
+
|
|
633
|
+
@pinned_connection.lock.synchronize do
|
|
634
|
+
synchronize do
|
|
635
|
+
# The pinned connection may have been cleaned up before we synchronized, so check if it is still present
|
|
636
|
+
if @pinned_connection
|
|
554
637
|
@pinned_connection.verify!
|
|
638
|
+
|
|
555
639
|
# Any leased connection must be in @connections otherwise
|
|
556
640
|
# some methods like #connected? won't behave correctly
|
|
557
641
|
unless @connections.include?(@pinned_connection)
|
|
558
642
|
@connections << @pinned_connection
|
|
559
643
|
end
|
|
644
|
+
|
|
645
|
+
@pinned_connection
|
|
646
|
+
else
|
|
647
|
+
checkout_and_verify(acquire_connection(checkout_timeout))
|
|
560
648
|
end
|
|
561
649
|
end
|
|
562
|
-
@pinned_connection
|
|
563
|
-
else
|
|
564
|
-
checkout_and_verify(acquire_connection(checkout_timeout))
|
|
565
650
|
end
|
|
566
651
|
end
|
|
567
652
|
|
|
@@ -576,11 +661,7 @@ module ActiveRecord
|
|
|
576
661
|
conn.lock.synchronize do
|
|
577
662
|
synchronize do
|
|
578
663
|
connection_lease.clear(conn)
|
|
579
|
-
|
|
580
|
-
conn._run_checkin_callbacks do
|
|
581
|
-
conn.expire
|
|
582
|
-
end
|
|
583
|
-
|
|
664
|
+
conn.expire
|
|
584
665
|
@available.add conn
|
|
585
666
|
end
|
|
586
667
|
end
|
|
@@ -598,7 +679,7 @@ module ActiveRecord
|
|
|
598
679
|
@available.delete conn
|
|
599
680
|
|
|
600
681
|
# @available.any_waiting? => true means that prior to removing this
|
|
601
|
-
# conn, the pool was at its max size (@connections.size == @
|
|
682
|
+
# conn, the pool was at its max size (@connections.size == @max_connections).
|
|
602
683
|
# This would mean that any threads stuck waiting in the queue wouldn't
|
|
603
684
|
# know they could checkout_new_connection, so let's do it for them.
|
|
604
685
|
# Because condition-wait loop is encapsulated in the Queue class
|
|
@@ -606,14 +687,14 @@ module ActiveRecord
|
|
|
606
687
|
# that are "stuck" there are helpless. They have no way of creating
|
|
607
688
|
# new connections and are completely reliant on us feeding available
|
|
608
689
|
# connections into the Queue.
|
|
609
|
-
needs_new_connection = @available.
|
|
690
|
+
needs_new_connection = @available.num_waiting > @maintaining
|
|
610
691
|
end
|
|
611
692
|
|
|
612
693
|
# This is intentionally done outside of the synchronized section as we
|
|
613
694
|
# would like not to hold the main mutex while checking out new connections.
|
|
614
695
|
# Thus there is some chance that needs_new_connection information is now
|
|
615
696
|
# stale, we can live with that (bulk_make_new_connections will make
|
|
616
|
-
# sure not to exceed the pool's @
|
|
697
|
+
# sure not to exceed the pool's @max_connections limit).
|
|
617
698
|
bulk_make_new_connections(1) if needs_new_connection
|
|
618
699
|
end
|
|
619
700
|
|
|
@@ -646,11 +727,27 @@ module ActiveRecord
|
|
|
646
727
|
def flush(minimum_idle = @idle_timeout)
|
|
647
728
|
return if minimum_idle.nil?
|
|
648
729
|
|
|
649
|
-
|
|
730
|
+
removed_connections = synchronize do
|
|
650
731
|
return if self.discarded?
|
|
651
|
-
|
|
732
|
+
|
|
733
|
+
idle_connections = @connections.select do |conn|
|
|
652
734
|
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
|
653
|
-
end.
|
|
735
|
+
end.sort_by { |conn| -conn.seconds_idle } # sort longest idle first
|
|
736
|
+
|
|
737
|
+
# Don't go below our configured pool minimum unless we're flushing
|
|
738
|
+
# everything
|
|
739
|
+
idles_to_retain =
|
|
740
|
+
if minimum_idle > 0
|
|
741
|
+
@min_connections - (@connections.size - idle_connections.size)
|
|
742
|
+
else
|
|
743
|
+
0
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
if idles_to_retain > 0
|
|
747
|
+
idle_connections.pop idles_to_retain
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
idle_connections.each do |conn|
|
|
654
751
|
conn.lease
|
|
655
752
|
|
|
656
753
|
@available.delete conn
|
|
@@ -658,22 +755,109 @@ module ActiveRecord
|
|
|
658
755
|
end
|
|
659
756
|
end
|
|
660
757
|
|
|
661
|
-
|
|
758
|
+
removed_connections.each do |conn|
|
|
662
759
|
conn.disconnect!
|
|
663
760
|
end
|
|
664
761
|
end
|
|
665
762
|
|
|
666
763
|
# Disconnect all currently idle connections. Connections currently checked
|
|
667
|
-
# out are unaffected.
|
|
764
|
+
# out are unaffected. The pool will stop maintaining its minimum size until
|
|
765
|
+
# it is reactivated (such as by a subsequent checkout).
|
|
668
766
|
def flush!
|
|
669
767
|
reap
|
|
670
768
|
flush(-1)
|
|
769
|
+
|
|
770
|
+
# Stop maintaining the minimum size until reactivated
|
|
771
|
+
@activated = false
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
# Ensure that the pool contains at least the configured minimum number of
|
|
775
|
+
# connections.
|
|
776
|
+
def prepopulate
|
|
777
|
+
need_new_connections = nil
|
|
778
|
+
|
|
779
|
+
synchronize do
|
|
780
|
+
return if self.discarded?
|
|
781
|
+
|
|
782
|
+
# We don't want to start prepopulating until we know the pool is wanted,
|
|
783
|
+
# so we can avoid maintaining full pools in one-off scripts etc.
|
|
784
|
+
return unless @activated
|
|
785
|
+
|
|
786
|
+
need_new_connections = @connections.size < @min_connections
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
if need_new_connections
|
|
790
|
+
while new_conn = try_to_checkout_new_connection { @connections.size < @min_connections }
|
|
791
|
+
new_conn.allow_preconnect = true
|
|
792
|
+
checkin(new_conn)
|
|
793
|
+
end
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
def retire_old_connections(max_age = @max_age)
|
|
798
|
+
max_age ||= Float::INFINITY
|
|
799
|
+
|
|
800
|
+
sequential_maintenance -> c { c.connection_age&.>= c.pool_jitter(max_age) } do |conn|
|
|
801
|
+
# Disconnect, then return the adapter to the pool. Preconnect will
|
|
802
|
+
# handle the rest.
|
|
803
|
+
conn.disconnect!
|
|
804
|
+
end
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
# Preconnect all connections in the pool. This saves pool users from
|
|
808
|
+
# having to wait for a connection to be established when first using it
|
|
809
|
+
# after checkout.
|
|
810
|
+
def preconnect
|
|
811
|
+
sequential_maintenance -> c { (!c.connected? || !c.verified?) && c.allow_preconnect } do |conn|
|
|
812
|
+
conn.connect!
|
|
813
|
+
rescue
|
|
814
|
+
# Wholesale rescue: there's nothing we can do but move on. The
|
|
815
|
+
# connection will go back to the pool, and the next consumer will
|
|
816
|
+
# presumably try to connect again -- which will either work, or
|
|
817
|
+
# fail and they'll be able to report the exception.
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
# Prod any connections that have been idle for longer than the configured
|
|
822
|
+
# keepalive time. This will incidentally verify the connection is still
|
|
823
|
+
# alive, but the main purpose is to show the server (and any intermediate
|
|
824
|
+
# network hops) that we're still here and using the connection.
|
|
825
|
+
def keep_alive(threshold = @keepalive)
|
|
826
|
+
return if threshold.nil?
|
|
827
|
+
|
|
828
|
+
sequential_maintenance -> c { (c.seconds_since_last_activity || 0) > c.pool_jitter(threshold) } do |conn|
|
|
829
|
+
# conn.active? will cause some amount of network activity, which is all
|
|
830
|
+
# we need to provide a keepalive signal.
|
|
831
|
+
#
|
|
832
|
+
# If it returns false, the connection is already broken; disconnect,
|
|
833
|
+
# so it can be found and repaired.
|
|
834
|
+
conn.disconnect! unless conn.active?
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# Immediately mark all current connections as due for replacement,
|
|
839
|
+
# equivalent to them having reached +max_age+ -- even if there is
|
|
840
|
+
# no +max_age+ configured.
|
|
841
|
+
def recycle!
|
|
842
|
+
synchronize do
|
|
843
|
+
return if self.discarded?
|
|
844
|
+
|
|
845
|
+
@connections.each do |conn|
|
|
846
|
+
conn.force_retirement
|
|
847
|
+
end
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
retire_old_connections
|
|
671
851
|
end
|
|
672
852
|
|
|
673
853
|
def num_waiting_in_queue # :nodoc:
|
|
674
854
|
@available.num_waiting
|
|
675
855
|
end
|
|
676
856
|
|
|
857
|
+
def num_available_in_queue # :nodoc:
|
|
858
|
+
@available.size
|
|
859
|
+
end
|
|
860
|
+
|
|
677
861
|
# Returns the connection pool's usage statistic.
|
|
678
862
|
#
|
|
679
863
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
|
@@ -696,6 +880,24 @@ module ActiveRecord
|
|
|
696
880
|
Thread.pass
|
|
697
881
|
end
|
|
698
882
|
|
|
883
|
+
def new_connection # :nodoc:
|
|
884
|
+
connection = db_config.new_connection
|
|
885
|
+
connection.pool = self
|
|
886
|
+
connection
|
|
887
|
+
rescue ConnectionNotEstablished => ex
|
|
888
|
+
raise ex.set_pool(self)
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
def pool_transaction_isolation_level
|
|
892
|
+
isolation_level_key = "activerecord_pool_transaction_isolation_level_#{db_config.name}"
|
|
893
|
+
ActiveSupport::IsolatedExecutionState[isolation_level_key]
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
def pool_transaction_isolation_level=(isolation_level)
|
|
897
|
+
isolation_level_key = "activerecord_pool_transaction_isolation_level_#{db_config.name}"
|
|
898
|
+
ActiveSupport::IsolatedExecutionState[isolation_level_key] = isolation_level
|
|
899
|
+
end
|
|
900
|
+
|
|
699
901
|
private
|
|
700
902
|
def connection_lease
|
|
701
903
|
@leases[ActiveSupport::IsolatedExecutionState.context]
|
|
@@ -705,7 +907,9 @@ module ActiveRecord
|
|
|
705
907
|
case ActiveRecord.async_query_executor
|
|
706
908
|
when :multi_thread_pool
|
|
707
909
|
if @db_config.max_threads > 0
|
|
910
|
+
name_with_shard = [name_inspect, shard_inspect].join("-").tr("_", "-")
|
|
708
911
|
Concurrent::ThreadPoolExecutor.new(
|
|
912
|
+
name: "ActiveRecord-#{name_with_shard}-async-query-executor",
|
|
709
913
|
min_threads: @db_config.min_threads,
|
|
710
914
|
max_threads: @db_config.max_threads,
|
|
711
915
|
max_queue: @db_config.max_queue,
|
|
@@ -717,11 +921,109 @@ module ActiveRecord
|
|
|
717
921
|
end
|
|
718
922
|
end
|
|
719
923
|
|
|
924
|
+
# Perform maintenance work on pool connections. This method will
|
|
925
|
+
# select a connection to work on by calling the +candidate_selector+
|
|
926
|
+
# proc while holding the pool lock. If a connection is selected, it
|
|
927
|
+
# will be checked out for maintenance and passed to the
|
|
928
|
+
# +maintenance_work+ proc. The connection will always be returned to
|
|
929
|
+
# the pool after the proc completes.
|
|
930
|
+
#
|
|
931
|
+
# If the pool has async threads, all work will be scheduled there.
|
|
932
|
+
# Otherwise, this method will block until all work is complete.
|
|
933
|
+
#
|
|
934
|
+
# Each connection will only be processed once per call to this method,
|
|
935
|
+
# but (particularly in the async case) there is no protection against
|
|
936
|
+
# a second call to this method starting to work through the list
|
|
937
|
+
# before the first call has completed. (Though regular pool behavior
|
|
938
|
+
# will prevent two instances from working on the same specific
|
|
939
|
+
# connection at the same time.)
|
|
940
|
+
def sequential_maintenance(candidate_selector, &maintenance_work)
|
|
941
|
+
# This hash doesn't need to be synchronized, because it's only
|
|
942
|
+
# used by one thread at a time: the +perform_work+ block gives
|
|
943
|
+
# up its right to +connections_visited+ when it schedules the
|
|
944
|
+
# next iteration.
|
|
945
|
+
connections_visited = Hash.new(false)
|
|
946
|
+
connections_visited.compare_by_identity
|
|
947
|
+
|
|
948
|
+
perform_work = lambda do
|
|
949
|
+
connection_to_maintain = nil
|
|
950
|
+
|
|
951
|
+
synchronize do
|
|
952
|
+
unless self.discarded?
|
|
953
|
+
if connection_to_maintain = @connections.select { |conn| !conn.in_use? }.select(&candidate_selector).sort_by(&:seconds_idle).find { |conn| !connections_visited[conn] }
|
|
954
|
+
checkout_for_maintenance connection_to_maintain
|
|
955
|
+
end
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
if connection_to_maintain
|
|
960
|
+
connections_visited[connection_to_maintain] = true
|
|
961
|
+
|
|
962
|
+
# If we're running async, we can schedule the next round of work
|
|
963
|
+
# as soon as we've grabbed a connection to work on.
|
|
964
|
+
@async_executor&.post(&perform_work)
|
|
965
|
+
|
|
966
|
+
begin
|
|
967
|
+
maintenance_work.call connection_to_maintain
|
|
968
|
+
ensure
|
|
969
|
+
return_from_maintenance connection_to_maintain
|
|
970
|
+
end
|
|
971
|
+
|
|
972
|
+
true
|
|
973
|
+
end
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
if @async_executor
|
|
977
|
+
@async_executor.post(&perform_work)
|
|
978
|
+
else
|
|
979
|
+
nil while perform_work.call
|
|
980
|
+
end
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
# Directly check a specific connection out of the pool. Skips callbacks.
|
|
984
|
+
#
|
|
985
|
+
# The connection must later either #return_from_maintenance or
|
|
986
|
+
# #remove_from_maintenance, or the pool will hang.
|
|
987
|
+
def checkout_for_maintenance(conn)
|
|
988
|
+
synchronize do
|
|
989
|
+
@maintaining += 1
|
|
990
|
+
@available.delete(conn)
|
|
991
|
+
conn.lease
|
|
992
|
+
conn
|
|
993
|
+
end
|
|
994
|
+
end
|
|
995
|
+
|
|
996
|
+
# Return a connection to the pool after it has been checked out for
|
|
997
|
+
# maintenance. Does not update the connection's idle time, and skips
|
|
998
|
+
# callbacks.
|
|
999
|
+
#--
|
|
1000
|
+
# We assume that a connection that has required maintenance is less
|
|
1001
|
+
# desirable (either it's been idle for a long time, or it was just
|
|
1002
|
+
# created and hasn't been used yet). We'll put it at the back of the
|
|
1003
|
+
# queue.
|
|
1004
|
+
def return_from_maintenance(conn)
|
|
1005
|
+
synchronize do
|
|
1006
|
+
conn.expire(false)
|
|
1007
|
+
@available.add_back(conn)
|
|
1008
|
+
@maintaining -= 1
|
|
1009
|
+
end
|
|
1010
|
+
end
|
|
1011
|
+
|
|
1012
|
+
# Remove a connection from the pool after it has been checked out for
|
|
1013
|
+
# maintenance. It will be automatically replaced with a new connection if
|
|
1014
|
+
# necessary.
|
|
1015
|
+
def remove_from_maintenance(conn)
|
|
1016
|
+
synchronize do
|
|
1017
|
+
@maintaining -= 1
|
|
1018
|
+
remove conn
|
|
1019
|
+
end
|
|
1020
|
+
end
|
|
1021
|
+
|
|
720
1022
|
#--
|
|
721
1023
|
# this is unfortunately not concurrent
|
|
722
1024
|
def bulk_make_new_connections(num_new_conns_needed)
|
|
723
1025
|
num_new_conns_needed.times do
|
|
724
|
-
# try_to_checkout_new_connection will not exceed pool's @
|
|
1026
|
+
# try_to_checkout_new_connection will not exceed pool's @max_connections limit
|
|
725
1027
|
if new_conn = try_to_checkout_new_connection
|
|
726
1028
|
# make the new_conn available to the starving threads stuck @available Queue
|
|
727
1029
|
checkin(new_conn)
|
|
@@ -734,9 +1036,11 @@ module ActiveRecord
|
|
|
734
1036
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
|
735
1037
|
# to be performed outside of the main +synchronize+ block.
|
|
736
1038
|
def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
1039
|
+
@reaper_lock.synchronize do
|
|
1040
|
+
with_new_connections_blocked do
|
|
1041
|
+
attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
|
|
1042
|
+
yield
|
|
1043
|
+
end
|
|
740
1044
|
end
|
|
741
1045
|
end
|
|
742
1046
|
|
|
@@ -856,13 +1160,13 @@ module ActiveRecord
|
|
|
856
1160
|
# <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
|
|
857
1161
|
# and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
|
|
858
1162
|
# of the said methods and avoid an additional +synchronize+ overhead.
|
|
859
|
-
if conn = @available.poll || try_to_checkout_new_connection
|
|
1163
|
+
if conn = @available.poll || try_to_queue_for_background_connection(checkout_timeout) || try_to_checkout_new_connection
|
|
860
1164
|
conn
|
|
861
1165
|
else
|
|
862
1166
|
reap
|
|
863
1167
|
# Retry after reaping, which may return an available connection,
|
|
864
1168
|
# remove an inactive connection, or both
|
|
865
|
-
if conn = @available.poll || try_to_checkout_new_connection
|
|
1169
|
+
if conn = @available.poll || try_to_queue_for_background_connection(checkout_timeout) || try_to_checkout_new_connection
|
|
866
1170
|
conn
|
|
867
1171
|
else
|
|
868
1172
|
@available.poll(checkout_timeout)
|
|
@@ -872,32 +1176,60 @@ module ActiveRecord
|
|
|
872
1176
|
raise ex.set_pool(self)
|
|
873
1177
|
end
|
|
874
1178
|
|
|
1179
|
+
#--
|
|
1180
|
+
# If new connections are already being established in the background,
|
|
1181
|
+
# and there are fewer threads already waiting than the number of
|
|
1182
|
+
# upcoming connections, we can just get in queue and wait to be handed a
|
|
1183
|
+
# connection. This avoids us overshooting the required connection count
|
|
1184
|
+
# by starting a new connection ourselves, and is likely to be faster
|
|
1185
|
+
# too (because at least some of the time it takes to establish a new
|
|
1186
|
+
# connection must have already passed).
|
|
1187
|
+
#
|
|
1188
|
+
# If background connections are available, this method will block and
|
|
1189
|
+
# return a connection. If no background connections are available, it
|
|
1190
|
+
# will immediately return +nil+.
|
|
1191
|
+
def try_to_queue_for_background_connection(checkout_timeout)
|
|
1192
|
+
return unless @maintaining > 0
|
|
1193
|
+
|
|
1194
|
+
synchronize do
|
|
1195
|
+
return unless @maintaining > @available.num_waiting
|
|
1196
|
+
|
|
1197
|
+
# We are guaranteed the "maintaining" thread will return its promised
|
|
1198
|
+
# connection within one maintenance-unit of time. Thus we can safely
|
|
1199
|
+
# do a blocking wait with (functionally) no timeout.
|
|
1200
|
+
@available.poll(100)
|
|
1201
|
+
end
|
|
1202
|
+
end
|
|
1203
|
+
|
|
875
1204
|
#--
|
|
876
1205
|
# if owner_thread param is omitted, this must be called in synchronize block
|
|
877
1206
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
|
878
|
-
|
|
1207
|
+
if owner_thread
|
|
1208
|
+
@leases[owner_thread].clear(conn)
|
|
1209
|
+
end
|
|
879
1210
|
end
|
|
880
1211
|
alias_method :release, :remove_connection_from_thread_cache
|
|
881
1212
|
|
|
882
|
-
|
|
883
|
-
connection = db_config.new_connection
|
|
884
|
-
connection.pool = self
|
|
885
|
-
connection
|
|
886
|
-
rescue ConnectionNotEstablished => ex
|
|
887
|
-
raise ex.set_pool(self)
|
|
888
|
-
end
|
|
889
|
-
|
|
890
|
-
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
|
1213
|
+
# If the pool is not at a <tt>@max_connections</tt> limit, establish new connection. Connecting
|
|
891
1214
|
# to the DB is done outside main synchronized section.
|
|
1215
|
+
#
|
|
1216
|
+
# If a block is supplied, it is an additional constraint (checked while holding the
|
|
1217
|
+
# pool lock) on whether a new connection should be established.
|
|
892
1218
|
#--
|
|
893
1219
|
# Implementation constraint: a newly established connection returned by this
|
|
894
1220
|
# method must be in the +.leased+ state.
|
|
895
1221
|
def try_to_checkout_new_connection
|
|
896
1222
|
# first in synchronized section check if establishing new conns is allowed
|
|
897
|
-
# and increment @now_connecting, to prevent overstepping this pool's @
|
|
1223
|
+
# and increment @now_connecting, to prevent overstepping this pool's @max_connections
|
|
898
1224
|
# constraint
|
|
899
1225
|
do_checkout = synchronize do
|
|
900
|
-
if
|
|
1226
|
+
return if self.discarded?
|
|
1227
|
+
|
|
1228
|
+
if @threads_blocking_new_connections.zero? && (@max_connections.nil? || (@connections.size + @now_connecting) < @max_connections) && (!block_given? || yield)
|
|
1229
|
+
if @connections.size > 0 || @original_context != ActiveSupport::IsolatedExecutionState.context
|
|
1230
|
+
@activated = true
|
|
1231
|
+
end
|
|
1232
|
+
|
|
901
1233
|
@now_connecting += 1
|
|
902
1234
|
end
|
|
903
1235
|
end
|
|
@@ -908,12 +1240,16 @@ module ActiveRecord
|
|
|
908
1240
|
conn = checkout_new_connection
|
|
909
1241
|
ensure
|
|
910
1242
|
synchronize do
|
|
1243
|
+
@now_connecting -= 1
|
|
911
1244
|
if conn
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1245
|
+
if self.discarded?
|
|
1246
|
+
conn.discard!
|
|
1247
|
+
else
|
|
1248
|
+
adopt_connection(conn)
|
|
1249
|
+
# returned conn needs to be already leased
|
|
1250
|
+
conn.lease
|
|
1251
|
+
end
|
|
915
1252
|
end
|
|
916
|
-
@now_connecting -= 1
|
|
917
1253
|
end
|
|
918
1254
|
end
|
|
919
1255
|
end
|
|
@@ -936,15 +1272,20 @@ module ActiveRecord
|
|
|
936
1272
|
end
|
|
937
1273
|
|
|
938
1274
|
def checkout_and_verify(c)
|
|
939
|
-
c.
|
|
940
|
-
c.clean!
|
|
941
|
-
end
|
|
942
|
-
c
|
|
1275
|
+
c.clean!
|
|
943
1276
|
rescue Exception
|
|
944
1277
|
remove c
|
|
945
1278
|
c.disconnect!
|
|
946
1279
|
raise
|
|
947
1280
|
end
|
|
1281
|
+
|
|
1282
|
+
def name_inspect
|
|
1283
|
+
db_config.name.inspect unless db_config.name == "primary"
|
|
1284
|
+
end
|
|
1285
|
+
|
|
1286
|
+
def shard_inspect
|
|
1287
|
+
shard.inspect unless shard == :default
|
|
1288
|
+
end
|
|
948
1289
|
end
|
|
949
1290
|
end
|
|
950
1291
|
end
|