activerecord 8.0.0 → 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.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1381 -241
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +20 -4
  9. data/lib/active_record/associations/builder/association.rb +16 -5
  10. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  11. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  12. data/lib/active_record/associations/builder/has_one.rb +1 -1
  13. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  14. data/lib/active_record/associations/collection_association.rb +9 -5
  15. data/lib/active_record/associations/collection_proxy.rb +26 -4
  16. data/lib/active_record/associations/deprecation.rb +88 -0
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_one_association.rb +1 -0
  20. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  21. data/lib/active_record/associations/join_dependency.rb +5 -4
  22. data/lib/active_record/associations/preloader/association.rb +1 -1
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations.rb +159 -21
  26. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  27. data/lib/active_record/attribute_methods/query.rb +34 -0
  28. data/lib/active_record/attribute_methods/serialization.rb +19 -6
  29. data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
  30. data/lib/active_record/attribute_methods.rb +24 -17
  31. data/lib/active_record/attributes.rb +40 -26
  32. data/lib/active_record/autosave_association.rb +22 -12
  33. data/lib/active_record/base.rb +3 -4
  34. data/lib/active_record/callbacks.rb +1 -2
  35. data/lib/active_record/coders/column_serializer.rb +1 -1
  36. data/lib/active_record/coders/json.rb +16 -6
  37. data/lib/active_record/coders/yaml_column.rb +2 -2
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
  52. data/lib/active_record/connection_adapters/column.rb +14 -4
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  55. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
  60. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  61. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  62. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
  63. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  64. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
  73. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  74. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  76. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
  79. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  80. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
  81. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
  82. data/lib/active_record/connection_adapters.rb +1 -0
  83. data/lib/active_record/connection_handling.rb +15 -10
  84. data/lib/active_record/core.rb +44 -12
  85. data/lib/active_record/counter_cache.rb +36 -9
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  87. data/lib/active_record/database_configurations/database_config.rb +5 -1
  88. data/lib/active_record/database_configurations/hash_config.rb +66 -9
  89. data/lib/active_record/database_configurations/url_config.rb +15 -3
  90. data/lib/active_record/database_configurations.rb +7 -3
  91. data/lib/active_record/delegated_type.rb +19 -19
  92. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  93. data/lib/active_record/dynamic_matchers.rb +54 -69
  94. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  95. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  96. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  97. data/lib/active_record/encryption/encryptor.rb +39 -25
  98. data/lib/active_record/encryption/scheme.rb +1 -1
  99. data/lib/active_record/enum.rb +37 -20
  100. data/lib/active_record/errors.rb +23 -7
  101. data/lib/active_record/explain.rb +1 -1
  102. data/lib/active_record/explain_registry.rb +51 -2
  103. data/lib/active_record/filter_attribute_handler.rb +76 -0
  104. data/lib/active_record/fixture_set/table_row.rb +19 -2
  105. data/lib/active_record/fixtures.rb +2 -2
  106. data/lib/active_record/future_result.rb +5 -3
  107. data/lib/active_record/gem_version.rb +2 -2
  108. data/lib/active_record/inheritance.rb +9 -4
  109. data/lib/active_record/insert_all.rb +14 -9
  110. data/lib/active_record/locking/optimistic.rb +7 -0
  111. data/lib/active_record/locking/pessimistic.rb +5 -0
  112. data/lib/active_record/log_subscriber.rb +30 -22
  113. data/lib/active_record/message_pack.rb +13 -0
  114. data/lib/active_record/middleware/shard_selector.rb +34 -17
  115. data/lib/active_record/migration/command_recorder.rb +32 -6
  116. data/lib/active_record/migration/compatibility.rb +34 -24
  117. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  118. data/lib/active_record/migration.rb +31 -21
  119. data/lib/active_record/model_schema.rb +38 -12
  120. data/lib/active_record/nested_attributes.rb +10 -6
  121. data/lib/active_record/persistence.rb +35 -3
  122. data/lib/active_record/query_cache.rb +22 -15
  123. data/lib/active_record/query_logs.rb +7 -7
  124. data/lib/active_record/querying.rb +17 -5
  125. data/lib/active_record/railtie.rb +35 -6
  126. data/lib/active_record/railties/controller_runtime.rb +11 -6
  127. data/lib/active_record/railties/databases.rake +24 -20
  128. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  129. data/lib/active_record/railties/job_runtime.rb +10 -11
  130. data/lib/active_record/reflection.rb +36 -1
  131. data/lib/active_record/relation/batches.rb +25 -11
  132. data/lib/active_record/relation/calculations.rb +62 -41
  133. data/lib/active_record/relation/delegation.rb +0 -1
  134. data/lib/active_record/relation/finder_methods.rb +76 -45
  135. data/lib/active_record/relation/merger.rb +4 -2
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  137. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  138. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
  139. data/lib/active_record/relation/predicate_builder.rb +10 -8
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +106 -45
  142. data/lib/active_record/relation/spawn_methods.rb +6 -6
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +63 -31
  145. data/lib/active_record/result.rb +44 -21
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +2 -0
  148. data/lib/active_record/schema.rb +1 -1
  149. data/lib/active_record/schema_dumper.rb +41 -21
  150. data/lib/active_record/scoping.rb +0 -1
  151. data/lib/active_record/secure_token.rb +3 -3
  152. data/lib/active_record/signed_id.rb +48 -19
  153. data/lib/active_record/statement_cache.rb +16 -12
  154. data/lib/active_record/store.rb +46 -19
  155. data/lib/active_record/structured_event_subscriber.rb +85 -0
  156. data/lib/active_record/table_metadata.rb +5 -20
  157. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  158. data/lib/active_record/tasks/database_tasks.rb +44 -45
  159. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
  160. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  161. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  162. data/lib/active_record/test_databases.rb +18 -4
  163. data/lib/active_record/test_fixtures.rb +40 -6
  164. data/lib/active_record/testing/query_assertions.rb +8 -2
  165. data/lib/active_record/timestamp.rb +4 -2
  166. data/lib/active_record/transaction.rb +2 -5
  167. data/lib/active_record/transactions.rb +58 -32
  168. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  169. data/lib/active_record/type/internal/timezone.rb +7 -0
  170. data/lib/active_record/type/json.rb +13 -2
  171. data/lib/active_record/type/serialized.rb +16 -4
  172. data/lib/active_record/type/type_map.rb +1 -1
  173. data/lib/active_record/type_caster/connection.rb +2 -1
  174. data/lib/active_record/validations/associated.rb +1 -1
  175. data/lib/active_record.rb +72 -7
  176. data/lib/arel/alias_predication.rb +2 -0
  177. data/lib/arel/collectors/bind.rb +1 -1
  178. data/lib/arel/collectors/sql_string.rb +1 -1
  179. data/lib/arel/collectors/substitute_binds.rb +2 -2
  180. data/lib/arel/crud.rb +8 -11
  181. data/lib/arel/delete_manager.rb +5 -0
  182. data/lib/arel/nodes/binary.rb +1 -1
  183. data/lib/arel/nodes/count.rb +2 -2
  184. data/lib/arel/nodes/delete_statement.rb +4 -2
  185. data/lib/arel/nodes/function.rb +4 -10
  186. data/lib/arel/nodes/named_function.rb +2 -2
  187. data/lib/arel/nodes/node.rb +2 -2
  188. data/lib/arel/nodes/sql_literal.rb +1 -1
  189. data/lib/arel/nodes/update_statement.rb +4 -2
  190. data/lib/arel/nodes.rb +0 -2
  191. data/lib/arel/predications.rb +1 -3
  192. data/lib/arel/select_manager.rb +13 -4
  193. data/lib/arel/update_manager.rb +5 -0
  194. data/lib/arel/visitors/dot.rb +2 -3
  195. data/lib/arel/visitors/postgresql.rb +55 -0
  196. data/lib/arel/visitors/sqlite.rb +55 -8
  197. data/lib/arel/visitors/to_sql.rb +7 -23
  198. data/lib/arel.rb +3 -1
  199. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  200. metadata +16 -15
  201. data/lib/active_record/explain_subscriber.rb +0 -34
  202. data/lib/active_record/normalization.rb +0 -163
@@ -8,27 +8,22 @@ require "active_record/connection_adapters/abstract/connection_pool/reaper"
8
8
 
9
9
  module ActiveRecord
10
10
  module ConnectionAdapters
11
- module AbstractPool # :nodoc:
12
- end
13
-
14
11
  class NullPool # :nodoc:
15
- include ConnectionAdapters::AbstractPool
16
-
17
- class NullConfig # :nodoc:
12
+ class NullConfig
18
13
  def method_missing(...)
19
14
  nil
20
15
  end
21
16
  end
22
- NULL_CONFIG = NullConfig.new # :nodoc:
17
+ NULL_CONFIG = NullConfig.new
23
18
 
24
19
  def initialize
25
20
  super()
26
- @mutex = Mutex.new
21
+ @monitor = Monitor.new
27
22
  @server_version = nil
28
23
  end
29
24
 
30
25
  def server_version(connection) # :nodoc:
31
- @server_version || @mutex.synchronize { @server_version ||= connection.get_database_version }
26
+ @server_version || @monitor.synchronize { @server_version ||= connection.get_database_version }
32
27
  end
33
28
 
34
29
  def schema_reflection
@@ -36,7 +31,8 @@ module ActiveRecord
36
31
  end
37
32
 
38
33
  def schema_cache; end
39
- def connection_class; end
34
+ def query_cache; end
35
+ def connection_descriptor; end
40
36
  def checkin(_); end
41
37
  def remove(_); end
42
38
  def async_executor; end
@@ -48,6 +44,11 @@ module ActiveRecord
48
44
  def dirties_query_cache
49
45
  true
50
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
51
52
  end
52
53
 
53
54
  # = Active Record Connection Pool
@@ -100,13 +101,21 @@ module ActiveRecord
100
101
  # There are several connection-pooling-related options that you can add to
101
102
  # your database connection configuration:
102
103
  #
103
- # * +pool+: maximum number of connections the pool may manage (default 5).
104
- # * +idle_timeout+: number of seconds that a connection will be kept
105
- # unused in the pool before it is automatically disconnected (default
106
- # 300 seconds). Set this to zero to keep connections forever.
107
104
  # * +checkout_timeout+: number of seconds to wait for a connection to
108
105
  # become available before giving up and raising a timeout error (default
109
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).
110
119
  #
111
120
  #--
112
121
  # Synchronization policy:
@@ -114,27 +123,36 @@ module ActiveRecord
114
123
  # * access to these instance variables needs to be in +synchronize+:
115
124
  # * @connections
116
125
  # * @now_connecting
126
+ # * @maintaining
117
127
  # * private methods that require being called in a +synchronize+ blocks
118
128
  # are now explicitly documented
119
129
  class ConnectionPool
120
- class WeakThreadKeyMap # :nodoc:
121
- # FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
122
- # but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
123
- def initialize
124
- @map = {}
125
- end
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) && Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("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 = Concurrent::Map.new
140
+ end
126
141
 
127
- def clear
128
- @map.clear
129
- end
142
+ def clear
143
+ @map.clear
144
+ end
130
145
 
131
- def [](key)
132
- @map[key]
133
- end
146
+ def [](key)
147
+ @map[key]
148
+ end
134
149
 
135
- def []=(key, value)
136
- @map.select! { |c, _| c.alive? }
137
- @map[key] = value
150
+ def []=(key, value)
151
+ @map.each_pair do |thread, _|
152
+ @map.delete(thread) unless thread&.alive?
153
+ end
154
+ @map[key] = value
155
+ end
138
156
  end
139
157
  end
140
158
 
@@ -164,21 +182,30 @@ module ActiveRecord
164
182
  end
165
183
  end
166
184
 
167
- class LeaseRegistry # :nodoc:
168
- def initialize
169
- @mutex = Mutex.new
170
- @map = WeakThreadKeyMap.new
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
171
191
  end
192
+ else
193
+ class LeaseRegistry # :nodoc:
194
+ def initialize
195
+ @mutex = Mutex.new
196
+ @map = WeakThreadKeyMap.new
197
+ end
172
198
 
173
- def [](context)
174
- @mutex.synchronize do
175
- @map[context] ||= Lease.new
199
+ def [](context)
200
+ @mutex.synchronize do
201
+ @map[context] ||= Lease.new
202
+ end
176
203
  end
177
- end
178
204
 
179
- def clear
180
- @mutex.synchronize do
181
- @map.clear
205
+ def clear
206
+ @mutex.synchronize do
207
+ @map.clear
208
+ end
182
209
  end
183
210
  end
184
211
  end
@@ -210,10 +237,10 @@ module ActiveRecord
210
237
 
211
238
  include MonitorMixin
212
239
  prepend QueryCache::ConnectionPoolConfiguration
213
- include ConnectionAdapters::AbstractPool
214
240
 
215
241
  attr_accessor :automatic_reconnect, :checkout_timeout
216
- attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
242
+ attr_reader :db_config, :max_connections, :min_connections, :max_age, :keepalive, :reaper, :pool_config, :async_executor, :role, :shard
243
+ alias :size :max_connections
217
244
 
218
245
  delegate :schema_reflection, :server_version, to: :pool_config
219
246
 
@@ -233,7 +260,10 @@ module ActiveRecord
233
260
 
234
261
  @checkout_timeout = db_config.checkout_timeout
235
262
  @idle_timeout = db_config.idle_timeout
236
- @size = db_config.pool
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
237
267
 
238
268
  # This variable tracks the cache of threads mapped to reserved connections, with the
239
269
  # sole purpose of speeding up the +connection+ method. It is not the authoritative
@@ -255,6 +285,12 @@ module ActiveRecord
255
285
  # currently in the process of independently establishing connections to the DB.
256
286
  @now_connecting = 0
257
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
+
258
294
  @threads_blocking_new_connections = 0
259
295
 
260
296
  @available = ConnectionLeasingQueue.new self
@@ -265,13 +301,17 @@ module ActiveRecord
265
301
 
266
302
  @schema_cache = nil
267
303
 
304
+ @activated = false
305
+ @original_context = ActiveSupport::IsolatedExecutionState.context
306
+
307
+ @reaper_lock = Monitor.new
268
308
  @reaper = Reaper.new(self, db_config.reaping_frequency)
269
309
  @reaper.run
270
310
  end
271
311
 
272
312
  def inspect # :nodoc:
273
- name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
274
- shard_field = " shard=#{@shard.inspect}" unless @shard == :default
313
+ name_field = " name=#{name_inspect}" if name_inspect
314
+ shard_field = " shard=#{shard_inspect}" if shard_inspect
275
315
 
276
316
  "#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
277
317
  end
@@ -301,6 +341,14 @@ module ActiveRecord
301
341
  InternalMetadata.new(self)
302
342
  end
303
343
 
344
+ def activate
345
+ @activated = true
346
+ end
347
+
348
+ def activated?
349
+ @activated
350
+ end
351
+
304
352
  # Retrieve the connection associated with the current thread, or call
305
353
  # #checkout to obtain one if necessary.
306
354
  #
@@ -308,8 +356,9 @@ module ActiveRecord
308
356
  # held in a cache keyed by a thread.
309
357
  def lease_connection
310
358
  lease = connection_lease
311
- lease.sticky = true
312
359
  lease.connection ||= checkout
360
+ lease.sticky = true
361
+ lease.connection
313
362
  end
314
363
 
315
364
  def permanent_lease? # :nodoc:
@@ -327,6 +376,7 @@ module ActiveRecord
327
376
  end
328
377
 
329
378
  @pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
379
+ @pinned_connection.pinned = true
330
380
  @pinned_connection.verify! # eagerly validate the connection
331
381
  @pinned_connection.begin_transaction joinable: false, _lazy: false
332
382
  end
@@ -349,6 +399,7 @@ module ActiveRecord
349
399
  end
350
400
 
351
401
  if @pinned_connection.nil?
402
+ connection.pinned = false
352
403
  connection.steal!
353
404
  connection.lock_thread = nil
354
405
  checkin(connection)
@@ -358,8 +409,8 @@ module ActiveRecord
358
409
  clean
359
410
  end
360
411
 
361
- def connection_class # :nodoc:
362
- pool_config.connection_class
412
+ def connection_descriptor # :nodoc:
413
+ pool_config.connection_descriptor
363
414
  end
364
415
 
365
416
  # Returns true if there is an open connection being used for the current thread.
@@ -380,6 +431,8 @@ module ActiveRecord
380
431
  # #lease_connection or #with_connection methods, connections obtained through
381
432
  # #checkout will not be automatically released.
382
433
  def release_connection(existing_lease = nil)
434
+ return if self.discarded?
435
+
383
436
  if conn = connection_lease.release
384
437
  checkin conn
385
438
  return true
@@ -405,18 +458,36 @@ module ActiveRecord
405
458
  begin
406
459
  yield lease.connection
407
460
  ensure
408
- lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
461
+ lease.sticky = sticky_was if prevent_permanent_checkout
409
462
  end
410
463
  else
411
464
  begin
412
465
  yield lease.connection = checkout
413
466
  ensure
414
- lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
467
+ lease.sticky = sticky_was if prevent_permanent_checkout
415
468
  release_connection(lease) unless lease.sticky
416
469
  end
417
470
  end
418
471
  end
419
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
+
420
491
  # Returns true if a connection has already been opened.
421
492
  def connected?
422
493
  synchronize { @connections.any?(&:connected?) }
@@ -444,18 +515,32 @@ module ActiveRecord
444
515
  # connections in the pool within a timeout interval (default duration is
445
516
  # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
446
517
  def disconnect(raise_on_acquisition_timeout = true)
447
- with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
448
- synchronize do
449
- @connections.each do |conn|
450
- if conn.in_use?
451
- conn.steal!
452
- checkin conn
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
453
536
  end
454
- conn.disconnect!
537
+ @connections = @pinned_connection ? [@pinned_connection] : []
538
+ @leases.clear
539
+ @available.clear
540
+
541
+ # Stop maintaining the minimum size until reactivated
542
+ @activated = false
455
543
  end
456
- @connections = []
457
- @leases.clear
458
- @available.clear
459
544
  end
460
545
  end
461
546
  end
@@ -476,12 +561,15 @@ module ActiveRecord
476
561
  #
477
562
  # See AbstractAdapter#discard!
478
563
  def discard! # :nodoc:
479
- synchronize do
480
- return if self.discarded?
481
- @connections.each do |conn|
482
- conn.discard!
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
483
572
  end
484
- @connections = @available = @leases = nil
485
573
  end
486
574
  end
487
575
 
@@ -489,7 +577,17 @@ module ActiveRecord
489
577
  @connections.nil?
490
578
  end
491
579
 
492
- # Clears the cache which maps classes and re-connects connections that
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
493
591
  # require reloading.
494
592
  #
495
593
  # Raises:
@@ -512,7 +610,7 @@ module ActiveRecord
512
610
  end
513
611
  end
514
612
 
515
- # Clears the cache which maps classes and re-connects connections that
613
+ # Clears reloadable connections from the pool and re-connects connections that
516
614
  # require reloading.
517
615
  #
518
616
  # The pool first tries to gain ownership of all connections. If unable to
@@ -539,20 +637,25 @@ module ActiveRecord
539
637
  # Raises:
540
638
  # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
541
639
  def checkout(checkout_timeout = @checkout_timeout)
542
- if @pinned_connection
543
- @pinned_connection.lock.synchronize do
544
- synchronize do
640
+ return checkout_and_verify(acquire_connection(checkout_timeout)) unless @pinned_connection
641
+
642
+ @pinned_connection.lock.synchronize do
643
+ synchronize do
644
+ # The pinned connection may have been cleaned up before we synchronized, so check if it is still present
645
+ if @pinned_connection
545
646
  @pinned_connection.verify!
647
+
546
648
  # Any leased connection must be in @connections otherwise
547
649
  # some methods like #connected? won't behave correctly
548
650
  unless @connections.include?(@pinned_connection)
549
651
  @connections << @pinned_connection
550
652
  end
653
+
654
+ @pinned_connection
655
+ else
656
+ checkout_and_verify(acquire_connection(checkout_timeout))
551
657
  end
552
658
  end
553
- @pinned_connection
554
- else
555
- checkout_and_verify(acquire_connection(checkout_timeout))
556
659
  end
557
660
  end
558
661
 
@@ -567,11 +670,7 @@ module ActiveRecord
567
670
  conn.lock.synchronize do
568
671
  synchronize do
569
672
  connection_lease.clear(conn)
570
-
571
- conn._run_checkin_callbacks do
572
- conn.expire
573
- end
574
-
673
+ conn.expire
575
674
  @available.add conn
576
675
  end
577
676
  end
@@ -589,7 +688,7 @@ module ActiveRecord
589
688
  @available.delete conn
590
689
 
591
690
  # @available.any_waiting? => true means that prior to removing this
592
- # conn, the pool was at its max size (@connections.size == @size).
691
+ # conn, the pool was at its max size (@connections.size == @max_connections).
593
692
  # This would mean that any threads stuck waiting in the queue wouldn't
594
693
  # know they could checkout_new_connection, so let's do it for them.
595
694
  # Because condition-wait loop is encapsulated in the Queue class
@@ -597,14 +696,14 @@ module ActiveRecord
597
696
  # that are "stuck" there are helpless. They have no way of creating
598
697
  # new connections and are completely reliant on us feeding available
599
698
  # connections into the Queue.
600
- needs_new_connection = @available.any_waiting?
699
+ needs_new_connection = @available.num_waiting > @maintaining
601
700
  end
602
701
 
603
702
  # This is intentionally done outside of the synchronized section as we
604
703
  # would like not to hold the main mutex while checking out new connections.
605
704
  # Thus there is some chance that needs_new_connection information is now
606
705
  # stale, we can live with that (bulk_make_new_connections will make
607
- # sure not to exceed the pool's @size limit).
706
+ # sure not to exceed the pool's @max_connections limit).
608
707
  bulk_make_new_connections(1) if needs_new_connection
609
708
  end
610
709
 
@@ -637,11 +736,27 @@ module ActiveRecord
637
736
  def flush(minimum_idle = @idle_timeout)
638
737
  return if minimum_idle.nil?
639
738
 
640
- idle_connections = synchronize do
739
+ removed_connections = synchronize do
641
740
  return if self.discarded?
642
- @connections.select do |conn|
741
+
742
+ idle_connections = @connections.select do |conn|
643
743
  !conn.in_use? && conn.seconds_idle >= minimum_idle
644
- end.each do |conn|
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|
645
760
  conn.lease
646
761
 
647
762
  @available.delete conn
@@ -649,22 +764,111 @@ module ActiveRecord
649
764
  end
650
765
  end
651
766
 
652
- idle_connections.each do |conn|
767
+ removed_connections.each do |conn|
653
768
  conn.disconnect!
654
769
  end
655
770
  end
656
771
 
657
772
  # Disconnect all currently idle connections. Connections currently checked
658
- # 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).
659
775
  def flush!
660
776
  reap
661
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
662
862
  end
663
863
 
664
864
  def num_waiting_in_queue # :nodoc:
665
865
  @available.num_waiting
666
866
  end
667
867
 
868
+ def num_available_in_queue # :nodoc:
869
+ @available.size
870
+ end
871
+
668
872
  # Returns the connection pool's usage statistic.
669
873
  #
670
874
  # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
@@ -687,6 +891,24 @@ module ActiveRecord
687
891
  Thread.pass
688
892
  end
689
893
 
894
+ def new_connection # :nodoc:
895
+ connection = db_config.new_connection
896
+ connection.pool = self
897
+ connection
898
+ rescue ConnectionNotEstablished => ex
899
+ raise ex.set_pool(self)
900
+ end
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
+
690
912
  private
691
913
  def connection_lease
692
914
  @leases[ActiveSupport::IsolatedExecutionState.context]
@@ -696,7 +918,9 @@ module ActiveRecord
696
918
  case ActiveRecord.async_query_executor
697
919
  when :multi_thread_pool
698
920
  if @db_config.max_threads > 0
921
+ name_with_shard = [name_inspect, shard_inspect].join("-").tr("_", "-")
699
922
  Concurrent::ThreadPoolExecutor.new(
923
+ name: "ActiveRecord-#{name_with_shard}-async-query-executor",
700
924
  min_threads: @db_config.min_threads,
701
925
  max_threads: @db_config.max_threads,
702
926
  max_queue: @db_config.max_queue,
@@ -708,11 +932,109 @@ module ActiveRecord
708
932
  end
709
933
  end
710
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
+
711
1033
  #--
712
1034
  # this is unfortunately not concurrent
713
1035
  def bulk_make_new_connections(num_new_conns_needed)
714
1036
  num_new_conns_needed.times do
715
- # try_to_checkout_new_connection will not exceed pool's @size limit
1037
+ # try_to_checkout_new_connection will not exceed pool's @max_connections limit
716
1038
  if new_conn = try_to_checkout_new_connection
717
1039
  # make the new_conn available to the starving threads stuck @available Queue
718
1040
  checkin(new_conn)
@@ -725,9 +1047,11 @@ module ActiveRecord
725
1047
  # wrap it in +synchronize+ because some pool's actions are allowed
726
1048
  # to be performed outside of the main +synchronize+ block.
727
1049
  def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
728
- with_new_connections_blocked do
729
- attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
730
- yield
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
731
1055
  end
732
1056
  end
733
1057
 
@@ -847,13 +1171,13 @@ module ActiveRecord
847
1171
  # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
848
1172
  # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
849
1173
  # of the said methods and avoid an additional +synchronize+ overhead.
850
- 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
851
1175
  conn
852
1176
  else
853
1177
  reap
854
1178
  # Retry after reaping, which may return an available connection,
855
1179
  # remove an inactive connection, or both
856
- 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
857
1181
  conn
858
1182
  else
859
1183
  @available.poll(checkout_timeout)
@@ -863,32 +1187,61 @@ module ActiveRecord
863
1187
  raise ex.set_pool(self)
864
1188
  end
865
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
+
866
1216
  #--
867
1217
  # if owner_thread param is omitted, this must be called in synchronize block
868
1218
  def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
869
- @leases[owner_thread].clear(conn)
1219
+ if owner_thread
1220
+ @leases[owner_thread].clear(conn)
1221
+ end
870
1222
  end
871
1223
  alias_method :release, :remove_connection_from_thread_cache
872
1224
 
873
- def new_connection
874
- connection = db_config.new_connection
875
- connection.pool = self
876
- connection
877
- rescue ConnectionNotEstablished => ex
878
- raise ex.set_pool(self)
879
- end
880
-
881
- # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
1225
+ # If the pool is not at a <tt>@max_connections</tt> limit, establish new connection. Connecting
882
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.
883
1230
  #--
884
1231
  # Implementation constraint: a newly established connection returned by this
885
1232
  # method must be in the +.leased+ state.
886
1233
  def try_to_checkout_new_connection
887
1234
  # first in synchronized section check if establishing new conns is allowed
888
- # and increment @now_connecting, to prevent overstepping this pool's @size
1235
+ # and increment @now_connecting, to prevent overstepping this pool's @max_connections
889
1236
  # constraint
890
1237
  do_checkout = synchronize do
891
- if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
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
+
892
1245
  @now_connecting += 1
893
1246
  end
894
1247
  end
@@ -899,12 +1252,16 @@ module ActiveRecord
899
1252
  conn = checkout_new_connection
900
1253
  ensure
901
1254
  synchronize do
1255
+ @now_connecting -= 1
902
1256
  if conn
903
- adopt_connection(conn)
904
- # returned conn needs to be already leased
905
- conn.lease
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
906
1264
  end
907
- @now_connecting -= 1
908
1265
  end
909
1266
  end
910
1267
  end
@@ -927,15 +1284,20 @@ module ActiveRecord
927
1284
  end
928
1285
 
929
1286
  def checkout_and_verify(c)
930
- c._run_checkout_callbacks do
931
- c.clean!
932
- end
933
- c
1287
+ c.clean!
934
1288
  rescue Exception
935
1289
  remove c
936
1290
  c.disconnect!
937
1291
  raise
938
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
939
1301
  end
940
1302
  end
941
1303
  end