activerecord 6.1.7.2 → 7.0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1295 -1007
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +124 -95
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +71 -71
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +206 -105
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +123 -148
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +149 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +1 -1
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +9 -6
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +225 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. metadata +53 -9
@@ -3,7 +3,9 @@
3
3
  require "thread"
4
4
  require "concurrent/map"
5
5
  require "monitor"
6
- require "weakref"
6
+
7
+ require "active_record/connection_adapters/abstract/connection_pool/queue"
8
+ require "active_record/connection_adapters/abstract/connection_pool/reaper"
7
9
 
8
10
  module ActiveRecord
9
11
  module ConnectionAdapters
@@ -17,6 +19,13 @@ module ActiveRecord
17
19
  def set_schema_cache(cache)
18
20
  self.schema_cache = cache
19
21
  end
22
+
23
+ def lazily_set_schema_cache
24
+ return unless ActiveRecord.lazily_load_schema_cache
25
+
26
+ cache = SchemaCache.load_from(db_config.lazy_schema_cache_path)
27
+ set_schema_cache(cache)
28
+ end
20
29
  end
21
30
 
22
31
  class NullPool # :nodoc:
@@ -24,9 +33,10 @@ module ActiveRecord
24
33
 
25
34
  attr_accessor :schema_cache
26
35
 
27
- def connection_klass
28
- nil
29
- end
36
+ def connection_class; end
37
+ def checkin(_); end
38
+ def remove(_); end
39
+ def async_executor; end
30
40
  end
31
41
 
32
42
  # Connection pool base class for managing Active Record database
@@ -90,278 +100,15 @@ module ActiveRecord
90
100
  # * private methods that require being called in a +synchronize+ blocks
91
101
  # are now explicitly documented
92
102
  class ConnectionPool
93
- # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool
94
- # with which it shares a Monitor.
95
- class Queue
96
- def initialize(lock = Monitor.new)
97
- @lock = lock
98
- @cond = @lock.new_cond
99
- @num_waiting = 0
100
- @queue = []
101
- end
102
-
103
- # Test if any threads are currently waiting on the queue.
104
- def any_waiting?
105
- synchronize do
106
- @num_waiting > 0
107
- end
108
- end
109
-
110
- # Returns the number of threads currently waiting on this
111
- # queue.
112
- def num_waiting
113
- synchronize do
114
- @num_waiting
115
- end
116
- end
117
-
118
- # Add +element+ to the queue. Never blocks.
119
- def add(element)
120
- synchronize do
121
- @queue.push element
122
- @cond.signal
123
- end
124
- end
125
-
126
- # If +element+ is in the queue, remove and return it, or +nil+.
127
- def delete(element)
128
- synchronize do
129
- @queue.delete(element)
130
- end
131
- end
132
-
133
- # Remove all elements from the queue.
134
- def clear
135
- synchronize do
136
- @queue.clear
137
- end
138
- end
139
-
140
- # Remove the head of the queue.
141
- #
142
- # If +timeout+ is not given, remove and return the head of the
143
- # queue if the number of available elements is strictly
144
- # greater than the number of threads currently waiting (that
145
- # is, don't jump ahead in line). Otherwise, return +nil+.
146
- #
147
- # If +timeout+ is given, block if there is no element
148
- # available, waiting up to +timeout+ seconds for an element to
149
- # become available.
150
- #
151
- # Raises:
152
- # - ActiveRecord::ConnectionTimeoutError if +timeout+ is given and no element
153
- # becomes available within +timeout+ seconds,
154
- def poll(timeout = nil)
155
- synchronize { internal_poll(timeout) }
156
- end
157
-
158
- private
159
- def internal_poll(timeout)
160
- no_wait_poll || (timeout && wait_poll(timeout))
161
- end
162
-
163
- def synchronize(&block)
164
- @lock.synchronize(&block)
165
- end
166
-
167
- # Test if the queue currently contains any elements.
168
- def any?
169
- !@queue.empty?
170
- end
171
-
172
- # A thread can remove an element from the queue without
173
- # waiting if and only if the number of currently available
174
- # connections is strictly greater than the number of waiting
175
- # threads.
176
- def can_remove_no_wait?
177
- @queue.size > @num_waiting
178
- end
179
-
180
- # Removes and returns the head of the queue if possible, or +nil+.
181
- def remove
182
- @queue.pop
183
- end
184
-
185
- # Remove and return the head of the queue if the number of
186
- # available elements is strictly greater than the number of
187
- # threads currently waiting. Otherwise, return +nil+.
188
- def no_wait_poll
189
- remove if can_remove_no_wait?
190
- end
191
-
192
- # Waits on the queue up to +timeout+ seconds, then removes and
193
- # returns the head of the queue.
194
- def wait_poll(timeout)
195
- @num_waiting += 1
196
-
197
- t0 = Concurrent.monotonic_time
198
- elapsed = 0
199
- loop do
200
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
201
- @cond.wait(timeout - elapsed)
202
- end
203
-
204
- return remove if any?
205
-
206
- elapsed = Concurrent.monotonic_time - t0
207
- if elapsed >= timeout
208
- msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
209
- [timeout, elapsed]
210
- raise ConnectionTimeoutError, msg
211
- end
212
- end
213
- ensure
214
- @num_waiting -= 1
215
- end
216
- end
217
-
218
- # Adds the ability to turn a basic fair FIFO queue into one
219
- # biased to some thread.
220
- module BiasableQueue # :nodoc:
221
- class BiasedConditionVariable # :nodoc:
222
- # semantics of condition variables guarantee that +broadcast+, +broadcast_on_biased+,
223
- # +signal+ and +wait+ methods are only called while holding a lock
224
- def initialize(lock, other_cond, preferred_thread)
225
- @real_cond = lock.new_cond
226
- @other_cond = other_cond
227
- @preferred_thread = preferred_thread
228
- @num_waiting_on_real_cond = 0
229
- end
230
-
231
- def broadcast
232
- broadcast_on_biased
233
- @other_cond.broadcast
234
- end
235
-
236
- def broadcast_on_biased
237
- @num_waiting_on_real_cond = 0
238
- @real_cond.broadcast
239
- end
240
-
241
- def signal
242
- if @num_waiting_on_real_cond > 0
243
- @num_waiting_on_real_cond -= 1
244
- @real_cond
245
- else
246
- @other_cond
247
- end.signal
248
- end
249
-
250
- def wait(timeout)
251
- if Thread.current == @preferred_thread
252
- @num_waiting_on_real_cond += 1
253
- @real_cond
254
- else
255
- @other_cond
256
- end.wait(timeout)
257
- end
258
- end
259
-
260
- def with_a_bias_for(thread)
261
- previous_cond = nil
262
- new_cond = nil
263
- synchronize do
264
- previous_cond = @cond
265
- @cond = new_cond = BiasedConditionVariable.new(@lock, @cond, thread)
266
- end
267
- yield
268
- ensure
269
- synchronize do
270
- @cond = previous_cond if previous_cond
271
- new_cond.broadcast_on_biased if new_cond # wake up any remaining sleepers
272
- end
273
- end
274
- end
275
-
276
- # Connections must be leased while holding the main pool mutex. This is
277
- # an internal subclass that also +.leases+ returned connections while
278
- # still in queue's critical section (queue synchronizes with the same
279
- # <tt>@lock</tt> as the main pool) so that a returned connection is already
280
- # leased and there is no need to re-enter synchronized block.
281
- class ConnectionLeasingQueue < Queue # :nodoc:
282
- include BiasableQueue
283
-
284
- private
285
- def internal_poll(timeout)
286
- conn = super
287
- conn.lease if conn
288
- conn
289
- end
290
- end
291
-
292
- # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
293
- # +pool+. A reaper instantiated with a zero frequency will never reap
294
- # the connection pool.
295
- #
296
- # Configure the frequency by setting +reaping_frequency+ in your database
297
- # yaml file (default 60 seconds).
298
- class Reaper
299
- attr_reader :pool, :frequency
300
-
301
- def initialize(pool, frequency)
302
- @pool = pool
303
- @frequency = frequency
304
- end
305
-
306
- @mutex = Mutex.new
307
- @pools = {}
308
- @threads = {}
309
-
310
- class << self
311
- def register_pool(pool, frequency) # :nodoc:
312
- @mutex.synchronize do
313
- unless @threads[frequency]&.alive?
314
- @threads[frequency] = spawn_thread(frequency)
315
- end
316
- @pools[frequency] ||= []
317
- @pools[frequency] << WeakRef.new(pool)
318
- end
319
- end
320
-
321
- private
322
- def spawn_thread(frequency)
323
- Thread.new(frequency) do |t|
324
- # Advise multi-threaded app servers to ignore this thread for
325
- # the purposes of fork safety warnings
326
- Thread.current.thread_variable_set(:fork_safe, true)
327
- running = true
328
- while running
329
- sleep t
330
- @mutex.synchronize do
331
- @pools[frequency].select! do |pool|
332
- pool.weakref_alive? && !pool.discarded?
333
- end
334
-
335
- @pools[frequency].each do |p|
336
- p.reap
337
- p.flush
338
- rescue WeakRef::RefError
339
- end
340
-
341
- if @pools[frequency].empty?
342
- @pools.delete(frequency)
343
- @threads.delete(frequency)
344
- running = false
345
- end
346
- end
347
- end
348
- end
349
- end
350
- end
351
-
352
- def run
353
- return unless frequency && frequency > 0
354
- self.class.register_pool(pool, frequency)
355
- end
356
- end
357
-
358
103
  include MonitorMixin
359
104
  include QueryCache::ConnectionPoolConfiguration
360
105
  include ConnectionAdapters::AbstractPool
361
106
 
362
107
  attr_accessor :automatic_reconnect, :checkout_timeout
363
- attr_reader :db_config, :size, :reaper, :pool_config, :connection_klass
108
+ attr_reader :db_config, :size, :reaper, :pool_config, :connection_class, :async_executor, :role, :shard
364
109
 
110
+ alias_method :connection_klass, :connection_class
111
+ deprecate :connection_klass
365
112
  delegate :schema_cache, :schema_cache=, to: :pool_config
366
113
 
367
114
  # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
@@ -375,7 +122,9 @@ module ActiveRecord
375
122
 
376
123
  @pool_config = pool_config
377
124
  @db_config = pool_config.db_config
378
- @connection_klass = pool_config.connection_klass
125
+ @connection_class = pool_config.connection_class
126
+ @role = pool_config.role
127
+ @shard = pool_config.shard
379
128
 
380
129
  @checkout_timeout = db_config.checkout_timeout
381
130
  @idle_timeout = db_config.idle_timeout
@@ -407,6 +156,10 @@ module ActiveRecord
407
156
 
408
157
  @lock_thread = false
409
158
 
159
+ @async_executor = build_async_executor
160
+
161
+ lazily_set_schema_cache
162
+
410
163
  @reaper = Reaper.new(self, db_config.reaping_frequency)
411
164
  @reaper.run
412
165
  end
@@ -713,7 +466,28 @@ module ActiveRecord
713
466
  end
714
467
  end
715
468
 
469
+ def schedule_query(future_result) # :nodoc:
470
+ @async_executor.post { future_result.execute_or_skip }
471
+ Thread.pass
472
+ end
473
+
716
474
  private
475
+ def build_async_executor
476
+ case ActiveRecord.async_query_executor
477
+ when :multi_thread_pool
478
+ if @db_config.max_threads > 0
479
+ Concurrent::ThreadPoolExecutor.new(
480
+ min_threads: @db_config.min_threads,
481
+ max_threads: @db_config.max_threads,
482
+ max_queue: @db_config.max_queue,
483
+ fallback_policy: :caller_runs
484
+ )
485
+ end
486
+ when :global_thread_pool
487
+ ActiveRecord.global_thread_pool_async_query_executor
488
+ end
489
+ end
490
+
717
491
  #--
718
492
  # this is unfortunately not concurrent
719
493
  def bulk_make_new_connections(num_new_conns_needed)
@@ -757,13 +531,13 @@ module ActiveRecord
757
531
  end
758
532
 
759
533
  newly_checked_out = []
760
- timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
534
+ timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
761
535
 
762
536
  @available.with_a_bias_for(Thread.current) do
763
537
  loop do
764
538
  synchronize do
765
539
  return if collected_conns.size == @connections.size && @now_connecting == 0
766
- remaining_timeout = timeout_time - Concurrent.monotonic_time
540
+ remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
767
541
  remaining_timeout = 0 if remaining_timeout < 0
768
542
  conn = checkout_for_exclusive_access(remaining_timeout)
769
543
  collected_conns << conn
@@ -937,293 +711,5 @@ module ActiveRecord
937
711
  raise
938
712
  end
939
713
  end
940
-
941
- # ConnectionHandler is a collection of ConnectionPool objects. It is used
942
- # for keeping separate connection pools that connect to different databases.
943
- #
944
- # For example, suppose that you have 5 models, with the following hierarchy:
945
- #
946
- # class Author < ActiveRecord::Base
947
- # end
948
- #
949
- # class BankAccount < ActiveRecord::Base
950
- # end
951
- #
952
- # class Book < ActiveRecord::Base
953
- # establish_connection :library_db
954
- # end
955
- #
956
- # class ScaryBook < Book
957
- # end
958
- #
959
- # class GoodBook < Book
960
- # end
961
- #
962
- # And a database.yml that looked like this:
963
- #
964
- # development:
965
- # database: my_application
966
- # host: localhost
967
- #
968
- # library_db:
969
- # database: library
970
- # host: some.library.org
971
- #
972
- # Your primary database in the development environment is "my_application"
973
- # but the Book model connects to a separate database called "library_db"
974
- # (this can even be a database on a different machine).
975
- #
976
- # Book, ScaryBook and GoodBook will all use the same connection pool to
977
- # "library_db" while Author, BankAccount, and any other models you create
978
- # will use the default connection pool to "my_application".
979
- #
980
- # The various connection pools are managed by a single instance of
981
- # ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
982
- # All Active Record models use this handler to determine the connection pool that they
983
- # should use.
984
- #
985
- # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
986
- # about the model. The model needs to pass a connection specification name to the handler,
987
- # in order to look up the correct connection pool.
988
- class ConnectionHandler
989
- FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
990
- private_constant :FINALIZER
991
-
992
- def initialize
993
- # These caches are keyed by pool_config.connection_specification_name (PoolConfig#connection_specification_name).
994
- @owner_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
995
-
996
- # Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
997
- ObjectSpace.define_finalizer self, FINALIZER
998
- end
999
-
1000
- def prevent_writes # :nodoc:
1001
- Thread.current[:prevent_writes]
1002
- end
1003
-
1004
- def prevent_writes=(prevent_writes) # :nodoc:
1005
- Thread.current[:prevent_writes] = prevent_writes
1006
- end
1007
-
1008
- # Prevent writing to the database regardless of role.
1009
- #
1010
- # In some cases you may want to prevent writes to the database
1011
- # even if you are on a database that can write. `while_preventing_writes`
1012
- # will prevent writes to the database for the duration of the block.
1013
- #
1014
- # This method does not provide the same protection as a readonly
1015
- # user and is meant to be a safeguard against accidental writes.
1016
- #
1017
- # See `READ_QUERY` for the queries that are blocked by this
1018
- # method.
1019
- def while_preventing_writes(enabled = true)
1020
- unless ActiveRecord::Base.legacy_connection_handling
1021
- raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
1022
- end
1023
-
1024
- original, self.prevent_writes = self.prevent_writes, enabled
1025
- yield
1026
- ensure
1027
- self.prevent_writes = original
1028
- end
1029
-
1030
- def connection_pool_names # :nodoc:
1031
- owner_to_pool_manager.keys
1032
- end
1033
-
1034
- def all_connection_pools
1035
- owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
1036
- end
1037
-
1038
- def connection_pool_list(role = ActiveRecord::Base.current_role)
1039
- owner_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
1040
- end
1041
- alias :connection_pools :connection_pool_list
1042
-
1043
- def establish_connection(config, owner_name: Base, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
1044
- owner_name = config.to_s if config.is_a?(Symbol)
1045
-
1046
- pool_config = resolve_pool_config(config, owner_name)
1047
- db_config = pool_config.db_config
1048
-
1049
- # Protects the connection named `ActiveRecord::Base` from being removed
1050
- # if the user calls `establish_connection :primary`.
1051
- if owner_to_pool_manager.key?(pool_config.connection_specification_name)
1052
- remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
1053
- end
1054
-
1055
- message_bus = ActiveSupport::Notifications.instrumenter
1056
- payload = {}
1057
- if pool_config
1058
- payload[:spec_name] = pool_config.connection_specification_name
1059
- payload[:shard] = shard
1060
- payload[:config] = db_config.configuration_hash
1061
- end
1062
-
1063
- if ActiveRecord::Base.legacy_connection_handling
1064
- owner_to_pool_manager[pool_config.connection_specification_name] ||= LegacyPoolManager.new
1065
- else
1066
- owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
1067
- end
1068
- pool_manager = get_pool_manager(pool_config.connection_specification_name)
1069
- pool_manager.set_pool_config(role, shard, pool_config)
1070
-
1071
- message_bus.instrument("!connection.active_record", payload) do
1072
- pool_config.pool
1073
- end
1074
- end
1075
-
1076
- # Returns true if there are any active connections among the connection
1077
- # pools that the ConnectionHandler is managing.
1078
- def active_connections?(role = ActiveRecord::Base.current_role)
1079
- connection_pool_list(role).any?(&:active_connection?)
1080
- end
1081
-
1082
- # Returns any connections in use by the current thread back to the pool,
1083
- # and also returns connections to the pool cached by threads that are no
1084
- # longer alive.
1085
- def clear_active_connections!(role = ActiveRecord::Base.current_role)
1086
- connection_pool_list(role).each(&:release_connection)
1087
- end
1088
-
1089
- # Clears the cache which maps classes.
1090
- #
1091
- # See ConnectionPool#clear_reloadable_connections! for details.
1092
- def clear_reloadable_connections!(role = ActiveRecord::Base.current_role)
1093
- connection_pool_list(role).each(&:clear_reloadable_connections!)
1094
- end
1095
-
1096
- def clear_all_connections!(role = ActiveRecord::Base.current_role)
1097
- connection_pool_list(role).each(&:disconnect!)
1098
- end
1099
-
1100
- # Disconnects all currently idle connections.
1101
- #
1102
- # See ConnectionPool#flush! for details.
1103
- def flush_idle_connections!(role = ActiveRecord::Base.current_role)
1104
- connection_pool_list(role).each(&:flush!)
1105
- end
1106
-
1107
- # Locate the connection of the nearest super class. This can be an
1108
- # active or defined connection: if it is the latter, it will be
1109
- # opened and set as the active connection for the class it was defined
1110
- # for (not necessarily the current class).
1111
- def retrieve_connection(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
1112
- pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
1113
-
1114
- unless pool
1115
- if shard != ActiveRecord::Base.default_shard
1116
- message = "No connection pool for '#{spec_name}' found for the '#{shard}' shard."
1117
- elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1118
- message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
1119
- elsif role != ActiveRecord::Base.default_role
1120
- message = "No connection pool for '#{spec_name}' found for the '#{role}' role."
1121
- else
1122
- message = "No connection pool for '#{spec_name}' found."
1123
- end
1124
-
1125
- raise ConnectionNotEstablished, message
1126
- end
1127
-
1128
- pool.connection
1129
- end
1130
-
1131
- # Returns true if a connection that's accessible to this class has
1132
- # already been opened.
1133
- def connected?(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1134
- pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
1135
- pool && pool.connected?
1136
- end
1137
-
1138
- # Remove the connection for this class. This will close the active
1139
- # connection and the defined connection (if they exist). The result
1140
- # can be used as an argument for #establish_connection, for easily
1141
- # re-establishing the connection.
1142
- def remove_connection(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1143
- remove_connection_pool(owner, role: role, shard: shard)&.configuration_hash
1144
- end
1145
- deprecate remove_connection: "Use #remove_connection_pool, which now returns a DatabaseConfig object instead of a Hash"
1146
-
1147
- def remove_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1148
- if pool_manager = get_pool_manager(owner)
1149
- pool_config = pool_manager.remove_pool_config(role, shard)
1150
-
1151
- if pool_config
1152
- pool_config.disconnect!
1153
- pool_config.db_config
1154
- end
1155
- end
1156
- end
1157
-
1158
- # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
1159
- # This makes retrieving the connection pool O(1) once the process is warm.
1160
- # When a connection is established or removed, we invalidate the cache.
1161
- def retrieve_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1162
- pool_config = get_pool_manager(owner)&.get_pool_config(role, shard)
1163
- pool_config&.pool
1164
- end
1165
-
1166
- private
1167
- attr_reader :owner_to_pool_manager
1168
-
1169
- # Returns the pool manager for an owner.
1170
- #
1171
- # Using `"primary"` to look up the pool manager for `ActiveRecord::Base` is
1172
- # deprecated in favor of looking it up by `"ActiveRecord::Base"`.
1173
- #
1174
- # During the deprecation period, if `"primary"` is passed, the pool manager
1175
- # for `ActiveRecord::Base` will still be returned.
1176
- def get_pool_manager(owner)
1177
- return owner_to_pool_manager[owner] if owner_to_pool_manager.key?(owner)
1178
-
1179
- if owner == "primary"
1180
- ActiveSupport::Deprecation.warn("Using `\"primary\"` as a `connection_specification_name` is deprecated and will be removed in Rails 7.0.0. Please use `ActiveRecord::Base`.")
1181
- owner_to_pool_manager[Base.name]
1182
- end
1183
- end
1184
-
1185
- # Returns an instance of PoolConfig for a given adapter.
1186
- # Accepts a hash one layer deep that contains all connection information.
1187
- #
1188
- # == Example
1189
- #
1190
- # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
1191
- # pool_config = Base.configurations.resolve_pool_config(:production)
1192
- # pool_config.db_config.configuration_hash
1193
- # # => { host: "localhost", database: "foo", adapter: "sqlite3" }
1194
- #
1195
- def resolve_pool_config(config, owner_name)
1196
- db_config = Base.configurations.resolve(config)
1197
-
1198
- raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
1199
-
1200
- # Require the adapter itself and give useful feedback about
1201
- # 1. Missing adapter gems and
1202
- # 2. Adapter gems' missing dependencies.
1203
- path_to_adapter = "active_record/connection_adapters/#{db_config.adapter}_adapter"
1204
- begin
1205
- require path_to_adapter
1206
- rescue LoadError => e
1207
- # We couldn't require the adapter itself. Raise an exception that
1208
- # points out config typos and missing gems.
1209
- if e.path == path_to_adapter
1210
- # We can assume that a non-builtin adapter was specified, so it's
1211
- # either misspelled or missing from Gemfile.
1212
- raise LoadError, "Could not load the '#{db_config.adapter}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
1213
-
1214
- # Bubbled up from the adapter require. Prefix the exception message
1215
- # with some guidance about how to address it and reraise.
1216
- else
1217
- raise LoadError, "Error loading the '#{db_config.adapter}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
1218
- end
1219
- end
1220
-
1221
- unless ActiveRecord::Base.respond_to?(db_config.adapter_method)
1222
- raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
1223
- end
1224
-
1225
- ConnectionAdapters::PoolConfig.new(owner_name, db_config)
1226
- end
1227
- end
1228
714
  end
1229
715
  end
@@ -12,28 +12,11 @@ module ActiveRecord
12
12
  max_identifier_length
13
13
  end
14
14
 
15
- # Returns the maximum allowed length for an index name. This
16
- # limit is enforced by \Rails and is less than or equal to
17
- # #index_name_length. The gap between
18
- # #index_name_length is to allow internal \Rails
19
- # operations to use prefixes in temporary operations.
20
- def allowed_index_name_length
21
- index_name_length
22
- end
23
- deprecate :allowed_index_name_length
24
-
25
15
  # Returns the maximum length of an index name.
26
16
  def index_name_length
27
17
  max_identifier_length
28
18
  end
29
19
 
30
- # Returns the maximum number of elements in an IN (x,y,z) clause.
31
- # +nil+ means no limit.
32
- def in_clause_length
33
- nil
34
- end
35
- deprecate :in_clause_length
36
-
37
20
  private
38
21
  def bind_params_length
39
22
  65535