activerecord 6.1.7 → 7.1.5

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