activerecord 6.1.7 → 7.1.0

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 (307) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1516 -1019
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +50 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +35 -31
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency.rb +26 -16
  27. data/lib/active_record/associations/preloader/association.rb +207 -52
  28. data/lib/active_record/associations/preloader/batch.rb +48 -0
  29. data/lib/active_record/associations/preloader/branch.rb +147 -0
  30. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  31. data/lib/active_record/associations/preloader.rb +50 -121
  32. data/lib/active_record/associations/singular_association.rb +9 -3
  33. data/lib/active_record/associations/through_association.rb +25 -14
  34. data/lib/active_record/associations.rb +423 -289
  35. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  36. data/lib/active_record/attribute_assignment.rb +1 -3
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +61 -14
  39. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  40. data/lib/active_record/attribute_methods/query.rb +31 -19
  41. data/lib/active_record/attribute_methods/read.rb +25 -10
  42. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  44. data/lib/active_record/attribute_methods/write.rb +10 -13
  45. data/lib/active_record/attribute_methods.rb +121 -40
  46. data/lib/active_record/attributes.rb +27 -38
  47. data/lib/active_record/autosave_association.rb +61 -30
  48. data/lib/active_record/base.rb +25 -2
  49. data/lib/active_record/callbacks.rb +18 -34
  50. data/lib/active_record/coders/column_serializer.rb +61 -0
  51. data/lib/active_record/coders/json.rb +1 -1
  52. data/lib/active_record/coders/yaml_column.rb +70 -46
  53. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  54. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
  69. data/lib/active_record/connection_adapters/column.rb +13 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
  83. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
  98. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  99. data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
  100. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  101. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  102. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
  103. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  104. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  107. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  108. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  109. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  110. data/lib/active_record/connection_adapters.rb +9 -6
  111. data/lib/active_record/connection_handling.rb +107 -136
  112. data/lib/active_record/core.rb +194 -224
  113. data/lib/active_record/counter_cache.rb +46 -25
  114. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  115. data/lib/active_record/database_configurations/database_config.rb +21 -12
  116. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  117. data/lib/active_record/database_configurations/url_config.rb +18 -12
  118. data/lib/active_record/database_configurations.rb +95 -59
  119. data/lib/active_record/delegated_type.rb +61 -15
  120. data/lib/active_record/deprecator.rb +7 -0
  121. data/lib/active_record/destroy_association_async_job.rb +3 -1
  122. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  123. data/lib/active_record/dynamic_matchers.rb +1 -1
  124. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  125. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  126. data/lib/active_record/encryption/cipher.rb +53 -0
  127. data/lib/active_record/encryption/config.rb +68 -0
  128. data/lib/active_record/encryption/configurable.rb +60 -0
  129. data/lib/active_record/encryption/context.rb +42 -0
  130. data/lib/active_record/encryption/contexts.rb +76 -0
  131. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  132. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  133. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  134. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  135. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  136. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  137. data/lib/active_record/encryption/encryptor.rb +155 -0
  138. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  139. data/lib/active_record/encryption/errors.rb +15 -0
  140. data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
  141. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  142. data/lib/active_record/encryption/key.rb +28 -0
  143. data/lib/active_record/encryption/key_generator.rb +53 -0
  144. data/lib/active_record/encryption/key_provider.rb +46 -0
  145. data/lib/active_record/encryption/message.rb +33 -0
  146. data/lib/active_record/encryption/message_serializer.rb +92 -0
  147. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  148. data/lib/active_record/encryption/properties.rb +76 -0
  149. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  150. data/lib/active_record/encryption/scheme.rb +96 -0
  151. data/lib/active_record/encryption.rb +56 -0
  152. data/lib/active_record/enum.rb +156 -62
  153. data/lib/active_record/errors.rb +171 -15
  154. data/lib/active_record/explain.rb +23 -3
  155. data/lib/active_record/explain_registry.rb +11 -6
  156. data/lib/active_record/explain_subscriber.rb +1 -1
  157. data/lib/active_record/fixture_set/file.rb +15 -1
  158. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  159. data/lib/active_record/fixture_set/render_context.rb +2 -0
  160. data/lib/active_record/fixture_set/table_row.rb +70 -14
  161. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  162. data/lib/active_record/fixtures.rb +131 -86
  163. data/lib/active_record/future_result.rb +164 -0
  164. data/lib/active_record/gem_version.rb +3 -3
  165. data/lib/active_record/inheritance.rb +81 -29
  166. data/lib/active_record/insert_all.rb +133 -20
  167. data/lib/active_record/integration.rb +11 -10
  168. data/lib/active_record/internal_metadata.rb +117 -33
  169. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  170. data/lib/active_record/locking/optimistic.rb +36 -21
  171. data/lib/active_record/locking/pessimistic.rb +15 -6
  172. data/lib/active_record/log_subscriber.rb +52 -19
  173. data/lib/active_record/marshalling.rb +56 -0
  174. data/lib/active_record/message_pack.rb +124 -0
  175. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  176. data/lib/active_record/middleware/database_selector.rb +23 -13
  177. data/lib/active_record/middleware/shard_selector.rb +62 -0
  178. data/lib/active_record/migration/command_recorder.rb +108 -13
  179. data/lib/active_record/migration/compatibility.rb +221 -48
  180. data/lib/active_record/migration/default_strategy.rb +23 -0
  181. data/lib/active_record/migration/execution_strategy.rb +19 -0
  182. data/lib/active_record/migration/join_table.rb +1 -1
  183. data/lib/active_record/migration.rb +355 -171
  184. data/lib/active_record/model_schema.rb +116 -97
  185. data/lib/active_record/nested_attributes.rb +36 -15
  186. data/lib/active_record/no_touching.rb +3 -3
  187. data/lib/active_record/normalization.rb +159 -0
  188. data/lib/active_record/persistence.rb +405 -85
  189. data/lib/active_record/promise.rb +84 -0
  190. data/lib/active_record/query_cache.rb +3 -21
  191. data/lib/active_record/query_logs.rb +174 -0
  192. data/lib/active_record/query_logs_formatter.rb +41 -0
  193. data/lib/active_record/querying.rb +29 -6
  194. data/lib/active_record/railtie.rb +219 -43
  195. data/lib/active_record/railties/controller_runtime.rb +13 -9
  196. data/lib/active_record/railties/databases.rake +185 -249
  197. data/lib/active_record/railties/job_runtime.rb +23 -0
  198. data/lib/active_record/readonly_attributes.rb +41 -3
  199. data/lib/active_record/reflection.rb +229 -80
  200. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  201. data/lib/active_record/relation/batches.rb +192 -63
  202. data/lib/active_record/relation/calculations.rb +211 -90
  203. data/lib/active_record/relation/delegation.rb +27 -13
  204. data/lib/active_record/relation/finder_methods.rb +108 -51
  205. data/lib/active_record/relation/merger.rb +22 -13
  206. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  207. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  208. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  209. data/lib/active_record/relation/predicate_builder.rb +27 -20
  210. data/lib/active_record/relation/query_attribute.rb +30 -12
  211. data/lib/active_record/relation/query_methods.rb +654 -127
  212. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  213. data/lib/active_record/relation/spawn_methods.rb +20 -3
  214. data/lib/active_record/relation/where_clause.rb +10 -19
  215. data/lib/active_record/relation.rb +262 -120
  216. data/lib/active_record/result.rb +37 -11
  217. data/lib/active_record/runtime_registry.rb +18 -13
  218. data/lib/active_record/sanitization.rb +65 -20
  219. data/lib/active_record/schema.rb +36 -22
  220. data/lib/active_record/schema_dumper.rb +73 -24
  221. data/lib/active_record/schema_migration.rb +68 -33
  222. data/lib/active_record/scoping/default.rb +72 -15
  223. data/lib/active_record/scoping/named.rb +5 -13
  224. data/lib/active_record/scoping.rb +65 -34
  225. data/lib/active_record/secure_password.rb +60 -0
  226. data/lib/active_record/secure_token.rb +21 -3
  227. data/lib/active_record/serialization.rb +6 -1
  228. data/lib/active_record/signed_id.rb +10 -8
  229. data/lib/active_record/store.rb +10 -10
  230. data/lib/active_record/suppressor.rb +13 -15
  231. data/lib/active_record/table_metadata.rb +16 -3
  232. data/lib/active_record/tasks/database_tasks.rb +225 -136
  233. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  234. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  235. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  236. data/lib/active_record/test_databases.rb +1 -1
  237. data/lib/active_record/test_fixtures.rb +116 -96
  238. data/lib/active_record/timestamp.rb +28 -17
  239. data/lib/active_record/token_for.rb +113 -0
  240. data/lib/active_record/touch_later.rb +11 -6
  241. data/lib/active_record/transactions.rb +48 -27
  242. data/lib/active_record/translation.rb +3 -3
  243. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  244. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  245. data/lib/active_record/type/internal/timezone.rb +7 -2
  246. data/lib/active_record/type/serialized.rb +9 -5
  247. data/lib/active_record/type/time.rb +4 -0
  248. data/lib/active_record/type/type_map.rb +17 -20
  249. data/lib/active_record/type.rb +1 -2
  250. data/lib/active_record/validations/absence.rb +1 -1
  251. data/lib/active_record/validations/associated.rb +4 -4
  252. data/lib/active_record/validations/numericality.rb +5 -4
  253. data/lib/active_record/validations/presence.rb +5 -28
  254. data/lib/active_record/validations/uniqueness.rb +51 -6
  255. data/lib/active_record/validations.rb +8 -4
  256. data/lib/active_record/version.rb +1 -1
  257. data/lib/active_record.rb +335 -32
  258. data/lib/arel/attributes/attribute.rb +0 -8
  259. data/lib/arel/crud.rb +28 -22
  260. data/lib/arel/delete_manager.rb +18 -4
  261. data/lib/arel/errors.rb +10 -0
  262. data/lib/arel/factory_methods.rb +4 -0
  263. data/lib/arel/filter_predications.rb +9 -0
  264. data/lib/arel/insert_manager.rb +2 -3
  265. data/lib/arel/nodes/and.rb +4 -0
  266. data/lib/arel/nodes/binary.rb +6 -1
  267. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  268. data/lib/arel/nodes/casted.rb +1 -1
  269. data/lib/arel/nodes/cte.rb +36 -0
  270. data/lib/arel/nodes/delete_statement.rb +12 -13
  271. data/lib/arel/nodes/filter.rb +10 -0
  272. data/lib/arel/nodes/fragments.rb +35 -0
  273. data/lib/arel/nodes/function.rb +1 -0
  274. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  275. data/lib/arel/nodes/insert_statement.rb +2 -2
  276. data/lib/arel/nodes/leading_join.rb +8 -0
  277. data/lib/arel/nodes/node.rb +111 -2
  278. data/lib/arel/nodes/select_core.rb +2 -2
  279. data/lib/arel/nodes/select_statement.rb +2 -2
  280. data/lib/arel/nodes/sql_literal.rb +6 -0
  281. data/lib/arel/nodes/table_alias.rb +4 -0
  282. data/lib/arel/nodes/update_statement.rb +8 -3
  283. data/lib/arel/nodes.rb +5 -0
  284. data/lib/arel/predications.rb +13 -3
  285. data/lib/arel/select_manager.rb +10 -4
  286. data/lib/arel/table.rb +9 -6
  287. data/lib/arel/tree_manager.rb +0 -12
  288. data/lib/arel/update_manager.rb +18 -4
  289. data/lib/arel/visitors/dot.rb +80 -90
  290. data/lib/arel/visitors/mysql.rb +16 -3
  291. data/lib/arel/visitors/postgresql.rb +0 -10
  292. data/lib/arel/visitors/to_sql.rb +139 -19
  293. data/lib/arel/visitors/visitor.rb +2 -2
  294. data/lib/arel.rb +18 -3
  295. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  296. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  297. data/lib/rails/generators/active_record/migration.rb +3 -1
  298. data/lib/rails/generators/active_record/model/USAGE +113 -0
  299. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  300. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  302. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  303. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  304. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  305. metadata +92 -13
  306. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  307. data/lib/active_record/null_relation.rb +0 -67
@@ -3,32 +3,40 @@
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
10
12
  module AbstractPool # :nodoc:
11
- def get_schema_cache(connection)
12
- self.schema_cache ||= SchemaCache.new(connection)
13
- schema_cache.connection = connection
14
- schema_cache
15
- end
16
-
17
- def set_schema_cache(cache)
18
- self.schema_cache = cache
19
- end
20
13
  end
21
14
 
22
15
  class NullPool # :nodoc:
23
16
  include ConnectionAdapters::AbstractPool
24
17
 
25
- attr_accessor :schema_cache
18
+ class NullConfig # :nodoc:
19
+ def method_missing(*)
20
+ nil
21
+ end
22
+ end
23
+ NULL_CONFIG = NullConfig.new # :nodoc:
26
24
 
27
- def connection_klass
28
- nil
25
+ def schema_reflection
26
+ SchemaReflection.new(nil)
27
+ end
28
+
29
+ def connection_class; end
30
+ def checkin(_); end
31
+ def remove(_); end
32
+ def async_executor; end
33
+ def db_config
34
+ NULL_CONFIG
29
35
  end
30
36
  end
31
37
 
38
+ # = Active Record Connection Pool
39
+ #
32
40
  # Connection pool base class for managing Active Record database
33
41
  # connections.
34
42
  #
@@ -43,7 +51,7 @@ module ActiveRecord
43
51
  # handle cases in which there are more threads than connections: if all
44
52
  # connections have been checked out, and a thread tries to checkout a
45
53
  # connection anyway, then ConnectionPool will wait until some other thread
46
- # has checked in a connection.
54
+ # has checked in a connection, or the +checkout_timeout+ has expired.
47
55
  #
48
56
  # == Obtaining (checking out) a connection
49
57
  #
@@ -54,7 +62,7 @@ module ActiveRecord
54
62
  # as with Active Record 2.1 and
55
63
  # earlier (pre-connection-pooling). Eventually, when you're done with
56
64
  # the connection(s) and wish it to be returned to the pool, you call
57
- # {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
65
+ # {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
58
66
  # This will be the default behavior for Active Record when used in conjunction with
59
67
  # Action Pack's request handling cycle.
60
68
  # 2. Manually check out a connection from the pool with
@@ -68,6 +76,12 @@ module ActiveRecord
68
76
  # Connections in the pool are actually AbstractAdapter objects (or objects
69
77
  # compatible with AbstractAdapter's interface).
70
78
  #
79
+ # While a thread has a connection checked out from the pool using one of the
80
+ # above three methods, that connection will automatically be the one used
81
+ # by ActiveRecord queries executing on that thread. It is not required to
82
+ # explicitly pass the checked out connection to \Rails models or queries, for
83
+ # example.
84
+ #
71
85
  # == Options
72
86
  #
73
87
  # There are several connection-pooling-related options that you can add to
@@ -90,279 +104,14 @@ module ActiveRecord
90
104
  # * private methods that require being called in a +synchronize+ blocks
91
105
  # are now explicitly documented
92
106
  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
107
  include MonitorMixin
359
108
  include QueryCache::ConnectionPoolConfiguration
360
109
  include ConnectionAdapters::AbstractPool
361
110
 
362
111
  attr_accessor :automatic_reconnect, :checkout_timeout
363
- attr_reader :db_config, :size, :reaper, :pool_config, :connection_klass
112
+ attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
364
113
 
365
- delegate :schema_cache, :schema_cache=, to: :pool_config
114
+ delegate :schema_reflection, :schema_reflection=, to: :pool_config
366
115
 
367
116
  # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
368
117
  # object which describes database connection information (e.g. adapter,
@@ -375,7 +124,8 @@ module ActiveRecord
375
124
 
376
125
  @pool_config = pool_config
377
126
  @db_config = pool_config.db_config
378
- @connection_klass = pool_config.connection_klass
127
+ @role = pool_config.role
128
+ @shard = pool_config.shard
379
129
 
380
130
  @checkout_timeout = db_config.checkout_timeout
381
131
  @idle_timeout = db_config.idle_timeout
@@ -407,16 +157,22 @@ module ActiveRecord
407
157
 
408
158
  @lock_thread = false
409
159
 
160
+ @async_executor = build_async_executor
161
+
410
162
  @reaper = Reaper.new(self, db_config.reaping_frequency)
411
163
  @reaper.run
412
164
  end
413
165
 
414
166
  def lock_thread=(lock_thread)
415
167
  if lock_thread
416
- @lock_thread = Thread.current
168
+ @lock_thread = ActiveSupport::IsolatedExecutionState.context
417
169
  else
418
170
  @lock_thread = nil
419
171
  end
172
+
173
+ if (active_connection = @thread_cached_conns[connection_cache_key(current_thread)])
174
+ active_connection.lock_thread = @lock_thread
175
+ end
420
176
  end
421
177
 
422
178
  # Retrieve the connection associated with the current thread, or call
@@ -428,6 +184,12 @@ module ActiveRecord
428
184
  @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
429
185
  end
430
186
 
187
+ def connection_class # :nodoc:
188
+ pool_config.connection_class
189
+ end
190
+ alias :connection_klass :connection_class
191
+ deprecate :connection_klass, deprecator: ActiveRecord.deprecator
192
+
431
193
  # Returns true if there is an open connection being used for the current thread.
432
194
  #
433
195
  # This method only works for connections that have been obtained through
@@ -444,18 +206,23 @@ module ActiveRecord
444
206
  # This method only works for connections that have been obtained through
445
207
  # #connection or #with_connection methods, connections obtained through
446
208
  # #checkout will not be automatically released.
447
- def release_connection(owner_thread = Thread.current)
209
+ def release_connection(owner_thread = ActiveSupport::IsolatedExecutionState.context)
448
210
  if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
449
211
  checkin conn
450
212
  end
451
213
  end
452
214
 
453
- # If a connection obtained through #connection or #with_connection methods
454
- # already exists yield it to the block. If no such connection
455
- # exists checkout a connection, yield it to the block, and checkin the
456
- # connection when finished.
215
+ # Yields a connection from the connection pool to the block. If no connection
216
+ # is already checked out by the current thread, a connection will be checked
217
+ # out from the pool, yielded to the block, and then returned to the pool when
218
+ # the block is finished. If a connection has already been checked out on the
219
+ # current thread, such as via #connection or #with_connection, that existing
220
+ # connection will be the one yielded and it will not be returned to the pool
221
+ # automatically at the end of the block; it is expected that such an existing
222
+ # connection will be properly returned to the pool by the code that checked
223
+ # it out.
457
224
  def with_connection
458
- unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
225
+ unless conn = @thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)]
459
226
  conn = connection
460
227
  fresh_connection = true
461
228
  end
@@ -585,7 +352,9 @@ module ActiveRecord
585
352
  # Raises:
586
353
  # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
587
354
  def checkout(checkout_timeout = @checkout_timeout)
588
- checkout_and_verify(acquire_connection(checkout_timeout))
355
+ connection = checkout_and_verify(acquire_connection(checkout_timeout))
356
+ connection.lock_thread = @lock_thread
357
+ connection
589
358
  end
590
359
 
591
360
  # Check-in a database connection back into the pool, indicating that you
@@ -602,6 +371,7 @@ module ActiveRecord
602
371
  conn.expire
603
372
  end
604
373
 
374
+ conn.lock_thread = nil
605
375
  @available.add conn
606
376
  end
607
377
  end
@@ -713,7 +483,28 @@ module ActiveRecord
713
483
  end
714
484
  end
715
485
 
486
+ def schedule_query(future_result) # :nodoc:
487
+ @async_executor.post { future_result.execute_or_skip }
488
+ Thread.pass
489
+ end
490
+
716
491
  private
492
+ def build_async_executor
493
+ case ActiveRecord.async_query_executor
494
+ when :multi_thread_pool
495
+ if @db_config.max_threads > 0
496
+ Concurrent::ThreadPoolExecutor.new(
497
+ min_threads: @db_config.min_threads,
498
+ max_threads: @db_config.max_threads,
499
+ max_queue: @db_config.max_queue,
500
+ fallback_policy: :caller_runs
501
+ )
502
+ end
503
+ when :global_thread_pool
504
+ ActiveRecord.global_thread_pool_async_query_executor
505
+ end
506
+ end
507
+
717
508
  #--
718
509
  # this is unfortunately not concurrent
719
510
  def bulk_make_new_connections(num_new_conns_needed)
@@ -736,7 +527,7 @@ module ActiveRecord
736
527
  end
737
528
 
738
529
  def current_thread
739
- @lock_thread || Thread.current
530
+ @lock_thread || ActiveSupport::IsolatedExecutionState.context
740
531
  end
741
532
 
742
533
  # Take control of all existing connections so a "group" action such as
@@ -753,17 +544,17 @@ module ActiveRecord
753
544
  def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
754
545
  collected_conns = synchronize do
755
546
  # account for our own connections
756
- @connections.select { |conn| conn.owner == Thread.current }
547
+ @connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
757
548
  end
758
549
 
759
550
  newly_checked_out = []
760
- timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
551
+ timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
761
552
 
762
- @available.with_a_bias_for(Thread.current) do
553
+ @available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
763
554
  loop do
764
555
  synchronize do
765
556
  return if collected_conns.size == @connections.size && @now_connecting == 0
766
- remaining_timeout = timeout_time - Concurrent.monotonic_time
557
+ remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
767
558
  remaining_timeout = 0 if remaining_timeout < 0
768
559
  conn = checkout_for_exclusive_access(remaining_timeout)
769
560
  collected_conns << conn
@@ -806,7 +597,7 @@ module ActiveRecord
806
597
 
807
598
  thread_report = []
808
599
  @connections.each do |conn|
809
- unless conn.owner == Thread.current
600
+ unless conn.owner == ActiveSupport::IsolatedExecutionState.context
810
601
  thread_report << "#{conn} is owned by #{conn.owner}"
811
602
  end
812
603
  end
@@ -879,9 +670,12 @@ module ActiveRecord
879
670
  alias_method :release, :remove_connection_from_thread_cache
880
671
 
881
672
  def new_connection
882
- Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
883
- conn.check_version
884
- end
673
+ connection = Base.public_send(db_config.adapter_method, db_config.configuration_hash)
674
+ connection.pool = self
675
+ connection.check_version
676
+ connection
677
+ rescue ConnectionNotEstablished => ex
678
+ raise ex.set_pool(self)
885
679
  end
886
680
 
887
681
  # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
@@ -928,302 +722,14 @@ module ActiveRecord
928
722
 
929
723
  def checkout_and_verify(c)
930
724
  c._run_checkout_callbacks do
931
- c.verify!
725
+ c.clean!
932
726
  end
933
727
  c
934
- rescue
728
+ rescue Exception
935
729
  remove c
936
730
  c.disconnect!
937
731
  raise
938
732
  end
939
733
  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
734
  end
1229
735
  end