activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,11 +1,12 @@
1
1
  require 'thread'
2
+ require 'thread_safe'
2
3
  require 'monitor'
3
4
  require 'set'
4
- require 'active_support/core_ext/module/deprecation'
5
5
 
6
6
  module ActiveRecord
7
7
  # Raised when a connection could not be obtained within the connection
8
- # acquisition timeout period.
8
+ # acquisition timeout period: because max connections in pool
9
+ # are in use.
9
10
  class ConnectionTimeoutError < ConnectionNotEstablished
10
11
  end
11
12
 
@@ -50,20 +51,179 @@ module ActiveRecord
50
51
  #
51
52
  # == Options
52
53
  #
53
- # There are two connection-pooling-related options that you can add to
54
+ # There are several connection-pooling-related options that you can add to
54
55
  # your database connection configuration:
55
56
  #
56
57
  # * +pool+: number indicating size of connection pool (default 5)
57
- # * +checkout _timeout+: number of seconds to block and wait for a
58
- # connection before giving up and raising a timeout error
59
- # (default 5 seconds). ('wait_timeout' supported for backwards
60
- # compatibility, but conflicts with key used for different purpose
61
- # by mysql2 adapter).
58
+ # * +checkout_timeout+: number of seconds to block and wait for a connection
59
+ # before giving up and raising a timeout error (default 5 seconds).
60
+ # * +reaping_frequency+: frequency in seconds to periodically run the
61
+ # Reaper, which attempts to find and close dead connections, which can
62
+ # occur if a programmer forgets to close a connection at the end of a
63
+ # thread or a thread dies unexpectedly. (Default nil, which means don't
64
+ # run the Reaper).
65
+ # * +dead_connection_timeout+: number of seconds from last checkout
66
+ # after which the Reaper will consider a connection reapable. (default
67
+ # 5 seconds).
62
68
  class ConnectionPool
69
+ # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
70
+ # with which it shares a Monitor. But could be a generic Queue.
71
+ #
72
+ # The Queue in stdlib's 'thread' could replace this class except
73
+ # stdlib's doesn't support waiting with a timeout.
74
+ class Queue
75
+ def initialize(lock = Monitor.new)
76
+ @lock = lock
77
+ @cond = @lock.new_cond
78
+ @num_waiting = 0
79
+ @queue = []
80
+ end
81
+
82
+ # Test if any threads are currently waiting on the queue.
83
+ def any_waiting?
84
+ synchronize do
85
+ @num_waiting > 0
86
+ end
87
+ end
88
+
89
+ # Return the number of threads currently waiting on this
90
+ # queue.
91
+ def num_waiting
92
+ synchronize do
93
+ @num_waiting
94
+ end
95
+ end
96
+
97
+ # Add +element+ to the queue. Never blocks.
98
+ def add(element)
99
+ synchronize do
100
+ @queue.push element
101
+ @cond.signal
102
+ end
103
+ end
104
+
105
+ # If +element+ is in the queue, remove and return it, or nil.
106
+ def delete(element)
107
+ synchronize do
108
+ @queue.delete(element)
109
+ end
110
+ end
111
+
112
+ # Remove all elements from the queue.
113
+ def clear
114
+ synchronize do
115
+ @queue.clear
116
+ end
117
+ end
118
+
119
+ # Remove the head of the queue.
120
+ #
121
+ # If +timeout+ is not given, remove and return the head the
122
+ # queue if the number of available elements is strictly
123
+ # greater than the number of threads currently waiting (that
124
+ # is, don't jump ahead in line). Otherwise, return nil.
125
+ #
126
+ # If +timeout+ is given, block if it there is no element
127
+ # available, waiting up to +timeout+ seconds for an element to
128
+ # become available.
129
+ #
130
+ # Raises:
131
+ # - ConnectionTimeoutError if +timeout+ is given and no element
132
+ # becomes available after +timeout+ seconds,
133
+ def poll(timeout = nil)
134
+ synchronize do
135
+ if timeout
136
+ no_wait_poll || wait_poll(timeout)
137
+ else
138
+ no_wait_poll
139
+ end
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def synchronize(&block)
146
+ @lock.synchronize(&block)
147
+ end
148
+
149
+ # Test if the queue currently contains any elements.
150
+ def any?
151
+ !@queue.empty?
152
+ end
153
+
154
+ # A thread can remove an element from the queue without
155
+ # waiting if an only if the number of currently available
156
+ # connections is strictly greater than the number of waiting
157
+ # threads.
158
+ def can_remove_no_wait?
159
+ @queue.size > @num_waiting
160
+ end
161
+
162
+ # Removes and returns the head of the queue if possible, or nil.
163
+ def remove
164
+ @queue.shift
165
+ end
166
+
167
+ # Remove and return the head the queue if the number of
168
+ # available elements is strictly greater than the number of
169
+ # threads currently waiting. Otherwise, return nil.
170
+ def no_wait_poll
171
+ remove if can_remove_no_wait?
172
+ end
173
+
174
+ # Waits on the queue up to +timeout+ seconds, then removes and
175
+ # returns the head of the queue.
176
+ def wait_poll(timeout)
177
+ @num_waiting += 1
178
+
179
+ t0 = Time.now
180
+ elapsed = 0
181
+ loop do
182
+ @cond.wait(timeout - elapsed)
183
+
184
+ return remove if any?
185
+
186
+ elapsed = Time.now - t0
187
+ if elapsed >= timeout
188
+ msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
189
+ [timeout, elapsed]
190
+ raise ConnectionTimeoutError, msg
191
+ end
192
+ end
193
+ ensure
194
+ @num_waiting -= 1
195
+ end
196
+ end
197
+
198
+ # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
199
+ # A reaper instantiated with a nil frequency will never reap the
200
+ # connection pool.
201
+ #
202
+ # Configure the frequency by setting "reaping_frequency" in your
203
+ # database yaml file.
204
+ class Reaper
205
+ attr_reader :pool, :frequency
206
+
207
+ def initialize(pool, frequency)
208
+ @pool = pool
209
+ @frequency = frequency
210
+ end
211
+
212
+ def run
213
+ return unless frequency
214
+ Thread.new(frequency, pool) { |t, p|
215
+ while true
216
+ sleep t
217
+ p.reap
218
+ end
219
+ }
220
+ end
221
+ end
222
+
63
223
  include MonitorMixin
64
224
 
65
- attr_accessor :automatic_reconnect
66
- attr_reader :spec, :connections
225
+ attr_accessor :automatic_reconnect, :checkout_timeout, :dead_connection_timeout
226
+ attr_reader :spec, :connections, :size, :reaper
67
227
 
68
228
  # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
69
229
  # object which describes database connection information (e.g. adapter,
@@ -76,20 +236,21 @@ module ActiveRecord
76
236
 
77
237
  @spec = spec
78
238
 
79
- # The cache of reserved connections mapped to threads
80
- @reserved_connections = {}
81
-
82
- @queue = new_cond
83
- # 'wait_timeout', the backward-compatible key, conflicts with spec key
84
- # used by mysql2 for something entirely different, checkout_timeout
85
- # preferred to avoid conflict and allow independent values.
86
- @timeout = spec.config[:checkout_timeout] || spec.config[:wait_timeout] || 5
239
+ @checkout_timeout = spec.config[:checkout_timeout] || 5
240
+ @dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
241
+ @reaper = Reaper.new self, spec.config[:reaping_frequency]
242
+ @reaper.run
87
243
 
88
244
  # default max pool size to 5
89
245
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
90
246
 
247
+ # The cache of reserved connections mapped to threads
248
+ @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
249
+
91
250
  @connections = []
92
251
  @automatic_reconnect = true
252
+
253
+ @available = Queue.new self
93
254
  end
94
255
 
95
256
  # Retrieve the connection associated with the current thread, or call
@@ -98,7 +259,9 @@ module ActiveRecord
98
259
  # #connection can be called any number of times; the connection is
99
260
  # held in a hash keyed by the thread id.
100
261
  def connection
101
- synchronize do
262
+ # this is correctly done double-checked locking
263
+ # (ThreadSafe::Cache's lookups have volatile semantics)
264
+ @reserved_connections[current_connection_id] || synchronize do
102
265
  @reserved_connections[current_connection_id] ||= checkout
103
266
  end
104
267
  end
@@ -116,8 +279,10 @@ module ActiveRecord
116
279
  # #release_connection releases the connection-thread association
117
280
  # and returns the connection to the pool.
118
281
  def release_connection(with_id = current_connection_id)
119
- conn = synchronize { @reserved_connections.delete(with_id) }
120
- checkin conn if conn
282
+ synchronize do
283
+ conn = @reserved_connections.delete(with_id)
284
+ checkin conn if conn
285
+ end
121
286
  end
122
287
 
123
288
  # If a connection already exists yield it to the block. If no connection
@@ -139,19 +304,20 @@ module ActiveRecord
139
304
  # Disconnects all connections in the pool, and clears the pool.
140
305
  def disconnect!
141
306
  synchronize do
142
- @reserved_connections = {}
307
+ @reserved_connections.clear
143
308
  @connections.each do |conn|
144
309
  checkin conn
145
310
  conn.disconnect!
146
311
  end
147
312
  @connections = []
313
+ @available.clear
148
314
  end
149
315
  end
150
316
 
151
317
  # Clears the cache which maps classes.
152
318
  def clear_reloadable_connections!
153
319
  synchronize do
154
- @reserved_connections = {}
320
+ @reserved_connections.clear
155
321
  @connections.each do |conn|
156
322
  checkin conn
157
323
  conn.disconnect! if conn.requires_reloading?
@@ -159,121 +325,37 @@ module ActiveRecord
159
325
  @connections.delete_if do |conn|
160
326
  conn.requires_reloading?
161
327
  end
162
- end
163
- end
164
-
165
- # Verify active connections and remove and disconnect connections
166
- # associated with stale threads.
167
- def verify_active_connections! #:nodoc:
168
- synchronize do
169
- clear_stale_cached_connections!
170
- @connections.each do |connection|
171
- connection.verify!
328
+ @available.clear
329
+ @connections.each do |conn|
330
+ @available.add conn
172
331
  end
173
332
  end
174
333
  end
175
334
 
176
- def columns
177
- with_connection do |c|
178
- c.schema_cache.columns
179
- end
180
- end
181
- deprecate :columns
182
-
183
- def columns_hash
184
- with_connection do |c|
185
- c.schema_cache.columns_hash
186
- end
187
- end
188
- deprecate :columns_hash
189
-
190
- def primary_keys
191
- with_connection do |c|
192
- c.schema_cache.primary_keys
193
- end
194
- end
195
- deprecate :primary_keys
196
-
197
- def clear_cache!
198
- with_connection do |c|
199
- c.schema_cache.clear!
200
- end
201
- end
202
- deprecate :clear_cache!
203
-
204
- # Return any checked-out connections back to the pool by threads that
205
- # are no longer alive.
206
- def clear_stale_cached_connections!
207
- keys = @reserved_connections.keys - Thread.list.find_all { |t|
208
- t.alive?
209
- }.map { |thread| thread.object_id }
210
- keys.each do |key|
211
- conn = @reserved_connections[key]
212
- ActiveSupport::Deprecation.warn(<<-eowarn) if conn.in_use?
213
- Database connections will not be closed automatically, please close your
214
- database connection at the end of the thread by calling `close` on your
215
- connection. For example: ActiveRecord::Base.connection.close
216
- eowarn
217
- checkin conn
218
- @reserved_connections.delete(key)
219
- end
335
+ def clear_stale_cached_connections! # :nodoc:
336
+ reap
220
337
  end
338
+ deprecate :clear_stale_cached_connections! => "Please use #reap instead"
221
339
 
222
340
  # Check-out a database connection from the pool, indicating that you want
223
341
  # to use it. You should call #checkin when you no longer need this.
224
342
  #
225
- # This is done by either returning an existing connection, or by creating
226
- # a new connection. If the maximum number of connections for this pool has
227
- # already been reached, but the pool is empty (i.e. they're all being used),
228
- # then this method will wait until a thread has checked in a connection.
229
- # The wait time is bounded however: if no connection can be checked out
230
- # within the timeout specified for this pool, then a ConnectionTimeoutError
231
- # exception will be raised.
343
+ # This is done by either returning and leasing existing connection, or by
344
+ # creating a new connection and leasing it.
345
+ #
346
+ # If all connections are leased and the pool is at capacity (meaning the
347
+ # number of currently leased connections is greater than or equal to the
348
+ # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
232
349
  #
233
350
  # Returns: an AbstractAdapter object.
234
351
  #
235
352
  # Raises:
236
- # - ConnectionTimeoutError: no connection can be obtained from the pool
237
- # within the timeout period.
353
+ # - ConnectionTimeoutError: no connection can be obtained from the pool.
238
354
  def checkout
239
355
  synchronize do
240
- waited_time = 0
241
-
242
- loop do
243
- conn = @connections.find { |c| c.lease }
244
-
245
- unless conn
246
- if @connections.size < @size
247
- conn = checkout_new_connection
248
- conn.lease
249
- end
250
- end
251
-
252
- if conn
253
- checkout_and_verify conn
254
- return conn
255
- end
256
-
257
- if waited_time >= @timeout
258
- raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout} (waited #{waited_time} seconds). The max pool size is currently #{@size}; consider increasing it."
259
- end
260
-
261
- # Sometimes our wait can end because a connection is available,
262
- # but another thread can snatch it up first. If timeout hasn't
263
- # passed but no connection is avail, looks like that happened --
264
- # loop and wait again, for the time remaining on our timeout.
265
- before_wait = Time.now
266
- @queue.wait( [@timeout - waited_time, 0].max )
267
- waited_time += (Time.now - before_wait)
268
-
269
- # Will go away in Rails 4, when we don't clean up
270
- # after leaked connections automatically anymore. Right now, clean
271
- # up after we've returned from a 'wait' if it looks like it's
272
- # needed, then loop and try again.
273
- if(active_connections.size >= @connections.size)
274
- clear_stale_cached_connections!
275
- end
276
- end
356
+ conn = acquire_connection
357
+ conn.lease
358
+ checkout_and_verify(conn)
277
359
  end
278
360
  end
279
361
 
@@ -286,37 +368,80 @@ connection. For example: ActiveRecord::Base.connection.close
286
368
  synchronize do
287
369
  conn.run_callbacks :checkin do
288
370
  conn.expire
289
- @queue.signal
290
371
  end
291
372
 
292
373
  release conn
374
+
375
+ @available.add conn
293
376
  end
294
377
  end
295
378
 
296
- private
297
-
298
- def release(conn)
379
+ # Remove a connection from the connection pool. The connection will
380
+ # remain open and active but will no longer be managed by this pool.
381
+ def remove(conn)
299
382
  synchronize do
300
- thread_id = nil
383
+ @connections.delete conn
384
+ @available.delete conn
301
385
 
302
- if @reserved_connections[current_connection_id] == conn
303
- thread_id = current_connection_id
304
- else
305
- thread_id = @reserved_connections.keys.find { |k|
306
- @reserved_connections[k] == conn
307
- }
386
+ # FIXME: we might want to store the key on the connection so that removing
387
+ # from the reserved hash will be a little easier.
388
+ release conn
389
+
390
+ @available.add checkout_new_connection if @available.any_waiting?
391
+ end
392
+ end
393
+
394
+ # Removes dead connections from the pool. A dead connection can occur
395
+ # if a programmer forgets to close a connection at the end of a thread
396
+ # or a thread dies unexpectedly.
397
+ def reap
398
+ synchronize do
399
+ stale = Time.now - @dead_connection_timeout
400
+ connections.dup.each do |conn|
401
+ if conn.in_use? && stale > conn.last_use && !conn.active_threadsafe?
402
+ remove conn
403
+ end
308
404
  end
405
+ end
406
+ end
309
407
 
310
- @reserved_connections.delete thread_id if thread_id
408
+ private
409
+
410
+ # Acquire a connection by one of 1) immediately removing one
411
+ # from the queue of available connections, 2) creating a new
412
+ # connection if the pool is not at capacity, 3) waiting on the
413
+ # queue for a connection to become available.
414
+ #
415
+ # Raises:
416
+ # - ConnectionTimeoutError if a connection could not be acquired
417
+ def acquire_connection
418
+ if conn = @available.poll
419
+ conn
420
+ elsif @connections.size < @size
421
+ checkout_new_connection
422
+ else
423
+ @available.poll(@checkout_timeout)
311
424
  end
312
425
  end
313
426
 
427
+ def release(conn)
428
+ thread_id = if @reserved_connections[current_connection_id] == conn
429
+ current_connection_id
430
+ else
431
+ @reserved_connections.keys.find { |k|
432
+ @reserved_connections[k] == conn
433
+ }
434
+ end
435
+
436
+ @reserved_connections.delete thread_id if thread_id
437
+ end
438
+
314
439
  def new_connection
315
- ActiveRecord::Base.send(spec.adapter_method, spec.config)
440
+ Base.send(spec.adapter_method, spec.config)
316
441
  end
317
442
 
318
443
  def current_connection_id #:nodoc:
319
- ActiveRecord::Base.connection_id ||= Thread.current.object_id
444
+ Base.connection_id ||= Thread.current.object_id
320
445
  end
321
446
 
322
447
  def checkout_new_connection
@@ -334,10 +459,6 @@ connection. For example: ActiveRecord::Base.connection.close
334
459
  end
335
460
  c
336
461
  end
337
-
338
- def active_connections
339
- @connections.find_all { |c| c.in_use? }
340
- end
341
462
  end
342
463
 
343
464
  # ConnectionHandler is a collection of ConnectionPool objects. It is used
@@ -362,43 +483,58 @@ connection. For example: ActiveRecord::Base.connection.close
362
483
  #
363
484
  # Normally there is only a single ConnectionHandler instance, accessible via
364
485
  # ActiveRecord::Base.connection_handler. Active Record models use this to
365
- # determine that connection pool that they should use.
486
+ # determine the connection pool that they should use.
366
487
  class ConnectionHandler
367
- attr_reader :connection_pools
488
+ def initialize
489
+ # These caches are keyed by klass.name, NOT klass. Keying them by klass
490
+ # alone would lead to memory leaks in development mode as all previous
491
+ # instances of the class would stay in memory.
492
+ @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
493
+ h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
494
+ end
495
+ @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
496
+ h[k] = ThreadSafe::Cache.new
497
+ end
498
+ end
499
+
500
+ def connection_pool_list
501
+ owner_to_pool.values.compact
502
+ end
368
503
 
369
- def initialize(pools = {})
370
- @connection_pools = pools
371
- @class_to_pool = {}
504
+ def connection_pools
505
+ ActiveSupport::Deprecation.warn(
506
+ "In the next release, this will return the same as #connection_pool_list. " \
507
+ "(An array of pools, rather than a hash mapping specs to pools.)"
508
+ )
509
+ Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
372
510
  end
373
511
 
374
- def establish_connection(name, spec)
375
- @connection_pools[spec] ||= ConnectionAdapters::ConnectionPool.new(spec)
376
- @class_to_pool[name] = @connection_pools[spec]
512
+ def establish_connection(owner, spec)
513
+ @class_to_pool.clear
514
+ raise RuntimeError, "Anonymous class is not allowed." unless owner.name
515
+ owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
377
516
  end
378
517
 
379
518
  # Returns true if there are any active connections among the connection
380
519
  # pools that the ConnectionHandler is managing.
381
520
  def active_connections?
382
- connection_pools.values.any? { |pool| pool.active_connection? }
521
+ connection_pool_list.any?(&:active_connection?)
383
522
  end
384
523
 
385
- # Returns any connections in use by the current thread back to the pool.
524
+ # Returns any connections in use by the current thread back to the pool,
525
+ # and also returns connections to the pool cached by threads that are no
526
+ # longer alive.
386
527
  def clear_active_connections!
387
- @connection_pools.each_value {|pool| pool.release_connection }
528
+ connection_pool_list.each(&:release_connection)
388
529
  end
389
530
 
390
531
  # Clears the cache which maps classes.
391
532
  def clear_reloadable_connections!
392
- @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
533
+ connection_pool_list.each(&:clear_reloadable_connections!)
393
534
  end
394
535
 
395
536
  def clear_all_connections!
396
- @connection_pools.each_value {|pool| pool.disconnect! }
397
- end
398
-
399
- # Verify active connections.
400
- def verify_active_connections! #:nodoc:
401
- @connection_pools.each_value {|pool| pool.verify_active_connections! }
537
+ connection_pool_list.each(&:disconnect!)
402
538
  end
403
539
 
404
540
  # Locate the connection of the nearest super class. This can be an
@@ -421,54 +557,65 @@ connection. For example: ActiveRecord::Base.connection.close
421
557
  # connection and the defined connection (if they exist). The result
422
558
  # can be used as an argument for establish_connection, for easily
423
559
  # re-establishing the connection.
424
- def remove_connection(klass)
425
- pool = @class_to_pool.delete(klass.name)
426
- return nil unless pool
427
-
428
- @connection_pools.delete pool.spec
429
- pool.automatic_reconnect = false
430
- pool.disconnect!
431
- pool.spec.config
560
+ def remove_connection(owner)
561
+ if pool = owner_to_pool.delete(owner.name)
562
+ @class_to_pool.clear
563
+ pool.automatic_reconnect = false
564
+ pool.disconnect!
565
+ pool.spec.config
566
+ end
432
567
  end
433
568
 
569
+ # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
570
+ # This makes retrieving the connection pool O(1) once the process is warm.
571
+ # When a connection is established or removed, we invalidate the cache.
572
+ #
573
+ # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
574
+ # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
575
+ # #fetch is significantly slower than #[]. So in the nil case, no caching will
576
+ # take place, but that's ok since the nil case is not the common one that we wish
577
+ # to optimise for.
434
578
  def retrieve_connection_pool(klass)
435
- pool = @class_to_pool[klass.name]
436
- return pool if pool
437
- return nil if ActiveRecord::Base == klass
438
- retrieve_connection_pool klass.superclass
439
- end
440
- end
441
-
442
- class ConnectionManagement
443
- class Proxy # :nodoc:
444
- attr_reader :body, :testing
579
+ class_to_pool[klass.name] ||= begin
580
+ until pool = pool_for(klass)
581
+ klass = klass.superclass
582
+ break unless klass <= Base
583
+ end
445
584
 
446
- def initialize(body, testing = false)
447
- @body = body
448
- @testing = testing
585
+ class_to_pool[klass.name] = pool
449
586
  end
587
+ end
450
588
 
451
- def method_missing(method_sym, *arguments, &block)
452
- @body.send(method_sym, *arguments, &block)
453
- end
589
+ private
454
590
 
455
- def respond_to?(method_sym, include_private = false)
456
- super || @body.respond_to?(method_sym)
457
- end
591
+ def owner_to_pool
592
+ @owner_to_pool[Process.pid]
593
+ end
458
594
 
459
- def each(&block)
460
- body.each(&block)
461
- end
595
+ def class_to_pool
596
+ @class_to_pool[Process.pid]
597
+ end
462
598
 
463
- def close
464
- body.close if body.respond_to?(:close)
599
+ def pool_for(owner)
600
+ owner_to_pool.fetch(owner.name) {
601
+ if ancestor_pool = pool_from_any_process_for(owner)
602
+ # A connection was established in an ancestor process that must have
603
+ # subsequently forked. We can't reuse the connection, but we can copy
604
+ # the specification and establish a new connection with it.
605
+ establish_connection owner, ancestor_pool.spec
606
+ else
607
+ owner_to_pool[owner.name] = nil
608
+ end
609
+ }
610
+ end
465
611
 
466
- # Don't return connection (and perform implicit rollback) if
467
- # this request is a part of integration test
468
- ActiveRecord::Base.clear_active_connections! unless testing
469
- end
612
+ def pool_from_any_process_for(owner)
613
+ owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
614
+ owner_to_pool && owner_to_pool[owner.name]
470
615
  end
616
+ end
471
617
 
618
+ class ConnectionManagement
472
619
  def initialize(app)
473
620
  @app = app
474
621
  end
@@ -476,9 +623,12 @@ connection. For example: ActiveRecord::Base.connection.close
476
623
  def call(env)
477
624
  testing = env.key?('rack.test')
478
625
 
479
- status, headers, body = @app.call(env)
626
+ response = @app.call(env)
627
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
628
+ ActiveRecord::Base.clear_active_connections! unless testing
629
+ end
480
630
 
481
- [status, headers, Proxy.new(body, testing)]
631
+ response
482
632
  rescue
483
633
  ActiveRecord::Base.clear_active_connections! unless testing
484
634
  raise