activerecord 4.2.11.3 → 6.0.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -1587
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +26 -12
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +133 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +136 -288
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +141 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +96 -38
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +806 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +238 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +469 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +517 -633
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +66 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +555 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +183 -41
  129. data/lib/active_record/core.rb +253 -229
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +315 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +519 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +286 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +597 -393
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +87 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +217 -151
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +166 -58
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Callbacks
4
+ # = Active Record \Callbacks
3
5
  #
4
- # Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
6
+ # \Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
5
7
  # before or after an alteration of the object state. This can be used to make sure that associated and
6
- # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
7
- # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
8
- # the <tt>Base#save</tt> call for a new record:
8
+ # dependent objects are deleted when {ActiveRecord::Base#destroy}[rdoc-ref:Persistence#destroy] is called (by overwriting +before_destroy+) or
9
+ # to massage attributes before they're validated (by overwriting +before_validation+).
10
+ # As an example of the callbacks initiated, consider the {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] call for a new record:
9
11
  #
10
12
  # * (-) <tt>save</tt>
11
13
  # * (-) <tt>valid</tt>
@@ -20,7 +22,7 @@ module ActiveRecord
20
22
  # * (7) <tt>after_commit</tt>
21
23
  #
22
24
  # Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
23
- # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
25
+ # Check out ActiveRecord::Transactions for more details about <tt>after_commit</tt> and
24
26
  # <tt>after_rollback</tt>.
25
27
  #
26
28
  # Additionally, an <tt>after_touch</tt> callback is triggered whenever an
@@ -31,7 +33,7 @@ module ActiveRecord
31
33
  # are instantiated as well.
32
34
  #
33
35
  # There are nineteen callbacks in total, which give you immense power to react and prepare for each state in the
34
- # Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
36
+ # Active Record life cycle. The sequence for calling {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] for an existing record is similar,
35
37
  # except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
36
38
  #
37
39
  # Examples:
@@ -53,9 +55,9 @@ module ActiveRecord
53
55
  # end
54
56
  #
55
57
  # class Firm < ActiveRecord::Base
56
- # # Destroys the associated clients and people when the firm is destroyed
57
- # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
58
- # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
58
+ # # Disables access to the system, for associated clients and people when the firm is destroyed
59
+ # before_destroy { |record| Person.where(firm_id: record.id).update_all(access: 'disabled') }
60
+ # before_destroy { |record| Client.where(client_of: record.id).update_all(access: 'disabled') }
59
61
  # end
60
62
  #
61
63
  # == Inheritable callback queues
@@ -73,21 +75,7 @@ module ActiveRecord
73
75
  # end
74
76
  #
75
77
  # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
76
- # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
77
- # where the +before_destroy+ method is overridden:
78
- #
79
- # class Topic < ActiveRecord::Base
80
- # def before_destroy() destroy_author end
81
- # end
82
- #
83
- # class Reply < Topic
84
- # def before_destroy() destroy_readers end
85
- # end
86
- #
87
- # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
88
- # So, use the callback macros when you want to ensure that a certain callback is called for the entire
89
- # hierarchy, and use the regular overwritable methods when you want to leave it up to each descendant
90
- # to decide whether they want to call +super+ and trigger the inherited callbacks.
78
+ # run, both +destroy_author+ and +destroy_readers+ are called.
91
79
  #
92
80
  # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
93
81
  # callbacks before specifying the associations. Otherwise, you might trigger the loading of a
@@ -96,9 +84,9 @@ module ActiveRecord
96
84
  # == Types of callbacks
97
85
  #
98
86
  # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
99
- # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
87
+ # inline methods (using a proc). Method references and callback objects
100
88
  # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
101
- # creating mix-ins), and inline eval methods are deprecated.
89
+ # creating mix-ins).
102
90
  #
103
91
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
104
92
  #
@@ -107,7 +95,7 @@ module ActiveRecord
107
95
  #
108
96
  # private
109
97
  # def delete_parents
110
- # self.class.delete_all "parent_id = #{id}"
98
+ # self.class.delete_by(parent_id: id)
111
99
  # end
112
100
  # end
113
101
  #
@@ -140,7 +128,7 @@ module ActiveRecord
140
128
  # end
141
129
  # end
142
130
  #
143
- # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
131
+ # So you specify the object you want to be messaged on a given callback. When that callback is triggered, the object has
144
132
  # a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
145
133
  # initialization data such as the name of the attribute to work with:
146
134
  #
@@ -175,43 +163,30 @@ module ActiveRecord
175
163
  # end
176
164
  # end
177
165
  #
178
- # The callback macros usually accept a symbol for the method they're supposed to run, but you can also
179
- # pass a "method string", which will then be evaluated within the binding of the callback. Example:
180
- #
181
- # class Topic < ActiveRecord::Base
182
- # before_destroy 'self.class.delete_all "parent_id = #{id}"'
183
- # end
184
- #
185
- # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
186
- # is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
187
- #
188
- # class Topic < ActiveRecord::Base
189
- # before_destroy 'self.class.delete_all "parent_id = #{id}"',
190
- # 'puts "Evaluated after parents are destroyed"'
191
- # end
192
- #
193
166
  # == <tt>before_validation*</tt> returning statements
194
167
  #
195
- # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
196
- # aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
197
- # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
168
+ # If the +before_validation+ callback throws +:abort+, the process will be
169
+ # aborted and {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] will return +false+.
170
+ # If {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] is called it will raise an ActiveRecord::RecordInvalid exception.
171
+ # Nothing will be appended to the errors object.
198
172
  #
199
173
  # == Canceling callbacks
200
174
  #
201
- # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
202
- # cancelled.
175
+ # If a <tt>before_*</tt> callback throws +:abort+, all the later callbacks and
176
+ # the associated action are cancelled.
203
177
  # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
204
178
  # methods on the model, which are called last.
205
179
  #
206
180
  # == Ordering callbacks
207
181
  #
208
182
  # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
209
- # callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option.
183
+ # callback (+log_children+ in this case) should be executed before the children get destroyed by the
184
+ # <tt>dependent: :destroy</tt> option.
210
185
  #
211
186
  # Let's look at the code below:
212
187
  #
213
188
  # class Topic < ActiveRecord::Base
214
- # has_many :children, dependent: destroy
189
+ # has_many :children, dependent: :destroy
215
190
  #
216
191
  # before_destroy :log_children
217
192
  #
@@ -222,10 +197,11 @@ module ActiveRecord
222
197
  # end
223
198
  #
224
199
  # In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
225
- # because the +destroy+ callback gets executed first. You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
200
+ # because the {ActiveRecord::Base#destroy}[rdoc-ref:Persistence#destroy] callback gets executed first.
201
+ # You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
226
202
  #
227
203
  # class Topic < ActiveRecord::Base
228
- # has_many :children, dependent: destroy
204
+ # has_many :children, dependent: :destroy
229
205
  #
230
206
  # before_destroy :log_children, prepend: true
231
207
  #
@@ -235,23 +211,72 @@ module ActiveRecord
235
211
  # end
236
212
  # end
237
213
  #
238
- # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
214
+ # This way, the +before_destroy+ gets executed before the <tt>dependent: :destroy</tt> is called, and the data is still available.
215
+ #
216
+ # Also, there are cases when you want several callbacks of the same type to
217
+ # be executed in order.
218
+ #
219
+ # For example:
220
+ #
221
+ # class Topic < ActiveRecord::Base
222
+ # has_many :children
223
+ #
224
+ # after_save :log_children
225
+ # after_save :do_something_else
239
226
  #
240
- # == Transactions
227
+ # private
241
228
  #
242
- # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
243
- # within a transaction. That includes <tt>after_*</tt> hooks. If everything
244
- # goes fine a COMMIT is executed once the chain has been completed.
229
+ # def log_children
230
+ # # Child processing
231
+ # end
232
+ #
233
+ # def do_something_else
234
+ # # Something else
235
+ # end
236
+ # end
237
+ #
238
+ # In this case the +log_children+ gets executed before +do_something_else+.
239
+ # The same applies to all non-transactional callbacks.
240
+ #
241
+ # In case there are multiple transactional callbacks as seen below, the order
242
+ # is reversed.
243
+ #
244
+ # For example:
245
+ #
246
+ # class Topic < ActiveRecord::Base
247
+ # has_many :children
248
+ #
249
+ # after_commit :log_children
250
+ # after_commit :do_something_else
251
+ #
252
+ # private
253
+ #
254
+ # def log_children
255
+ # # Child processing
256
+ # end
257
+ #
258
+ # def do_something_else
259
+ # # Something else
260
+ # end
261
+ # end
262
+ #
263
+ # In this case the +do_something_else+ gets executed before +log_children+.
264
+ #
265
+ # == \Transactions
266
+ #
267
+ # The entire callback chain of a {#save}[rdoc-ref:Persistence#save], {#save!}[rdoc-ref:Persistence#save!],
268
+ # or {#destroy}[rdoc-ref:Persistence#destroy] call runs within a transaction. That includes <tt>after_*</tt> hooks.
269
+ # If everything goes fine a COMMIT is executed once the chain has been completed.
245
270
  #
246
271
  # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
247
272
  # can also trigger a ROLLBACK raising an exception in any of the callbacks,
248
273
  # including <tt>after_*</tt> hooks. Note, however, that in that case the client
249
- # needs to be aware of it because an ordinary +save+ will raise such exception
274
+ # needs to be aware of it because an ordinary {#save}[rdoc-ref:Persistence#save] will raise such exception
250
275
  # instead of quietly returning +false+.
251
276
  #
252
277
  # == Debugging callbacks
253
278
  #
254
- # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
279
+ # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. Active Model \Callbacks support
255
280
  # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
256
281
  # defines what part of the chain the callback runs in.
257
282
  #
@@ -277,36 +302,37 @@ module ActiveRecord
277
302
  :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
278
303
  ]
279
304
 
280
- module ClassMethods
281
- include ActiveModel::Callbacks
282
- end
283
-
284
- included do
285
- include ActiveModel::Validations::Callbacks
286
-
287
- define_model_callbacks :initialize, :find, :touch, :only => :after
288
- define_model_callbacks :save, :create, :update, :destroy
289
- end
290
-
291
305
  def destroy #:nodoc:
306
+ @_destroy_callback_already_called ||= false
307
+ return if @_destroy_callback_already_called
308
+ @_destroy_callback_already_called = true
292
309
  _run_destroy_callbacks { super }
310
+ rescue RecordNotDestroyed => e
311
+ @_association_destroy_exception = e
312
+ false
313
+ ensure
314
+ @_destroy_callback_already_called = false
293
315
  end
294
316
 
295
317
  def touch(*) #:nodoc:
296
318
  _run_touch_callbacks { super }
297
319
  end
298
320
 
321
+ def increment!(attribute, by = 1, touch: nil) # :nodoc:
322
+ touch ? _run_touch_callbacks { super } : super
323
+ end
324
+
299
325
  private
300
326
 
301
- def create_or_update #:nodoc:
327
+ def create_or_update(**)
302
328
  _run_save_callbacks { super }
303
329
  end
304
330
 
305
- def _create_record #:nodoc:
331
+ def _create_record
306
332
  _run_create_callbacks { super }
307
333
  end
308
334
 
309
- def _update_record(*) #:nodoc:
335
+ def _update_record
310
336
  _run_update_callbacks { super }
311
337
  end
312
338
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Coders # :nodoc:
3
5
  class JSON # :nodoc:
@@ -6,7 +8,7 @@ module ActiveRecord
6
8
  end
7
9
 
8
10
  def self.load(json)
9
- ActiveSupport::JSON.decode(json) unless json.nil?
11
+ ActiveSupport::JSON.decode(json) unless json.blank?
10
12
  end
11
13
  end
12
14
  end
@@ -1,38 +1,50 @@
1
- require 'yaml'
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
2
4
 
3
5
  module ActiveRecord
4
6
  module Coders # :nodoc:
5
7
  class YAMLColumn # :nodoc:
6
-
7
8
  attr_accessor :object_class
8
9
 
9
- def initialize(object_class = Object)
10
+ def initialize(attr_name, object_class = Object)
11
+ @attr_name = attr_name
10
12
  @object_class = object_class
13
+ check_arity_of_constructor
11
14
  end
12
15
 
13
16
  def dump(obj)
14
17
  return if obj.nil?
15
18
 
16
- unless obj.is_a?(object_class)
17
- raise SerializationTypeMismatch,
18
- "Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
19
- end
19
+ assert_valid_value(obj, action: "dump")
20
20
  YAML.dump obj
21
21
  end
22
22
 
23
23
  def load(yaml)
24
24
  return object_class.new if object_class != Object && yaml.nil?
25
- return yaml unless yaml.is_a?(String) && yaml =~ /^---/
25
+ return yaml unless yaml.is_a?(String) && /^---/.match?(yaml)
26
26
  obj = YAML.load(yaml)
27
27
 
28
- unless obj.is_a?(object_class) || obj.nil?
29
- raise SerializationTypeMismatch,
30
- "Attribute was supposed to be a #{object_class}, but was a #{obj.class}"
31
- end
28
+ assert_valid_value(obj, action: "load")
32
29
  obj ||= object_class.new if object_class != Object
33
30
 
34
31
  obj
35
32
  end
33
+
34
+ def assert_valid_value(obj, action:)
35
+ unless obj.nil? || obj.is_a?(object_class)
36
+ raise SerializationTypeMismatch,
37
+ "can't #{action} `#{@attr_name}`: was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def check_arity_of_constructor
44
+ load(nil)
45
+ rescue ArgumentError
46
+ raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
47
+ end
36
48
  end
37
49
  end
38
50
  end
@@ -1,8 +1,9 @@
1
- require 'thread'
2
- require 'thread_safe'
3
- require 'monitor'
4
- require 'set'
5
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+ require "concurrent/map"
5
+ require "monitor"
6
+ require "weakref"
6
7
 
7
8
  module ActiveRecord
8
9
  # Raised when a connection could not be obtained within the connection
@@ -11,7 +12,34 @@ module ActiveRecord
11
12
  class ConnectionTimeoutError < ConnectionNotEstablished
12
13
  end
13
14
 
15
+ # Raised when a pool was unable to get ahold of all its connections
16
+ # to perform a "group" action such as
17
+ # {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
18
+ # or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
19
+ class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
20
+ end
21
+
14
22
  module ConnectionAdapters
23
+ module AbstractPool # :nodoc:
24
+ def get_schema_cache(connection)
25
+ @schema_cache ||= SchemaCache.new(connection)
26
+ @schema_cache.connection = connection
27
+ @schema_cache
28
+ end
29
+
30
+ def set_schema_cache(cache)
31
+ @schema_cache = cache
32
+ end
33
+ end
34
+
35
+ class NullPool # :nodoc:
36
+ include ConnectionAdapters::AbstractPool
37
+
38
+ def initialize
39
+ @schema_cache = nil
40
+ end
41
+ end
42
+
15
43
  # Connection pool base class for managing Active Record database
16
44
  # connections.
17
45
  #
@@ -33,17 +61,18 @@ module ActiveRecord
33
61
  # Connections can be obtained and used from a connection pool in several
34
62
  # ways:
35
63
  #
36
- # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
64
+ # 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection]
65
+ # as with Active Record 2.1 and
37
66
  # earlier (pre-connection-pooling). Eventually, when you're done with
38
67
  # the connection(s) and wish it to be returned to the pool, you call
39
- # ActiveRecord::Base.clear_active_connections!. This will be the
40
- # default behavior for Active Record when used in conjunction with
68
+ # {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
69
+ # This will be the default behavior for Active Record when used in conjunction with
41
70
  # Action Pack's request handling cycle.
42
71
  # 2. Manually check out a connection from the pool with
43
- # ActiveRecord::Base.connection_pool.checkout. You are responsible for
72
+ # {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
44
73
  # returning this connection to the pool when finished by calling
45
- # ActiveRecord::Base.connection_pool.checkin(connection).
46
- # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
74
+ # {ActiveRecord::Base.connection_pool.checkin(connection)}[rdoc-ref:#checkin].
75
+ # 3. Use {ActiveRecord::Base.connection_pool.with_connection(&block)}[rdoc-ref:#with_connection], which
47
76
  # obtains a connection, yields it as the sole argument to the block,
48
77
  # and returns it to the pool after the block completes.
49
78
  #
@@ -55,21 +84,25 @@ module ActiveRecord
55
84
  # There are several connection-pooling-related options that you can add to
56
85
  # your database connection configuration:
57
86
  #
58
- # * +pool+: number indicating size of connection pool (default 5)
59
- # * +checkout_timeout+: number of seconds to block and wait for a connection
60
- # before giving up and raising a timeout error (default 5 seconds).
61
- # * +reaping_frequency+: frequency in seconds to periodically run the
62
- # Reaper, which attempts to find and recover connections from dead
63
- # threads, which can occur if a programmer forgets to close a
64
- # connection at the end of a thread or a thread dies unexpectedly.
65
- # Regardless of this setting, the Reaper will be invoked before every
66
- # blocking wait. (Default nil, which means don't schedule the Reaper).
87
+ # * +pool+: maximum number of connections the pool may manage (default 5).
88
+ # * +idle_timeout+: number of seconds that a connection will be kept
89
+ # unused in the pool before it is automatically disconnected (default
90
+ # 300 seconds). Set this to zero to keep connections forever.
91
+ # * +checkout_timeout+: number of seconds to wait for a connection to
92
+ # become available before giving up and raising a timeout error (default
93
+ # 5 seconds).
94
+ #
95
+ #--
96
+ # Synchronization policy:
97
+ # * all public methods can be called outside +synchronize+
98
+ # * access to these instance variables needs to be in +synchronize+:
99
+ # * @connections
100
+ # * @now_connecting
101
+ # * private methods that require being called in a +synchronize+ blocks
102
+ # are now explicitly documented
67
103
  class ConnectionPool
68
- # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
69
- # with which it shares a Monitor. But could be a generic Queue.
70
- #
71
- # The Queue in stdlib's 'thread' could replace this class except
72
- # stdlib's doesn't support waiting with a timeout.
104
+ # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool
105
+ # with which it shares a Monitor.
73
106
  class Queue
74
107
  def initialize(lock = Monitor.new)
75
108
  @lock = lock
@@ -101,7 +134,7 @@ module ActiveRecord
101
134
  end
102
135
  end
103
136
 
104
- # If +element+ is in the queue, remove and return it, or nil.
137
+ # If +element+ is in the queue, remove and return it, or +nil+.
105
138
  def delete(element)
106
139
  synchronize do
107
140
  @queue.delete(element)
@@ -120,86 +153,160 @@ module ActiveRecord
120
153
  # If +timeout+ is not given, remove and return the head the
121
154
  # queue if the number of available elements is strictly
122
155
  # greater than the number of threads currently waiting (that
123
- # is, don't jump ahead in line). Otherwise, return nil.
156
+ # is, don't jump ahead in line). Otherwise, return +nil+.
124
157
  #
125
- # If +timeout+ is given, block if it there is no element
158
+ # If +timeout+ is given, block if there is no element
126
159
  # available, waiting up to +timeout+ seconds for an element to
127
160
  # become available.
128
161
  #
129
162
  # Raises:
130
- # - ConnectionTimeoutError if +timeout+ is given and no element
131
- # becomes available after +timeout+ seconds,
163
+ # - ActiveRecord::ConnectionTimeoutError if +timeout+ is given and no element
164
+ # becomes available within +timeout+ seconds,
132
165
  def poll(timeout = nil)
133
- synchronize do
134
- if timeout
135
- no_wait_poll || wait_poll(timeout)
136
- else
137
- no_wait_poll
138
- end
139
- end
166
+ synchronize { internal_poll(timeout) }
140
167
  end
141
168
 
142
169
  private
143
170
 
144
- def synchronize(&block)
145
- @lock.synchronize(&block)
146
- end
171
+ def internal_poll(timeout)
172
+ no_wait_poll || (timeout && wait_poll(timeout))
173
+ end
147
174
 
148
- # Test if the queue currently contains any elements.
149
- def any?
150
- !@queue.empty?
151
- end
175
+ def synchronize(&block)
176
+ @lock.synchronize(&block)
177
+ end
152
178
 
153
- # A thread can remove an element from the queue without
154
- # waiting if an only if the number of currently available
155
- # connections is strictly greater than the number of waiting
156
- # threads.
157
- def can_remove_no_wait?
158
- @queue.size > @num_waiting
159
- end
179
+ # Test if the queue currently contains any elements.
180
+ def any?
181
+ !@queue.empty?
182
+ end
160
183
 
161
- # Removes and returns the head of the queue if possible, or nil.
162
- def remove
163
- @queue.shift
164
- end
184
+ # A thread can remove an element from the queue without
185
+ # waiting if and only if the number of currently available
186
+ # connections is strictly greater than the number of waiting
187
+ # threads.
188
+ def can_remove_no_wait?
189
+ @queue.size > @num_waiting
190
+ end
165
191
 
166
- # Remove and return the head the queue if the number of
167
- # available elements is strictly greater than the number of
168
- # threads currently waiting. Otherwise, return nil.
169
- def no_wait_poll
170
- remove if can_remove_no_wait?
171
- end
192
+ # Removes and returns the head of the queue if possible, or +nil+.
193
+ def remove
194
+ @queue.pop
195
+ end
196
+
197
+ # Remove and return the head the queue if the number of
198
+ # available elements is strictly greater than the number of
199
+ # threads currently waiting. Otherwise, return +nil+.
200
+ def no_wait_poll
201
+ remove if can_remove_no_wait?
202
+ end
203
+
204
+ # Waits on the queue up to +timeout+ seconds, then removes and
205
+ # returns the head of the queue.
206
+ def wait_poll(timeout)
207
+ @num_waiting += 1
208
+
209
+ t0 = Concurrent.monotonic_time
210
+ elapsed = 0
211
+ loop do
212
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
213
+ @cond.wait(timeout - elapsed)
214
+ end
215
+
216
+ return remove if any?
217
+
218
+ elapsed = Concurrent.monotonic_time - t0
219
+ if elapsed >= timeout
220
+ msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
221
+ [timeout, elapsed]
222
+ raise ConnectionTimeoutError, msg
223
+ end
224
+ end
225
+ ensure
226
+ @num_waiting -= 1
227
+ end
228
+ end
172
229
 
173
- # Waits on the queue up to +timeout+ seconds, then removes and
174
- # returns the head of the queue.
175
- def wait_poll(timeout)
176
- @num_waiting += 1
230
+ # Adds the ability to turn a basic fair FIFO queue into one
231
+ # biased to some thread.
232
+ module BiasableQueue # :nodoc:
233
+ class BiasedConditionVariable # :nodoc:
234
+ # semantics of condition variables guarantee that +broadcast+, +broadcast_on_biased+,
235
+ # +signal+ and +wait+ methods are only called while holding a lock
236
+ def initialize(lock, other_cond, preferred_thread)
237
+ @real_cond = lock.new_cond
238
+ @other_cond = other_cond
239
+ @preferred_thread = preferred_thread
240
+ @num_waiting_on_real_cond = 0
241
+ end
177
242
 
178
- t0 = Time.now
179
- elapsed = 0
180
- loop do
181
- @cond.wait(timeout - elapsed)
243
+ def broadcast
244
+ broadcast_on_biased
245
+ @other_cond.broadcast
246
+ end
182
247
 
183
- return remove if any?
248
+ def broadcast_on_biased
249
+ @num_waiting_on_real_cond = 0
250
+ @real_cond.broadcast
251
+ end
184
252
 
185
- elapsed = Time.now - t0
186
- if elapsed >= timeout
187
- msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
188
- [timeout, elapsed]
189
- raise ConnectionTimeoutError, msg
190
- end
253
+ def signal
254
+ if @num_waiting_on_real_cond > 0
255
+ @num_waiting_on_real_cond -= 1
256
+ @real_cond
257
+ else
258
+ @other_cond
259
+ end.signal
260
+ end
261
+
262
+ def wait(timeout)
263
+ if Thread.current == @preferred_thread
264
+ @num_waiting_on_real_cond += 1
265
+ @real_cond
266
+ else
267
+ @other_cond
268
+ end.wait(timeout)
191
269
  end
270
+ end
271
+
272
+ def with_a_bias_for(thread)
273
+ previous_cond = nil
274
+ new_cond = nil
275
+ synchronize do
276
+ previous_cond = @cond
277
+ @cond = new_cond = BiasedConditionVariable.new(@lock, @cond, thread)
278
+ end
279
+ yield
192
280
  ensure
193
- @num_waiting -= 1
281
+ synchronize do
282
+ @cond = previous_cond if previous_cond
283
+ new_cond.broadcast_on_biased if new_cond # wake up any remaining sleepers
284
+ end
194
285
  end
195
286
  end
196
287
 
197
- # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
198
- # A reaper instantiated with a nil frequency will never reap the
199
- # connection pool.
288
+ # Connections must be leased while holding the main pool mutex. This is
289
+ # an internal subclass that also +.leases+ returned connections while
290
+ # still in queue's critical section (queue synchronizes with the same
291
+ # <tt>@lock</tt> as the main pool) so that a returned connection is already
292
+ # leased and there is no need to re-enter synchronized block.
293
+ class ConnectionLeasingQueue < Queue # :nodoc:
294
+ include BiasableQueue
295
+
296
+ private
297
+ def internal_poll(timeout)
298
+ conn = super
299
+ conn.lease if conn
300
+ conn
301
+ end
302
+ end
303
+
304
+ # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
305
+ # +pool+. A reaper instantiated with a zero frequency will never reap
306
+ # the connection pool.
200
307
  #
201
- # Configure the frequency by setting "reaping_frequency" in your
202
- # database yaml file.
308
+ # Configure the frequency by setting +reaping_frequency+ in your database
309
+ # yaml file (default 60 seconds).
203
310
  class Reaper
204
311
  attr_reader :pool, :frequency
205
312
 
@@ -208,21 +315,59 @@ module ActiveRecord
208
315
  @frequency = frequency
209
316
  end
210
317
 
211
- def run
212
- return unless frequency
213
- Thread.new(frequency, pool) { |t, p|
214
- while true
215
- sleep t
216
- p.reap
318
+ @mutex = Mutex.new
319
+ @pools = {}
320
+ @threads = {}
321
+
322
+ class << self
323
+ def register_pool(pool, frequency) # :nodoc:
324
+ @mutex.synchronize do
325
+ unless @threads[frequency]&.alive?
326
+ @threads[frequency] = spawn_thread(frequency)
327
+ end
328
+ @pools[frequency] ||= []
329
+ @pools[frequency] << WeakRef.new(pool)
330
+ end
331
+ end
332
+
333
+ private
334
+
335
+ def spawn_thread(frequency)
336
+ Thread.new(frequency) do |t|
337
+ running = true
338
+ while running
339
+ sleep t
340
+ @mutex.synchronize do
341
+ @pools[frequency].select!(&:weakref_alive?)
342
+ @pools[frequency].each do |p|
343
+ p.reap
344
+ p.flush
345
+ rescue WeakRef::RefError
346
+ end
347
+
348
+ if @pools[frequency].empty?
349
+ @pools.delete(frequency)
350
+ @threads.delete(frequency)
351
+ running = false
352
+ end
353
+ end
354
+ end
355
+ end
217
356
  end
218
- }
357
+ end
358
+
359
+ def run
360
+ return unless frequency && frequency > 0
361
+ self.class.register_pool(pool, frequency)
219
362
  end
220
363
  end
221
364
 
222
365
  include MonitorMixin
366
+ include QueryCache::ConnectionPoolConfiguration
367
+ include ConnectionAdapters::AbstractPool
223
368
 
224
- attr_accessor :automatic_reconnect, :checkout_timeout
225
- attr_reader :spec, :connections, :size, :reaper
369
+ attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
370
+ attr_reader :spec, :size, :reaper
226
371
 
227
372
  # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
228
373
  # object which describes database connection information (e.g. adapter,
@@ -236,62 +381,98 @@ module ActiveRecord
236
381
  @spec = spec
237
382
 
238
383
  @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
239
- @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
240
- @reaper.run
384
+ if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
385
+ @idle_timeout = @idle_timeout.to_f
386
+ @idle_timeout = nil if @idle_timeout <= 0
387
+ end
241
388
 
242
389
  # default max pool size to 5
243
390
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
244
391
 
245
- # The cache of reserved connections mapped to threads
246
- @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
392
+ # This variable tracks the cache of threads mapped to reserved connections, with the
393
+ # sole purpose of speeding up the +connection+ method. It is not the authoritative
394
+ # registry of which thread owns which connection. Connection ownership is tracked by
395
+ # the +connection.owner+ attr on each +connection+ instance.
396
+ # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
397
+ # then that +thread+ does indeed own that +conn+. However, an absence of a such
398
+ # mapping does not mean that the +thread+ doesn't own the said connection. In
399
+ # that case +conn.owner+ attr should be consulted.
400
+ # Access and modification of <tt>@thread_cached_conns</tt> does not require
401
+ # synchronization.
402
+ @thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
247
403
 
248
404
  @connections = []
249
405
  @automatic_reconnect = true
250
406
 
251
- @available = Queue.new self
407
+ # Connection pool allows for concurrent (outside the main +synchronize+ section)
408
+ # establishment of new connections. This variable tracks the number of threads
409
+ # currently in the process of independently establishing connections to the DB.
410
+ @now_connecting = 0
411
+
412
+ @threads_blocking_new_connections = 0
413
+
414
+ @available = ConnectionLeasingQueue.new self
415
+
416
+ @lock_thread = false
417
+
418
+ # +reaping_frequency+ is configurable mostly for historical reasons, but it could
419
+ # also be useful if someone wants a very low +idle_timeout+.
420
+ reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
421
+ @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
422
+ @reaper.run
423
+ end
424
+
425
+ def lock_thread=(lock_thread)
426
+ if lock_thread
427
+ @lock_thread = Thread.current
428
+ else
429
+ @lock_thread = nil
430
+ end
252
431
  end
253
432
 
254
433
  # Retrieve the connection associated with the current thread, or call
255
434
  # #checkout to obtain one if necessary.
256
435
  #
257
436
  # #connection can be called any number of times; the connection is
258
- # held in a hash keyed by the thread id.
437
+ # held in a cache keyed by a thread.
259
438
  def connection
260
- # this is correctly done double-checked locking
261
- # (ThreadSafe::Cache's lookups have volatile semantics)
262
- @reserved_connections[current_connection_id] || synchronize do
263
- @reserved_connections[current_connection_id] ||= checkout
264
- end
439
+ @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
265
440
  end
266
441
 
267
- # Is there an open connection that is being used for the current thread?
442
+ # Returns true if there is an open connection being used for the current thread.
443
+ #
444
+ # This method only works for connections that have been obtained through
445
+ # #connection or #with_connection methods. Connections obtained through
446
+ # #checkout will not be detected by #active_connection?
268
447
  def active_connection?
269
- synchronize do
270
- @reserved_connections.fetch(current_connection_id) {
271
- return false
272
- }.in_use?
273
- end
448
+ @thread_cached_conns[connection_cache_key(current_thread)]
274
449
  end
275
450
 
276
451
  # Signal that the thread is finished with the current connection.
277
452
  # #release_connection releases the connection-thread association
278
453
  # and returns the connection to the pool.
279
- def release_connection(with_id = current_connection_id)
280
- synchronize do
281
- conn = @reserved_connections.delete(with_id)
282
- checkin conn if conn
454
+ #
455
+ # This method only works for connections that have been obtained through
456
+ # #connection or #with_connection methods, connections obtained through
457
+ # #checkout will not be automatically released.
458
+ def release_connection(owner_thread = Thread.current)
459
+ if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
460
+ checkin conn
283
461
  end
284
462
  end
285
463
 
286
- # If a connection already exists yield it to the block. If no connection
464
+ # If a connection obtained through #connection or #with_connection methods
465
+ # already exists yield it to the block. If no such connection
287
466
  # exists checkout a connection, yield it to the block, and checkin the
288
467
  # connection when finished.
289
468
  def with_connection
290
- connection_id = current_connection_id
291
- fresh_connection = true unless active_connection?
292
- yield connection
469
+ unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
470
+ conn = connection
471
+ fresh_connection = true
472
+ end
473
+ yield conn
293
474
  ensure
294
- release_connection(connection_id) if fresh_connection
475
+ release_connection if fresh_connection
295
476
  end
296
477
 
297
478
  # Returns true if a connection has already been opened.
@@ -299,37 +480,103 @@ module ActiveRecord
299
480
  synchronize { @connections.any? }
300
481
  end
301
482
 
483
+ # Returns an array containing the connections currently in the pool.
484
+ # Access to the array does not require synchronization on the pool because
485
+ # the array is newly created and not retained by the pool.
486
+ #
487
+ # However; this method bypasses the ConnectionPool's thread-safe connection
488
+ # access pattern. A returned connection may be owned by another thread,
489
+ # unowned, or by happen-stance owned by the calling thread.
490
+ #
491
+ # Calling methods on a connection without ownership is subject to the
492
+ # thread-safety guarantees of the underlying method. Many of the methods
493
+ # on connection adapter classes are inherently multi-thread unsafe.
494
+ def connections
495
+ synchronize { @connections.dup }
496
+ end
497
+
302
498
  # Disconnects all connections in the pool, and clears the pool.
303
- def disconnect!
304
- synchronize do
305
- @reserved_connections.clear
306
- @connections.each do |conn|
307
- checkin conn
308
- conn.disconnect!
499
+ #
500
+ # Raises:
501
+ # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
502
+ # connections in the pool within a timeout interval (default duration is
503
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
504
+ def disconnect(raise_on_acquisition_timeout = true)
505
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
506
+ synchronize do
507
+ @connections.each do |conn|
508
+ if conn.in_use?
509
+ conn.steal!
510
+ checkin conn
511
+ end
512
+ conn.disconnect!
513
+ end
514
+ @connections = []
515
+ @available.clear
309
516
  end
310
- @connections = []
311
- @available.clear
312
517
  end
313
518
  end
314
519
 
315
- # Clears the cache which maps classes.
316
- def clear_reloadable_connections!
520
+ # Disconnects all connections in the pool, and clears the pool.
521
+ #
522
+ # The pool first tries to gain ownership of all connections. If unable to
523
+ # do so within a timeout interval (default duration is
524
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool is forcefully
525
+ # disconnected without any regard for other connection owning threads.
526
+ def disconnect!
527
+ disconnect(false)
528
+ end
529
+
530
+ # Discards all connections in the pool (even if they're currently
531
+ # leased!), along with the pool itself. Any further interaction with the
532
+ # pool (except #spec and #schema_cache) is undefined.
533
+ #
534
+ # See AbstractAdapter#discard!
535
+ def discard! # :nodoc:
317
536
  synchronize do
318
- @reserved_connections.clear
537
+ return if @connections.nil? # already discarded
319
538
  @connections.each do |conn|
320
- checkin conn
321
- conn.disconnect! if conn.requires_reloading?
322
- end
323
- @connections.delete_if do |conn|
324
- conn.requires_reloading?
539
+ conn.discard!
325
540
  end
326
- @available.clear
327
- @connections.each do |conn|
328
- @available.add conn
541
+ @connections = @available = @thread_cached_conns = nil
542
+ end
543
+ end
544
+
545
+ # Clears the cache which maps classes and re-connects connections that
546
+ # require reloading.
547
+ #
548
+ # Raises:
549
+ # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
550
+ # connections in the pool within a timeout interval (default duration is
551
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
552
+ def clear_reloadable_connections(raise_on_acquisition_timeout = true)
553
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
554
+ synchronize do
555
+ @connections.each do |conn|
556
+ if conn.in_use?
557
+ conn.steal!
558
+ checkin conn
559
+ end
560
+ conn.disconnect! if conn.requires_reloading?
561
+ end
562
+ @connections.delete_if(&:requires_reloading?)
563
+ @available.clear
329
564
  end
330
565
  end
331
566
  end
332
567
 
568
+ # Clears the cache which maps classes and re-connects connections that
569
+ # require reloading.
570
+ #
571
+ # The pool first tries to gain ownership of all connections. If unable to
572
+ # do so within a timeout interval (default duration is
573
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool forcefully
574
+ # clears the cache and reloads connections without any regard for other
575
+ # connection owning threads.
576
+ def clear_reloadable_connections!
577
+ clear_reloadable_connections(false)
578
+ end
579
+
333
580
  # Check-out a database connection from the pool, indicating that you want
334
581
  # to use it. You should call #checkin when you no longer need this.
335
582
  #
@@ -343,129 +590,363 @@ module ActiveRecord
343
590
  # Returns: an AbstractAdapter object.
344
591
  #
345
592
  # Raises:
346
- # - ConnectionTimeoutError: no connection can be obtained from the pool.
347
- def checkout
348
- synchronize do
349
- conn = acquire_connection
350
- conn.lease
351
- checkout_and_verify(conn)
352
- end
593
+ # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
594
+ def checkout(checkout_timeout = @checkout_timeout)
595
+ checkout_and_verify(acquire_connection(checkout_timeout))
353
596
  end
354
597
 
355
598
  # Check-in a database connection back into the pool, indicating that you
356
599
  # no longer need this connection.
357
600
  #
358
601
  # +conn+: an AbstractAdapter object, which was obtained by earlier by
359
- # calling +checkout+ on this pool.
602
+ # calling #checkout on this pool.
360
603
  def checkin(conn)
361
- synchronize do
362
- owner = conn.owner
363
-
364
- conn._run_checkin_callbacks do
365
- conn.expire
366
- end
604
+ conn.lock.synchronize do
605
+ synchronize do
606
+ remove_connection_from_thread_cache conn
367
607
 
368
- release conn, owner
608
+ conn._run_checkin_callbacks do
609
+ conn.expire
610
+ end
369
611
 
370
- @available.add conn
612
+ @available.add conn
613
+ end
371
614
  end
372
615
  end
373
616
 
374
- # Remove a connection from the connection pool. The connection will
617
+ # Remove a connection from the connection pool. The connection will
375
618
  # remain open and active but will no longer be managed by this pool.
376
619
  def remove(conn)
620
+ needs_new_connection = false
621
+
377
622
  synchronize do
623
+ remove_connection_from_thread_cache conn
624
+
378
625
  @connections.delete conn
379
626
  @available.delete conn
380
627
 
381
- release conn, conn.owner
382
-
383
- @available.add checkout_new_connection if @available.any_waiting?
628
+ # @available.any_waiting? => true means that prior to removing this
629
+ # conn, the pool was at its max size (@connections.size == @size).
630
+ # This would mean that any threads stuck waiting in the queue wouldn't
631
+ # know they could checkout_new_connection, so let's do it for them.
632
+ # Because condition-wait loop is encapsulated in the Queue class
633
+ # (that in turn is oblivious to ConnectionPool implementation), threads
634
+ # that are "stuck" there are helpless. They have no way of creating
635
+ # new connections and are completely reliant on us feeding available
636
+ # connections into the Queue.
637
+ needs_new_connection = @available.any_waiting?
384
638
  end
639
+
640
+ # This is intentionally done outside of the synchronized section as we
641
+ # would like not to hold the main mutex while checking out new connections.
642
+ # Thus there is some chance that needs_new_connection information is now
643
+ # stale, we can live with that (bulk_make_new_connections will make
644
+ # sure not to exceed the pool's @size limit).
645
+ bulk_make_new_connections(1) if needs_new_connection
385
646
  end
386
647
 
387
- # Recover lost connections for the pool. A lost connection can occur if
648
+ # Recover lost connections for the pool. A lost connection can occur if
388
649
  # a programmer forgets to checkin a connection at the end of a thread
389
650
  # or a thread dies unexpectedly.
390
651
  def reap
391
652
  stale_connections = synchronize do
653
+ return unless @connections
392
654
  @connections.select do |conn|
393
655
  conn.in_use? && !conn.owner.alive?
656
+ end.each do |conn|
657
+ conn.steal!
394
658
  end
395
659
  end
396
660
 
397
661
  stale_connections.each do |conn|
398
- synchronize do
399
- if conn.active?
400
- conn.reset!
401
- checkin conn
402
- else
403
- remove conn
404
- end
662
+ if conn.active?
663
+ conn.reset!
664
+ checkin conn
665
+ else
666
+ remove conn
405
667
  end
406
668
  end
407
669
  end
408
670
 
409
- private
671
+ # Disconnect all connections that have been idle for at least
672
+ # +minimum_idle+ seconds. Connections currently checked out, or that were
673
+ # checked in less than +minimum_idle+ seconds ago, are unaffected.
674
+ def flush(minimum_idle = @idle_timeout)
675
+ return if minimum_idle.nil?
410
676
 
411
- # Acquire a connection by one of 1) immediately removing one
412
- # from the queue of available connections, 2) creating a new
413
- # connection if the pool is not at capacity, 3) waiting on the
414
- # queue for a connection to become available.
415
- #
416
- # Raises:
417
- # - ConnectionTimeoutError if a connection could not be acquired
418
- def acquire_connection
419
- if conn = @available.poll
420
- conn
421
- elsif @connections.size < @size
422
- checkout_new_connection
423
- else
424
- reap
425
- @available.poll(@checkout_timeout)
426
- end
427
- end
677
+ idle_connections = synchronize do
678
+ return unless @connections
679
+ @connections.select do |conn|
680
+ !conn.in_use? && conn.seconds_idle >= minimum_idle
681
+ end.each do |conn|
682
+ conn.lease
428
683
 
429
- def release(conn, owner)
430
- thread_id = owner.object_id
684
+ @available.delete conn
685
+ @connections.delete conn
686
+ end
687
+ end
431
688
 
432
- if @reserved_connections[thread_id] == conn
433
- @reserved_connections.delete thread_id
689
+ idle_connections.each do |conn|
690
+ conn.disconnect!
434
691
  end
435
692
  end
436
693
 
437
- def new_connection
438
- Base.send(spec.adapter_method, spec.config)
694
+ # Disconnect all currently idle connections. Connections currently checked
695
+ # out are unaffected.
696
+ def flush!
697
+ reap
698
+ flush(-1)
439
699
  end
440
700
 
441
- def current_connection_id #:nodoc:
442
- Base.connection_id ||= Thread.current.object_id
701
+ def num_waiting_in_queue # :nodoc:
702
+ @available.num_waiting
443
703
  end
444
704
 
445
- def checkout_new_connection
446
- raise ConnectionNotEstablished unless @automatic_reconnect
447
-
448
- c = new_connection
449
- c.pool = self
450
- @connections << c
451
- c
705
+ # Return connection pool's usage statistic
706
+ # Example:
707
+ #
708
+ # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
709
+ def stat
710
+ synchronize do
711
+ {
712
+ size: size,
713
+ connections: @connections.size,
714
+ busy: @connections.count { |c| c.in_use? && c.owner.alive? },
715
+ dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
716
+ idle: @connections.count { |c| !c.in_use? },
717
+ waiting: num_waiting_in_queue,
718
+ checkout_timeout: checkout_timeout
719
+ }
720
+ end
452
721
  end
453
722
 
454
- def checkout_and_verify(c)
455
- c._run_checkout_callbacks do
456
- c.verify!
723
+ private
724
+ #--
725
+ # this is unfortunately not concurrent
726
+ def bulk_make_new_connections(num_new_conns_needed)
727
+ num_new_conns_needed.times do
728
+ # try_to_checkout_new_connection will not exceed pool's @size limit
729
+ if new_conn = try_to_checkout_new_connection
730
+ # make the new_conn available to the starving threads stuck @available Queue
731
+ checkin(new_conn)
732
+ end
733
+ end
734
+ end
735
+
736
+ #--
737
+ # From the discussion on GitHub:
738
+ # https://github.com/rails/rails/pull/14938#commitcomment-6601951
739
+ # This hook-in method allows for easier monkey-patching fixes needed by
740
+ # JRuby users that use Fibers.
741
+ def connection_cache_key(thread)
742
+ thread
743
+ end
744
+
745
+ def current_thread
746
+ @lock_thread || Thread.current
747
+ end
748
+
749
+ # Take control of all existing connections so a "group" action such as
750
+ # reload/disconnect can be performed safely. It is no longer enough to
751
+ # wrap it in +synchronize+ because some pool's actions are allowed
752
+ # to be performed outside of the main +synchronize+ block.
753
+ def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
754
+ with_new_connections_blocked do
755
+ attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
756
+ yield
757
+ end
758
+ end
759
+
760
+ def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
761
+ collected_conns = synchronize do
762
+ # account for our own connections
763
+ @connections.select { |conn| conn.owner == Thread.current }
764
+ end
765
+
766
+ newly_checked_out = []
767
+ timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
768
+
769
+ @available.with_a_bias_for(Thread.current) do
770
+ loop do
771
+ synchronize do
772
+ return if collected_conns.size == @connections.size && @now_connecting == 0
773
+ remaining_timeout = timeout_time - Concurrent.monotonic_time
774
+ remaining_timeout = 0 if remaining_timeout < 0
775
+ conn = checkout_for_exclusive_access(remaining_timeout)
776
+ collected_conns << conn
777
+ newly_checked_out << conn
778
+ end
779
+ end
780
+ end
781
+ rescue ExclusiveConnectionTimeoutError
782
+ # <tt>raise_on_acquisition_timeout == false</tt> means we are directed to ignore any
783
+ # timeouts and are expected to just give up: we've obtained as many connections
784
+ # as possible, note that in a case like that we don't return any of the
785
+ # +newly_checked_out+ connections.
786
+
787
+ if raise_on_acquisition_timeout
788
+ release_newly_checked_out = true
789
+ raise
790
+ end
791
+ rescue Exception # if something else went wrong
792
+ # this can't be a "naked" rescue, because we have should return conns
793
+ # even for non-StandardErrors
794
+ release_newly_checked_out = true
795
+ raise
796
+ ensure
797
+ if release_newly_checked_out && newly_checked_out
798
+ # releasing only those conns that were checked out in this method, conns
799
+ # checked outside this method (before it was called) are not for us to release
800
+ newly_checked_out.each { |conn| checkin(conn) }
801
+ end
802
+ end
803
+
804
+ #--
805
+ # Must be called in a synchronize block.
806
+ def checkout_for_exclusive_access(checkout_timeout)
807
+ checkout(checkout_timeout)
808
+ rescue ConnectionTimeoutError
809
+ # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
810
+ # rescue block, because doing so would put it outside of synchronize section, without
811
+ # being in a critical section thread_report might become inaccurate
812
+ msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
813
+
814
+ thread_report = []
815
+ @connections.each do |conn|
816
+ unless conn.owner == Thread.current
817
+ thread_report << "#{conn} is owned by #{conn.owner}"
818
+ end
819
+ end
820
+
821
+ msg << " (#{thread_report.join(', ')})" if thread_report.any?
822
+
823
+ raise ExclusiveConnectionTimeoutError, msg
824
+ end
825
+
826
+ def with_new_connections_blocked
827
+ synchronize do
828
+ @threads_blocking_new_connections += 1
829
+ end
830
+
831
+ yield
832
+ ensure
833
+ num_new_conns_required = 0
834
+
835
+ synchronize do
836
+ @threads_blocking_new_connections -= 1
837
+
838
+ if @threads_blocking_new_connections.zero?
839
+ @available.clear
840
+
841
+ num_new_conns_required = num_waiting_in_queue
842
+
843
+ @connections.each do |conn|
844
+ next if conn.in_use?
845
+
846
+ @available.add conn
847
+ num_new_conns_required -= 1
848
+ end
849
+ end
850
+ end
851
+
852
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
853
+ end
854
+
855
+ # Acquire a connection by one of 1) immediately removing one
856
+ # from the queue of available connections, 2) creating a new
857
+ # connection if the pool is not at capacity, 3) waiting on the
858
+ # queue for a connection to become available.
859
+ #
860
+ # Raises:
861
+ # - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
862
+ #
863
+ #--
864
+ # Implementation detail: the connection returned by +acquire_connection+
865
+ # will already be "+connection.lease+ -ed" to the current thread.
866
+ def acquire_connection(checkout_timeout)
867
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
868
+ # +conn.lease+ the returned connection (and to do this in a +synchronized+
869
+ # section). This is not the cleanest implementation, as ideally we would
870
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
871
+ # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
872
+ # of the said methods and avoid an additional +synchronize+ overhead.
873
+ if conn = @available.poll || try_to_checkout_new_connection
874
+ conn
875
+ else
876
+ reap
877
+ @available.poll(checkout_timeout)
878
+ end
879
+ end
880
+
881
+ #--
882
+ # if owner_thread param is omitted, this must be called in synchronize block
883
+ def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
884
+ @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
885
+ end
886
+ alias_method :release, :remove_connection_from_thread_cache
887
+
888
+ def new_connection
889
+ Base.send(spec.adapter_method, spec.config).tap do |conn|
890
+ conn.check_version
891
+ end
892
+ end
893
+
894
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
895
+ # to the DB is done outside main synchronized section.
896
+ #--
897
+ # Implementation constraint: a newly established connection returned by this
898
+ # method must be in the +.leased+ state.
899
+ def try_to_checkout_new_connection
900
+ # first in synchronized section check if establishing new conns is allowed
901
+ # and increment @now_connecting, to prevent overstepping this pool's @size
902
+ # constraint
903
+ do_checkout = synchronize do
904
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
905
+ @now_connecting += 1
906
+ end
907
+ end
908
+ if do_checkout
909
+ begin
910
+ # if successfully incremented @now_connecting establish new connection
911
+ # outside of synchronized section
912
+ conn = checkout_new_connection
913
+ ensure
914
+ synchronize do
915
+ if conn
916
+ adopt_connection(conn)
917
+ # returned conn needs to be already leased
918
+ conn.lease
919
+ end
920
+ @now_connecting -= 1
921
+ end
922
+ end
923
+ end
924
+ end
925
+
926
+ def adopt_connection(conn)
927
+ conn.pool = self
928
+ @connections << conn
929
+ end
930
+
931
+ def checkout_new_connection
932
+ raise ConnectionNotEstablished unless @automatic_reconnect
933
+ new_connection
934
+ end
935
+
936
+ def checkout_and_verify(c)
937
+ c._run_checkout_callbacks do
938
+ c.verify!
939
+ end
940
+ c
941
+ rescue
942
+ remove c
943
+ c.disconnect!
944
+ raise
457
945
  end
458
- c
459
- rescue
460
- remove c
461
- c.disconnect!
462
- raise
463
- end
464
946
  end
465
947
 
466
948
  # ConnectionHandler is a collection of ConnectionPool objects. It is used
467
- # for keeping separate connection pools for Active Record models that connect
468
- # to different databases.
949
+ # for keeping separate connection pools that connect to different databases.
469
950
  #
470
951
  # For example, suppose that you have 5 models, with the following hierarchy:
471
952
  #
@@ -476,7 +957,7 @@ module ActiveRecord
476
957
  # end
477
958
  #
478
959
  # class Book < ActiveRecord::Base
479
- # establish_connection "library_db"
960
+ # establish_connection :library_db
480
961
  # end
481
962
  #
482
963
  # class ScaryBook < Book
@@ -507,36 +988,87 @@ module ActiveRecord
507
988
  # ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
508
989
  # All Active Record models use this handler to determine the connection pool that they
509
990
  # should use.
991
+ #
992
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
993
+ # about the model. The model needs to pass a specification name to the handler,
994
+ # in order to look up the correct connection pool.
510
995
  class ConnectionHandler
511
- def initialize
512
- # These caches are keyed by klass.name, NOT klass. Keying them by klass
513
- # alone would lead to memory leaks in development mode as all previous
514
- # instances of the class would stay in memory.
515
- @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
516
- h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
996
+ def self.create_owner_to_pool # :nodoc:
997
+ Concurrent::Map.new(initial_capacity: 2) do |h, k|
998
+ # Discard the parent's connection pools immediately; we have no need
999
+ # of them
1000
+ discard_unowned_pools(h)
1001
+
1002
+ h[k] = Concurrent::Map.new(initial_capacity: 2)
517
1003
  end
518
- @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
519
- h[k] = ThreadSafe::Cache.new
1004
+ end
1005
+
1006
+ def self.unowned_pool_finalizer(pid_map) # :nodoc:
1007
+ lambda do |_|
1008
+ discard_unowned_pools(pid_map)
1009
+ end
1010
+ end
1011
+
1012
+ def self.discard_unowned_pools(pid_map) # :nodoc:
1013
+ pid_map.each do |pid, pools|
1014
+ pools.values.compact.each(&:discard!) unless pid == Process.pid
520
1015
  end
521
1016
  end
522
1017
 
1018
+ def initialize
1019
+ # These caches are keyed by spec.name (ConnectionSpecification#name).
1020
+ @owner_to_pool = ConnectionHandler.create_owner_to_pool
1021
+
1022
+ # Backup finalizer: if the forked child never needed a pool, the above
1023
+ # early discard has not occurred
1024
+ ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
1025
+ end
1026
+
1027
+ def prevent_writes # :nodoc:
1028
+ Thread.current[:prevent_writes]
1029
+ end
1030
+
1031
+ def prevent_writes=(prevent_writes) # :nodoc:
1032
+ Thread.current[:prevent_writes] = prevent_writes
1033
+ end
1034
+
1035
+ # Prevent writing to the database regardless of role.
1036
+ #
1037
+ # In some cases you may want to prevent writes to the database
1038
+ # even if you are on a database that can write. `while_preventing_writes`
1039
+ # will prevent writes to the database for the duration of the block.
1040
+ def while_preventing_writes(enabled = true)
1041
+ original, self.prevent_writes = self.prevent_writes, enabled
1042
+ yield
1043
+ ensure
1044
+ self.prevent_writes = original
1045
+ end
1046
+
523
1047
  def connection_pool_list
524
1048
  owner_to_pool.values.compact
525
1049
  end
1050
+ alias :connection_pools :connection_pool_list
526
1051
 
527
- def connection_pools
528
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
529
- In the next release, this will return the same as `#connection_pool_list`.
530
- (An array of pools, rather than a hash mapping specs to pools.)
531
- MSG
1052
+ def establish_connection(config)
1053
+ resolver = ConnectionSpecification::Resolver.new(Base.configurations)
1054
+ spec = resolver.spec(config)
532
1055
 
533
- Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
534
- end
1056
+ remove_connection(spec.name)
1057
+
1058
+ message_bus = ActiveSupport::Notifications.instrumenter
1059
+ payload = {
1060
+ connection_id: object_id
1061
+ }
1062
+ if spec
1063
+ payload[:spec_name] = spec.name
1064
+ payload[:config] = spec.config
1065
+ end
535
1066
 
536
- def establish_connection(owner, spec)
537
- @class_to_pool.clear
538
- raise RuntimeError, "Anonymous class is not allowed." unless owner.name
539
- owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
1067
+ message_bus.instrument("!connection.active_record", payload) do
1068
+ owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
1069
+ end
1070
+
1071
+ owner_to_pool[spec.name]
540
1072
  end
541
1073
 
542
1074
  # Returns true if there are any active connections among the connection
@@ -553,6 +1085,8 @@ module ActiveRecord
553
1085
  end
554
1086
 
555
1087
  # Clears the cache which maps classes.
1088
+ #
1089
+ # See ConnectionPool#clear_reloadable_connections! for details.
556
1090
  def clear_reloadable_connections!
557
1091
  connection_pool_list.each(&:clear_reloadable_connections!)
558
1092
  end
@@ -561,105 +1095,81 @@ module ActiveRecord
561
1095
  connection_pool_list.each(&:disconnect!)
562
1096
  end
563
1097
 
1098
+ # Disconnects all currently idle connections.
1099
+ #
1100
+ # See ConnectionPool#flush! for details.
1101
+ def flush_idle_connections!
1102
+ connection_pool_list.each(&:flush!)
1103
+ end
1104
+
564
1105
  # Locate the connection of the nearest super class. This can be an
565
1106
  # active or defined connection: if it is the latter, it will be
566
1107
  # opened and set as the active connection for the class it was defined
567
1108
  # for (not necessarily the current class).
568
- def retrieve_connection(klass) #:nodoc:
569
- pool = retrieve_connection_pool(klass)
570
- raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
571
- conn = pool.connection
572
- raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
573
- conn
1109
+ def retrieve_connection(spec_name) #:nodoc:
1110
+ pool = retrieve_connection_pool(spec_name)
1111
+
1112
+ unless pool
1113
+ # multiple database application
1114
+ if ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1115
+ raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
1116
+ else
1117
+ raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found."
1118
+ end
1119
+ end
1120
+
1121
+ pool.connection
574
1122
  end
575
1123
 
576
1124
  # Returns true if a connection that's accessible to this class has
577
1125
  # already been opened.
578
- def connected?(klass)
579
- conn = retrieve_connection_pool(klass)
580
- conn && conn.connected?
1126
+ def connected?(spec_name)
1127
+ pool = retrieve_connection_pool(spec_name)
1128
+ pool && pool.connected?
581
1129
  end
582
1130
 
583
1131
  # Remove the connection for this class. This will close the active
584
1132
  # connection and the defined connection (if they exist). The result
585
- # can be used as an argument for establish_connection, for easily
1133
+ # can be used as an argument for #establish_connection, for easily
586
1134
  # re-establishing the connection.
587
- def remove_connection(owner)
588
- if pool = owner_to_pool.delete(owner.name)
589
- @class_to_pool.clear
1135
+ def remove_connection(spec_name)
1136
+ if pool = owner_to_pool.delete(spec_name)
590
1137
  pool.automatic_reconnect = false
591
1138
  pool.disconnect!
592
1139
  pool.spec.config
593
1140
  end
594
1141
  end
595
1142
 
596
- # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
1143
+ # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool.
597
1144
  # This makes retrieving the connection pool O(1) once the process is warm.
598
1145
  # When a connection is established or removed, we invalidate the cache.
599
- #
600
- # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
601
- # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
602
- # #fetch is significantly slower than #[]. So in the nil case, no caching will
603
- # take place, but that's ok since the nil case is not the common one that we wish
604
- # to optimise for.
605
- def retrieve_connection_pool(klass)
606
- class_to_pool[klass.name] ||= begin
607
- until pool = pool_for(klass)
608
- klass = klass.superclass
609
- break unless klass <= Base
610
- end
611
-
612
- class_to_pool[klass.name] = pool
613
- end
614
- end
615
-
616
- private
617
-
618
- def owner_to_pool
619
- @owner_to_pool[Process.pid]
620
- end
621
-
622
- def class_to_pool
623
- @class_to_pool[Process.pid]
624
- end
625
-
626
- def pool_for(owner)
627
- owner_to_pool.fetch(owner.name) {
628
- if ancestor_pool = pool_from_any_process_for(owner)
1146
+ def retrieve_connection_pool(spec_name)
1147
+ owner_to_pool.fetch(spec_name) do
1148
+ # Check if a connection was previously established in an ancestor process,
1149
+ # which may have been forked.
1150
+ if ancestor_pool = pool_from_any_process_for(spec_name)
629
1151
  # A connection was established in an ancestor process that must have
630
1152
  # subsequently forked. We can't reuse the connection, but we can copy
631
1153
  # the specification and establish a new connection with it.
632
- establish_connection owner, ancestor_pool.spec
1154
+ establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
1155
+ pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
1156
+ end
633
1157
  else
634
- owner_to_pool[owner.name] = nil
1158
+ owner_to_pool[spec_name] = nil
635
1159
  end
636
- }
637
- end
638
-
639
- def pool_from_any_process_for(owner)
640
- owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[owner.name] }
641
- owner_to_pool && owner_to_pool[owner.name]
642
- end
643
- end
644
-
645
- class ConnectionManagement
646
- def initialize(app)
647
- @app = app
1160
+ end
648
1161
  end
649
1162
 
650
- def call(env)
651
- testing = env['rack.test']
1163
+ private
652
1164
 
653
- response = @app.call(env)
654
- response[2] = ::Rack::BodyProxy.new(response[2]) do
655
- ActiveRecord::Base.clear_active_connections! unless testing
1165
+ def owner_to_pool
1166
+ @owner_to_pool[Process.pid]
656
1167
  end
657
1168
 
658
- response
659
- rescue Exception
660
- ActiveRecord::Base.clear_active_connections! unless testing
661
- raise
662
- end
1169
+ def pool_from_any_process_for(spec_name)
1170
+ owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
1171
+ owner_to_pool && owner_to_pool[spec_name]
1172
+ end
663
1173
  end
664
1174
  end
665
1175
  end