activerecord 4.2.6 → 5.0.0

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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1307 -1105
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +3 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +11 -9
  11. data/lib/active_record/associations/association_scope.rb +73 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +50 -31
  21. data/lib/active_record/associations/collection_proxy.rb +69 -29
  22. data/lib/active_record/associations/foreign_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_association.rb +20 -71
  24. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  25. data/lib/active_record/associations/has_one_association.rb +12 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +29 -19
  28. data/lib/active_record/associations/preloader/association.rb +46 -52
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +14 -4
  34. data/lib/active_record/associations/singular_association.rb +7 -1
  35. data/lib/active_record/associations/through_association.rb +11 -3
  36. data/lib/active_record/associations.rb +317 -209
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -80
  54. data/lib/active_record/autosave_association.rb +49 -16
  55. data/lib/active_record/base.rb +32 -23
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -140
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -176
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  100. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  105. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  106. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  107. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  108. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  110. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  111. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  112. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  113. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  114. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  115. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  117. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +148 -203
  118. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  119. data/lib/active_record/connection_handling.rb +37 -14
  120. data/lib/active_record/core.rb +89 -107
  121. data/lib/active_record/counter_cache.rb +13 -24
  122. data/lib/active_record/dynamic_matchers.rb +1 -20
  123. data/lib/active_record/enum.rb +113 -76
  124. data/lib/active_record/errors.rb +87 -48
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +26 -5
  128. data/lib/active_record/fixtures.rb +76 -40
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +32 -40
  131. data/lib/active_record/integration.rb +4 -4
  132. data/lib/active_record/internal_metadata.rb +56 -0
  133. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  134. data/lib/active_record/locale/en.yml +3 -2
  135. data/lib/active_record/locking/optimistic.rb +15 -15
  136. data/lib/active_record/locking/pessimistic.rb +1 -1
  137. data/lib/active_record/log_subscriber.rb +43 -21
  138. data/lib/active_record/migration/command_recorder.rb +59 -18
  139. data/lib/active_record/migration/compatibility.rb +126 -0
  140. data/lib/active_record/migration.rb +364 -109
  141. data/lib/active_record/model_schema.rb +128 -38
  142. data/lib/active_record/nested_attributes.rb +58 -29
  143. data/lib/active_record/null_relation.rb +16 -8
  144. data/lib/active_record/persistence.rb +121 -80
  145. data/lib/active_record/query_cache.rb +15 -18
  146. data/lib/active_record/querying.rb +10 -9
  147. data/lib/active_record/railtie.rb +27 -18
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +58 -45
  150. data/lib/active_record/readonly_attributes.rb +1 -1
  151. data/lib/active_record/reflection.rb +282 -115
  152. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  153. data/lib/active_record/relation/batches.rb +139 -34
  154. data/lib/active_record/relation/calculations.rb +80 -102
  155. data/lib/active_record/relation/delegation.rb +7 -20
  156. data/lib/active_record/relation/finder_methods.rb +163 -81
  157. data/lib/active_record/relation/from_clause.rb +32 -0
  158. data/lib/active_record/relation/merger.rb +16 -42
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -15
  160. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  163. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  166. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  167. data/lib/active_record/relation/predicate_builder.rb +120 -107
  168. data/lib/active_record/relation/query_attribute.rb +19 -0
  169. data/lib/active_record/relation/query_methods.rb +308 -244
  170. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  171. data/lib/active_record/relation/spawn_methods.rb +4 -7
  172. data/lib/active_record/relation/where_clause.rb +174 -0
  173. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  174. data/lib/active_record/relation.rb +176 -116
  175. data/lib/active_record/result.rb +4 -3
  176. data/lib/active_record/runtime_registry.rb +1 -1
  177. data/lib/active_record/sanitization.rb +95 -66
  178. data/lib/active_record/schema.rb +26 -22
  179. data/lib/active_record/schema_dumper.rb +62 -38
  180. data/lib/active_record/schema_migration.rb +11 -17
  181. data/lib/active_record/scoping/default.rb +23 -9
  182. data/lib/active_record/scoping/named.rb +49 -28
  183. data/lib/active_record/scoping.rb +32 -15
  184. data/lib/active_record/secure_token.rb +38 -0
  185. data/lib/active_record/serialization.rb +2 -4
  186. data/lib/active_record/statement_cache.rb +16 -14
  187. data/lib/active_record/store.rb +8 -3
  188. data/lib/active_record/suppressor.rb +58 -0
  189. data/lib/active_record/table_metadata.rb +68 -0
  190. data/lib/active_record/tasks/database_tasks.rb +58 -41
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  193. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  194. data/lib/active_record/timestamp.rb +20 -9
  195. data/lib/active_record/touch_later.rb +58 -0
  196. data/lib/active_record/transactions.rb +138 -56
  197. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  198. data/lib/active_record/type/date.rb +2 -41
  199. data/lib/active_record/type/date_time.rb +2 -49
  200. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  201. data/lib/active_record/type/internal/timezone.rb +15 -0
  202. data/lib/active_record/type/serialized.rb +15 -14
  203. data/lib/active_record/type/time.rb +10 -16
  204. data/lib/active_record/type/type_map.rb +4 -4
  205. data/lib/active_record/type.rb +66 -17
  206. data/lib/active_record/type_caster/connection.rb +29 -0
  207. data/lib/active_record/type_caster/map.rb +19 -0
  208. data/lib/active_record/type_caster.rb +7 -0
  209. data/lib/active_record/validations/absence.rb +23 -0
  210. data/lib/active_record/validations/associated.rb +10 -3
  211. data/lib/active_record/validations/length.rb +24 -0
  212. data/lib/active_record/validations/presence.rb +11 -12
  213. data/lib/active_record/validations/uniqueness.rb +30 -29
  214. data/lib/active_record/validations.rb +33 -32
  215. data/lib/active_record.rb +7 -2
  216. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  217. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  218. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  219. data/lib/rails/generators/active_record/migration.rb +7 -0
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +58 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -50
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -105
@@ -1,8 +1,6 @@
1
1
  require 'thread'
2
- require 'thread_safe'
2
+ require 'concurrent/map'
3
3
  require 'monitor'
4
- require 'set'
5
- require 'active_support/core_ext/string/filters'
6
4
 
7
5
  module ActiveRecord
8
6
  # Raised when a connection could not be obtained within the connection
@@ -11,6 +9,13 @@ module ActiveRecord
11
9
  class ConnectionTimeoutError < ConnectionNotEstablished
12
10
  end
13
11
 
12
+ # Raised when a pool was unable to get ahold of all its connections
13
+ # to perform a "group" action such as
14
+ # {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
15
+ # or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
16
+ class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
17
+ end
18
+
14
19
  module ConnectionAdapters
15
20
  # Connection pool base class for managing Active Record database
16
21
  # connections.
@@ -33,17 +38,18 @@ module ActiveRecord
33
38
  # Connections can be obtained and used from a connection pool in several
34
39
  # ways:
35
40
  #
36
- # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
41
+ # 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection]
42
+ # as with Active Record 2.1 and
37
43
  # earlier (pre-connection-pooling). Eventually, when you're done with
38
44
  # 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
45
+ # {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
46
+ # This will be the default behavior for Active Record when used in conjunction with
41
47
  # Action Pack's request handling cycle.
42
48
  # 2. Manually check out a connection from the pool with
43
- # ActiveRecord::Base.connection_pool.checkout. You are responsible for
49
+ # {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
44
50
  # 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
51
+ # {ActiveRecord::Base.connection_pool.checkin(connection)}[rdoc-ref:#checkin].
52
+ # 3. Use {ActiveRecord::Base.connection_pool.with_connection(&block)}[rdoc-ref:#with_connection], which
47
53
  # obtains a connection, yields it as the sole argument to the block,
48
54
  # and returns it to the pool after the block completes.
49
55
  #
@@ -64,6 +70,15 @@ module ActiveRecord
64
70
  # connection at the end of a thread or a thread dies unexpectedly.
65
71
  # Regardless of this setting, the Reaper will be invoked before every
66
72
  # blocking wait. (Default nil, which means don't schedule the Reaper).
73
+ #
74
+ #--
75
+ # Synchronization policy:
76
+ # * all public methods can be called outside +synchronize+
77
+ # * access to these i-vars needs to be in +synchronize+:
78
+ # * @connections
79
+ # * @now_connecting
80
+ # * private methods that require being called in a +synchronize+ blocks
81
+ # are now explicitly documented
67
82
  class ConnectionPool
68
83
  # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
69
84
  # with which it shares a Monitor. But could be a generic Queue.
@@ -122,25 +137,23 @@ module ActiveRecord
122
137
  # greater than the number of threads currently waiting (that
123
138
  # is, don't jump ahead in line). Otherwise, return nil.
124
139
  #
125
- # If +timeout+ is given, block if it there is no element
140
+ # If +timeout+ is given, block if there is no element
126
141
  # available, waiting up to +timeout+ seconds for an element to
127
142
  # become available.
128
143
  #
129
144
  # Raises:
130
- # - ConnectionTimeoutError if +timeout+ is given and no element
131
- # becomes available after +timeout+ seconds,
145
+ # - ActiveRecord::ConnectionTimeoutError if +timeout+ is given and no element
146
+ # becomes available within +timeout+ seconds,
132
147
  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
148
+ synchronize { internal_poll(timeout) }
140
149
  end
141
150
 
142
151
  private
143
152
 
153
+ def internal_poll(timeout)
154
+ no_wait_poll || (timeout && wait_poll(timeout))
155
+ end
156
+
144
157
  def synchronize(&block)
145
158
  @lock.synchronize(&block)
146
159
  end
@@ -151,7 +164,7 @@ module ActiveRecord
151
164
  end
152
165
 
153
166
  # A thread can remove an element from the queue without
154
- # waiting if an only if the number of currently available
167
+ # waiting if and only if the number of currently available
155
168
  # connections is strictly greater than the number of waiting
156
169
  # threads.
157
170
  def can_remove_no_wait?
@@ -184,7 +197,7 @@ module ActiveRecord
184
197
 
185
198
  elapsed = Time.now - t0
186
199
  if elapsed >= timeout
187
- msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
200
+ msg = 'could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use' %
188
201
  [timeout, elapsed]
189
202
  raise ConnectionTimeoutError, msg
190
203
  end
@@ -194,6 +207,80 @@ module ActiveRecord
194
207
  end
195
208
  end
196
209
 
210
+ # Adds the ability to turn a basic fair FIFO queue into one
211
+ # biased to some thread.
212
+ module BiasableQueue # :nodoc:
213
+ class BiasedConditionVariable # :nodoc:
214
+ # semantics of condition variables guarantee that +broadcast+, +broadcast_on_biased+,
215
+ # +signal+ and +wait+ methods are only called while holding a lock
216
+ def initialize(lock, other_cond, preferred_thread)
217
+ @real_cond = lock.new_cond
218
+ @other_cond = other_cond
219
+ @preferred_thread = preferred_thread
220
+ @num_waiting_on_real_cond = 0
221
+ end
222
+
223
+ def broadcast
224
+ broadcast_on_biased
225
+ @other_cond.broadcast
226
+ end
227
+
228
+ def broadcast_on_biased
229
+ @num_waiting_on_real_cond = 0
230
+ @real_cond.broadcast
231
+ end
232
+
233
+ def signal
234
+ if @num_waiting_on_real_cond > 0
235
+ @num_waiting_on_real_cond -= 1
236
+ @real_cond
237
+ else
238
+ @other_cond
239
+ end.signal
240
+ end
241
+
242
+ def wait(timeout)
243
+ if Thread.current == @preferred_thread
244
+ @num_waiting_on_real_cond += 1
245
+ @real_cond
246
+ else
247
+ @other_cond
248
+ end.wait(timeout)
249
+ end
250
+ end
251
+
252
+ def with_a_bias_for(thread)
253
+ previous_cond = nil
254
+ new_cond = nil
255
+ synchronize do
256
+ previous_cond = @cond
257
+ @cond = new_cond = BiasedConditionVariable.new(@lock, @cond, thread)
258
+ end
259
+ yield
260
+ ensure
261
+ synchronize do
262
+ @cond = previous_cond if previous_cond
263
+ new_cond.broadcast_on_biased if new_cond # wake up any remaining sleepers
264
+ end
265
+ end
266
+ end
267
+
268
+ # Connections must be leased while holding the main pool mutex. This is
269
+ # an internal subclass that also +.leases+ returned connections while
270
+ # still in queue's critical section (queue synchronizes with the same
271
+ # +@lock+ as the main pool) so that a returned connection is already
272
+ # leased and there is no need to re-enter synchronized block.
273
+ class ConnectionLeasingQueue < Queue # :nodoc:
274
+ include BiasableQueue
275
+
276
+ private
277
+ def internal_poll(timeout)
278
+ conn = super
279
+ conn.lease if conn
280
+ conn
281
+ end
282
+ end
283
+
197
284
  # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
198
285
  # A reaper instantiated with a nil frequency will never reap the
199
286
  # connection pool.
@@ -221,7 +308,7 @@ module ActiveRecord
221
308
 
222
309
  include MonitorMixin
223
310
 
224
- attr_accessor :automatic_reconnect, :checkout_timeout
311
+ attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
225
312
  attr_reader :spec, :connections, :size, :reaper
226
313
 
227
314
  # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
@@ -242,56 +329,75 @@ module ActiveRecord
242
329
  # default max pool size to 5
243
330
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
244
331
 
245
- # The cache of reserved connections mapped to threads
246
- @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
332
+ # The cache of threads mapped to reserved connections, the sole purpose
333
+ # of the cache is to speed-up +connection+ method, it is not the authoritative
334
+ # registry of which thread owns which connection, that is tracked by
335
+ # +connection.owner+ attr on each +connection+ instance.
336
+ # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
337
+ # then that +thread+ does indeed own that +conn+, however an absence of a such
338
+ # mapping does not mean that the +thread+ doesn't own the said connection, in
339
+ # that case +conn.owner+ attr should be consulted.
340
+ # Access and modification of +@thread_cached_conns+ does not require
341
+ # synchronization.
342
+ @thread_cached_conns = Concurrent::Map.new(:initial_capacity => @size)
247
343
 
248
344
  @connections = []
249
345
  @automatic_reconnect = true
250
346
 
251
- @available = Queue.new self
347
+ # Connection pool allows for concurrent (outside the main +synchronize+ section)
348
+ # establishment of new connections. This variable tracks the number of threads
349
+ # currently in the process of independently establishing connections to the DB.
350
+ @now_connecting = 0
351
+
352
+ # A boolean toggle that allows/disallows new connections.
353
+ @new_cons_enabled = true
354
+
355
+ @available = ConnectionLeasingQueue.new self
252
356
  end
253
357
 
254
358
  # Retrieve the connection associated with the current thread, or call
255
359
  # #checkout to obtain one if necessary.
256
360
  #
257
361
  # #connection can be called any number of times; the connection is
258
- # held in a hash keyed by the thread id.
362
+ # held in a cache keyed by a thread.
259
363
  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
364
+ @thread_cached_conns[connection_cache_key(Thread.current)] ||= checkout
265
365
  end
266
366
 
267
367
  # Is there an open connection that is being used for the current thread?
368
+ #
369
+ # This method only works for connections that have been obtained through
370
+ # #connection or #with_connection methods, connections obtained through
371
+ # #checkout will not be detected by #active_connection?
268
372
  def active_connection?
269
- synchronize do
270
- @reserved_connections.fetch(current_connection_id) {
271
- return false
272
- }.in_use?
273
- end
373
+ @thread_cached_conns[connection_cache_key(Thread.current)]
274
374
  end
275
375
 
276
376
  # Signal that the thread is finished with the current connection.
277
377
  # #release_connection releases the connection-thread association
278
378
  # 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
379
+ #
380
+ # This method only works for connections that have been obtained through
381
+ # #connection or #with_connection methods, connections obtained through
382
+ # #checkout will not be automatically released.
383
+ def release_connection(owner_thread = Thread.current)
384
+ if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
385
+ checkin conn
283
386
  end
284
387
  end
285
388
 
286
- # If a connection already exists yield it to the block. If no connection
389
+ # If a connection obtained through #connection or #with_connection methods
390
+ # already exists yield it to the block. If no such connection
287
391
  # exists checkout a connection, yield it to the block, and checkin the
288
392
  # connection when finished.
289
393
  def with_connection
290
- connection_id = current_connection_id
291
- fresh_connection = true unless active_connection?
292
- yield connection
394
+ unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
395
+ conn = connection
396
+ fresh_connection = true
397
+ end
398
+ yield conn
293
399
  ensure
294
- release_connection(connection_id) if fresh_connection
400
+ release_connection if fresh_connection
295
401
  end
296
402
 
297
403
  # Returns true if a connection has already been opened.
@@ -300,34 +406,81 @@ module ActiveRecord
300
406
  end
301
407
 
302
408
  # 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!
409
+ #
410
+ # Raises:
411
+ # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
412
+ # connections in the pool within a timeout interval (default duration is
413
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
414
+ def disconnect(raise_on_acquisition_timeout = true)
415
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
416
+ synchronize do
417
+ @connections.each do |conn|
418
+ checkin conn
419
+ conn.disconnect!
420
+ end
421
+ @connections = []
422
+ @available.clear
309
423
  end
310
- @connections = []
311
- @available.clear
312
424
  end
313
425
  end
314
426
 
315
- # Clears the cache which maps classes.
316
- def clear_reloadable_connections!
317
- synchronize do
318
- @reserved_connections.clear
319
- @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?
325
- end
326
- @available.clear
327
- @connections.each do |conn|
328
- @available.add conn
427
+ # Disconnects all connections in the pool, and clears the pool.
428
+ #
429
+ # The pool first tries to gain ownership of all connections, if unable to
430
+ # do so within a timeout interval (default duration is
431
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), the pool is forcefully
432
+ # disconnected without any regard for other connection owning threads.
433
+ def disconnect!
434
+ disconnect(false)
435
+ end
436
+
437
+ # Clears the cache which maps classes and re-connects connections that
438
+ # require reloading.
439
+ #
440
+ # Raises:
441
+ # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
442
+ # connections in the pool within a timeout interval (default duration is
443
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
444
+ def clear_reloadable_connections(raise_on_acquisition_timeout = true)
445
+ num_new_conns_required = 0
446
+
447
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
448
+ synchronize do
449
+ @connections.each do |conn|
450
+ checkin conn
451
+ conn.disconnect! if conn.requires_reloading?
452
+ end
453
+ @connections.delete_if(&:requires_reloading?)
454
+
455
+ @available.clear
456
+
457
+ if @connections.size < @size
458
+ # because of the pruning done by this method, we might be running
459
+ # low on connections, while threads stuck in queue are helpless
460
+ # (not being able to establish new connections for themselves),
461
+ # see also more detailed explanation in +remove+
462
+ num_new_conns_required = num_waiting_in_queue - @connections.size
463
+ end
464
+
465
+ @connections.each do |conn|
466
+ @available.add conn
467
+ end
329
468
  end
330
469
  end
470
+
471
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
472
+ end
473
+
474
+ # Clears the cache which maps classes and re-connects connections that
475
+ # require reloading.
476
+ #
477
+ # The pool first tries to gain ownership of all connections, if unable to
478
+ # do so within a timeout interval (default duration is
479
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), the pool forcefully
480
+ # clears the cache and reloads connections without any regard for other
481
+ # connection owning threads.
482
+ def clear_reloadable_connections!
483
+ clear_reloadable_connections(false)
331
484
  end
332
485
 
333
486
  # Check-out a database connection from the pool, indicating that you want
@@ -343,48 +496,60 @@ module ActiveRecord
343
496
  # Returns: an AbstractAdapter object.
344
497
  #
345
498
  # 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
499
+ # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
500
+ def checkout(checkout_timeout = @checkout_timeout)
501
+ checkout_and_verify(acquire_connection(checkout_timeout))
353
502
  end
354
503
 
355
504
  # Check-in a database connection back into the pool, indicating that you
356
505
  # no longer need this connection.
357
506
  #
358
507
  # +conn+: an AbstractAdapter object, which was obtained by earlier by
359
- # calling +checkout+ on this pool.
508
+ # calling #checkout on this pool.
360
509
  def checkin(conn)
361
510
  synchronize do
362
- owner = conn.owner
511
+ remove_connection_from_thread_cache conn
363
512
 
364
513
  conn._run_checkin_callbacks do
365
514
  conn.expire
366
515
  end
367
516
 
368
- release conn, owner
369
-
370
517
  @available.add conn
371
518
  end
372
519
  end
373
520
 
374
- # Remove a connection from the connection pool. The connection will
521
+ # Remove a connection from the connection pool. The connection will
375
522
  # remain open and active but will no longer be managed by this pool.
376
523
  def remove(conn)
524
+ needs_new_connection = false
525
+
377
526
  synchronize do
527
+ remove_connection_from_thread_cache conn
528
+
378
529
  @connections.delete conn
379
530
  @available.delete conn
380
531
 
381
- release conn, conn.owner
382
-
383
- @available.add checkout_new_connection if @available.any_waiting?
532
+ # @available.any_waiting? => true means that prior to removing this
533
+ # conn, the pool was at its max size (@connections.size == @size)
534
+ # this would mean that any threads stuck waiting in the queue wouldn't
535
+ # know they could checkout_new_connection, so let's do it for them.
536
+ # Because condition-wait loop is encapsulated in the Queue class
537
+ # (that in turn is oblivious to ConnectionPool implementation), threads
538
+ # that are "stuck" there are helpless, they have no way of creating
539
+ # new connections and are completely reliant on us feeding available
540
+ # connections into the Queue.
541
+ needs_new_connection = @available.any_waiting?
384
542
  end
543
+
544
+ # This is intentionally done outside of the synchronized section as we
545
+ # would like not to hold the main mutex while checking out new connections,
546
+ # thus there is some chance that needs_new_connection information is now
547
+ # stale, we can live with that (bulk_make_new_connections will make
548
+ # sure not to exceed the pool's @size limit).
549
+ bulk_make_new_connections(1) if needs_new_connection
385
550
  end
386
551
 
387
- # Recover lost connections for the pool. A lost connection can occur if
552
+ # Recover lost connections for the pool. A lost connection can occur if
388
553
  # a programmer forgets to checkin a connection at the end of a thread
389
554
  # or a thread dies unexpectedly.
390
555
  def reap
@@ -406,7 +571,118 @@ module ActiveRecord
406
571
  end
407
572
  end
408
573
 
574
+ def num_waiting_in_queue # :nodoc:
575
+ @available.num_waiting
576
+ end
577
+
409
578
  private
579
+ #--
580
+ # this is unfortunately not concurrent
581
+ def bulk_make_new_connections(num_new_conns_needed)
582
+ num_new_conns_needed.times do
583
+ # try_to_checkout_new_connection will not exceed pool's @size limit
584
+ if new_conn = try_to_checkout_new_connection
585
+ # make the new_conn available to the starving threads stuck @available Queue
586
+ checkin(new_conn)
587
+ end
588
+ end
589
+ end
590
+
591
+ #--
592
+ # From the discussion on GitHub:
593
+ # https://github.com/rails/rails/pull/14938#commitcomment-6601951
594
+ # This hook-in method allows for easier monkey-patching fixes needed by
595
+ # JRuby users that use Fibers.
596
+ def connection_cache_key(thread)
597
+ thread
598
+ end
599
+
600
+ # Take control of all existing connections so a "group" action such as
601
+ # reload/disconnect can be performed safely. It is no longer enough to
602
+ # wrap it in +synchronize+ because some pool's actions are allowed
603
+ # to be performed outside of the main +synchronize+ block.
604
+ def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
605
+ with_new_connections_blocked do
606
+ attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
607
+ yield
608
+ end
609
+ end
610
+
611
+ def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
612
+ collected_conns = synchronize do
613
+ # account for our own connections
614
+ @connections.select {|conn| conn.owner == Thread.current}
615
+ end
616
+
617
+ newly_checked_out = []
618
+ timeout_time = Time.now + (@checkout_timeout * 2)
619
+
620
+ @available.with_a_bias_for(Thread.current) do
621
+ while true
622
+ synchronize do
623
+ return if collected_conns.size == @connections.size && @now_connecting == 0
624
+ remaining_timeout = timeout_time - Time.now
625
+ remaining_timeout = 0 if remaining_timeout < 0
626
+ conn = checkout_for_exclusive_access(remaining_timeout)
627
+ collected_conns << conn
628
+ newly_checked_out << conn
629
+ end
630
+ end
631
+ end
632
+ rescue ExclusiveConnectionTimeoutError
633
+ # <tt>raise_on_acquisition_timeout == false</tt> means we are directed to ignore any
634
+ # timeouts and are expected to just give up: we've obtained as many connections
635
+ # as possible, note that in a case like that we don't return any of the
636
+ # +newly_checked_out+ connections.
637
+
638
+ if raise_on_acquisition_timeout
639
+ release_newly_checked_out = true
640
+ raise
641
+ end
642
+ rescue Exception # if something else went wrong
643
+ # this can't be a "naked" rescue, because we have should return conns
644
+ # even for non-StandardErrors
645
+ release_newly_checked_out = true
646
+ raise
647
+ ensure
648
+ if release_newly_checked_out && newly_checked_out
649
+ # releasing only those conns that were checked out in this method, conns
650
+ # checked outside this method (before it was called) are not for us to release
651
+ newly_checked_out.each {|conn| checkin(conn)}
652
+ end
653
+ end
654
+
655
+ #--
656
+ # Must be called in a synchronize block.
657
+ def checkout_for_exclusive_access(checkout_timeout)
658
+ checkout(checkout_timeout)
659
+ rescue ConnectionTimeoutError
660
+ # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
661
+ # rescue block, because doing so would put it outside of synchronize section, without
662
+ # being in a critical section thread_report might become inaccurate
663
+ msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
664
+
665
+ thread_report = []
666
+ @connections.each do |conn|
667
+ unless conn.owner == Thread.current
668
+ thread_report << "#{conn} is owned by #{conn.owner}"
669
+ end
670
+ end
671
+
672
+ msg << " (#{thread_report.join(', ')})" if thread_report.any?
673
+
674
+ raise ExclusiveConnectionTimeoutError, msg
675
+ end
676
+
677
+ def with_new_connections_blocked
678
+ previous_value = nil
679
+ synchronize do
680
+ previous_value, @new_cons_enabled = @new_cons_enabled, false
681
+ end
682
+ yield
683
+ ensure
684
+ synchronize { @new_cons_enabled = previous_value }
685
+ end
410
686
 
411
687
  # Acquire a connection by one of 1) immediately removing one
412
688
  # from the queue of available connections, 2) creating a new
@@ -414,41 +690,79 @@ module ActiveRecord
414
690
  # queue for a connection to become available.
415
691
  #
416
692
  # Raises:
417
- # - ConnectionTimeoutError if a connection could not be acquired
418
- def acquire_connection
419
- if conn = @available.poll
693
+ # - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
694
+ #
695
+ #--
696
+ # Implementation detail: the connection returned by +acquire_connection+
697
+ # will already be "+connection.lease+ -ed" to the current thread.
698
+ def acquire_connection(checkout_timeout)
699
+ # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
700
+ # +conn.lease+ the returned connection (and to do this in a +synchronized+
701
+ # section), this is not the cleanest implementation, as ideally we would
702
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
703
+ # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
704
+ # of the said methods and avoid an additional +synchronize+ overhead.
705
+ if conn = @available.poll || try_to_checkout_new_connection
420
706
  conn
421
- elsif @connections.size < @size
422
- checkout_new_connection
423
707
  else
424
708
  reap
425
- @available.poll(@checkout_timeout)
709
+ @available.poll(checkout_timeout)
426
710
  end
427
711
  end
428
712
 
429
- def release(conn, owner)
430
- thread_id = owner.object_id
431
-
432
- if @reserved_connections[thread_id] == conn
433
- @reserved_connections.delete thread_id
434
- end
713
+ #--
714
+ # if owner_thread param is omitted, this must be called in synchronize block
715
+ def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
716
+ @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
435
717
  end
718
+ alias_method :release, :remove_connection_from_thread_cache
436
719
 
437
720
  def new_connection
438
- Base.send(spec.adapter_method, spec.config)
721
+ Base.send(spec.adapter_method, spec.config).tap do |conn|
722
+ conn.schema_cache = schema_cache.dup if schema_cache
723
+ end
724
+ end
725
+
726
+ # If the pool is not at a +@size+ limit, establish new connection. Connecting
727
+ # to the DB is done outside main synchronized section.
728
+ #--
729
+ # Implementation constraint: a newly established connection returned by this
730
+ # method must be in the +.leased+ state.
731
+ def try_to_checkout_new_connection
732
+ # first in synchronized section check if establishing new conns is allowed
733
+ # and increment @now_connecting, to prevent overstepping this pool's @size
734
+ # constraint
735
+ do_checkout = synchronize do
736
+ if @new_cons_enabled && (@connections.size + @now_connecting) < @size
737
+ @now_connecting += 1
738
+ end
739
+ end
740
+ if do_checkout
741
+ begin
742
+ # if successfully incremented @now_connecting establish new connection
743
+ # outside of synchronized section
744
+ conn = checkout_new_connection
745
+ ensure
746
+ synchronize do
747
+ if conn
748
+ adopt_connection(conn)
749
+ # returned conn needs to be already leased
750
+ conn.lease
751
+ end
752
+ @now_connecting -= 1
753
+ end
754
+ end
755
+ end
439
756
  end
440
757
 
441
- def current_connection_id #:nodoc:
442
- Base.connection_id ||= Thread.current.object_id
758
+ def adopt_connection(conn)
759
+ conn.pool = self
760
+ @connections << conn
443
761
  end
444
762
 
445
763
  def checkout_new_connection
446
764
  raise ConnectionNotEstablished unless @automatic_reconnect
447
-
448
- c = new_connection
449
- c.pool = self
450
- @connections << c
451
- c
765
+ new_connection
452
766
  end
453
767
 
454
768
  def checkout_and_verify(c)
@@ -464,8 +778,7 @@ module ActiveRecord
464
778
  end
465
779
 
466
780
  # 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.
781
+ # for keeping separate connection pools that connect to different databases.
469
782
  #
470
783
  # For example, suppose that you have 5 models, with the following hierarchy:
471
784
  #
@@ -507,36 +820,25 @@ module ActiveRecord
507
820
  # ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
508
821
  # All Active Record models use this handler to determine the connection pool that they
509
822
  # should use.
823
+ #
824
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowlodge
825
+ # about the model. The model, needs to pass a specification name to the handler,
826
+ # in order to lookup the correct connection pool.
510
827
  class ConnectionHandler
511
828
  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)
517
- end
518
- @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
519
- h[k] = ThreadSafe::Cache.new
829
+ # These caches are keyed by spec.name (ConnectionSpecification#name).
830
+ @owner_to_pool = Concurrent::Map.new(:initial_capacity => 2) do |h,k|
831
+ h[k] = Concurrent::Map.new(:initial_capacity => 2)
520
832
  end
521
833
  end
522
834
 
523
835
  def connection_pool_list
524
836
  owner_to_pool.values.compact
525
837
  end
838
+ alias :connection_pools :connection_pool_list
526
839
 
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
532
-
533
- Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
534
- end
535
-
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)
840
+ def establish_connection(spec)
841
+ owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
540
842
  end
541
843
 
542
844
  # Returns true if there are any active connections among the connection
@@ -553,6 +855,8 @@ module ActiveRecord
553
855
  end
554
856
 
555
857
  # Clears the cache which maps classes.
858
+ #
859
+ # See ConnectionPool#clear_reloadable_connections! for details.
556
860
  def clear_reloadable_connections!
557
861
  connection_pool_list.each(&:clear_reloadable_connections!)
558
862
  end
@@ -565,18 +869,18 @@ module ActiveRecord
565
869
  # active or defined connection: if it is the latter, it will be
566
870
  # opened and set as the active connection for the class it was defined
567
871
  # 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
872
+ def retrieve_connection(spec_name) #:nodoc:
873
+ pool = retrieve_connection_pool(spec_name)
874
+ raise ConnectionNotEstablished, "No connection pool with id #{spec_name} found." unless pool
571
875
  conn = pool.connection
572
- raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
876
+ raise ConnectionNotEstablished, "No connection for #{spec_name} in connection pool" unless conn
573
877
  conn
574
878
  end
575
879
 
576
880
  # Returns true if a connection that's accessible to this class has
577
881
  # already been opened.
578
- def connected?(klass)
579
- conn = retrieve_connection_pool(klass)
882
+ def connected?(spec_name)
883
+ conn = retrieve_connection_pool(spec_name)
580
884
  conn && conn.connected?
581
885
  end
582
886
 
@@ -584,9 +888,8 @@ module ActiveRecord
584
888
  # connection and the defined connection (if they exist). The result
585
889
  # can be used as an argument for establish_connection, for easily
586
890
  # re-establishing the connection.
587
- def remove_connection(owner)
588
- if pool = owner_to_pool.delete(owner.name)
589
- @class_to_pool.clear
891
+ def remove_connection(spec_name)
892
+ if pool = owner_to_pool.delete(spec_name)
590
893
  pool.automatic_reconnect = false
591
894
  pool.disconnect!
592
895
  pool.spec.config
@@ -602,63 +905,30 @@ module ActiveRecord
602
905
  # #fetch is significantly slower than #[]. So in the nil case, no caching will
603
906
  # take place, but that's ok since the nil case is not the common one that we wish
604
907
  # 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)
908
+ def retrieve_connection_pool(spec_name)
909
+ owner_to_pool.fetch(spec_name) do
910
+ if ancestor_pool = pool_from_any_process_for(spec_name)
629
911
  # A connection was established in an ancestor process that must have
630
912
  # subsequently forked. We can't reuse the connection, but we can copy
631
913
  # the specification and establish a new connection with it.
632
- establish_connection owner, ancestor_pool.spec
914
+ establish_connection(ancestor_pool.spec).tap do |pool|
915
+ pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
916
+ end
633
917
  else
634
- owner_to_pool[owner.name] = nil
918
+ owner_to_pool[spec_name] = nil
635
919
  end
636
- }
920
+ end
637
921
  end
638
922
 
639
- def pool_from_any_process_for(owner)
640
- owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
641
- owner_to_pool && owner_to_pool[owner.name]
642
- end
643
- end
923
+ private
644
924
 
645
- class ConnectionManagement
646
- def initialize(app)
647
- @app = app
925
+ def owner_to_pool
926
+ @owner_to_pool[Process.pid]
648
927
  end
649
928
 
650
- def call(env)
651
- testing = env['rack.test']
652
-
653
- response = @app.call(env)
654
- response[2] = ::Rack::BodyProxy.new(response[2]) do
655
- ActiveRecord::Base.clear_active_connections! unless testing
656
- end
657
-
658
- response
659
- rescue Exception
660
- ActiveRecord::Base.clear_active_connections! unless testing
661
- raise
929
+ def pool_from_any_process_for(spec_name)
930
+ owner_to_pool = @owner_to_pool.values.find { |v| v[spec_name] }
931
+ owner_to_pool && owner_to_pool[spec_name]
662
932
  end
663
933
  end
664
934
  end