activerecord 3.2.22.5 → 4.0.0.beta1

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  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 +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  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 +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  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/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/array/wrap'
2
-
3
1
  module ActiveRecord
4
2
  # = Active Record Callbacks
5
3
  #
@@ -25,7 +23,7 @@ module ActiveRecord
25
23
  # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
26
24
  # <tt>after_rollback</tt>.
27
25
  #
28
- # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
26
+ # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
29
27
  # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
30
28
  # are instantiated as well.
31
29
  #
@@ -36,8 +34,8 @@ module ActiveRecord
36
34
  # Examples:
37
35
  # class CreditCard < ActiveRecord::Base
38
36
  # # Strip everything but digits, so the user can specify "555 234 34" or
39
- # # "5552-3434" or both will mean "55523434"
40
- # before_validation(:on => :create) do
37
+ # # "5552-3434" and both will mean "55523434"
38
+ # before_validation(on: :create) do
41
39
  # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
42
40
  # end
43
41
  # end
@@ -202,6 +200,40 @@ module ActiveRecord
202
200
  # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
203
201
  # methods on the model, which are called last.
204
202
  #
203
+ # == Ordering callbacks
204
+ #
205
+ # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
206
+ # callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option.
207
+ #
208
+ # Let's look at the code below:
209
+ #
210
+ # class Topic < ActiveRecord::Base
211
+ # has_many :children, dependent: destroy
212
+ #
213
+ # before_destroy :log_children
214
+ #
215
+ # private
216
+ # def log_children
217
+ # # Child processing
218
+ # end
219
+ # end
220
+ #
221
+ # In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
222
+ # because the +destroy+ callback gets executed first. You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
223
+ #
224
+ # class Topic < ActiveRecord::Base
225
+ # has_many :children, dependent: destroy
226
+ #
227
+ # before_destroy :log_children, prepend: true
228
+ #
229
+ # private
230
+ # def log_children
231
+ # # Child processing
232
+ # end
233
+ # end
234
+ #
235
+ # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
236
+ #
205
237
  # == Transactions
206
238
  #
207
239
  # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
@@ -215,23 +247,23 @@ module ActiveRecord
215
247
  # instead of quietly returning +false+.
216
248
  #
217
249
  # == Debugging callbacks
218
- #
219
- # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
250
+ #
251
+ # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
220
252
  # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
221
253
  # defines what part of the chain the callback runs in.
222
- #
223
- # To find all callbacks in the before_save callback chain:
224
- #
254
+ #
255
+ # To find all callbacks in the before_save callback chain:
256
+ #
225
257
  # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
226
- #
258
+ #
227
259
  # Returns an array of callback objects that form the before_save chain.
228
- #
260
+ #
229
261
  # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
230
- #
262
+ #
231
263
  # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
232
- #
264
+ #
233
265
  # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
234
- #
266
+ #
235
267
  module Callbacks
236
268
  extend ActiveSupport::Concern
237
269
 
@@ -242,8 +274,11 @@ module ActiveRecord
242
274
  :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
243
275
  ]
244
276
 
277
+ module ClassMethods
278
+ include ActiveModel::Callbacks
279
+ end
280
+
245
281
  included do
246
- extend ActiveModel::Callbacks
247
282
  include ActiveModel::Validations::Callbacks
248
283
 
249
284
  define_model_callbacks :initialize, :find, :touch, :only => :after
@@ -264,11 +299,11 @@ module ActiveRecord
264
299
  run_callbacks(:save) { super }
265
300
  end
266
301
 
267
- def create #:nodoc:
302
+ def create_record #:nodoc:
268
303
  run_callbacks(:create) { super }
269
304
  end
270
305
 
271
- def update(*) #:nodoc:
306
+ def update_record(*) #:nodoc:
272
307
  run_callbacks(:update) { super }
273
308
  end
274
309
  end
@@ -1,12 +1,9 @@
1
- module ActiveRecord
2
- # :stopdoc:
3
- module Coders
4
- class YAMLColumn
5
- RESCUE_ERRORS = [ ArgumentError ]
1
+ require 'yaml'
6
2
 
7
- if defined?(Psych) && defined?(Psych::SyntaxError)
8
- RESCUE_ERRORS << Psych::SyntaxError
9
- end
3
+ module ActiveRecord
4
+ module Coders # :nodoc:
5
+ class YAMLColumn # :nodoc:
6
+ RESCUE_ERRORS = [ ArgumentError, Psych::SyntaxError ]
10
7
 
11
8
  attr_accessor :object_class
12
9
 
@@ -15,6 +12,12 @@ module ActiveRecord
15
12
  end
16
13
 
17
14
  def dump(obj)
15
+ return if obj.nil?
16
+
17
+ unless obj.is_a?(object_class)
18
+ raise SerializationTypeMismatch,
19
+ "Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
20
+ end
18
21
  YAML.dump obj
19
22
  end
20
23
 
@@ -37,5 +40,4 @@ module ActiveRecord
37
40
  end
38
41
  end
39
42
  end
40
- # :startdoc
41
43
  end
@@ -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,29 @@ 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
254
+ end
255
+
256
+ # Hack for tests to be able to add connections. Do not call outside of tests
257
+ def insert_connection_for_test!(c) #:nodoc:
258
+ synchronize do
259
+ @connections << c
260
+ @available.add c
261
+ end
93
262
  end
94
263
 
95
264
  # Retrieve the connection associated with the current thread, or call
@@ -98,7 +267,9 @@ module ActiveRecord
98
267
  # #connection can be called any number of times; the connection is
99
268
  # held in a hash keyed by the thread id.
100
269
  def connection
101
- synchronize do
270
+ # this is correctly done double-checked locking
271
+ # (ThreadSafe::Cache's lookups have volatile semantics)
272
+ @reserved_connections[current_connection_id] || synchronize do
102
273
  @reserved_connections[current_connection_id] ||= checkout
103
274
  end
104
275
  end
@@ -116,8 +287,10 @@ module ActiveRecord
116
287
  # #release_connection releases the connection-thread association
117
288
  # and returns the connection to the pool.
118
289
  def release_connection(with_id = current_connection_id)
119
- conn = synchronize { @reserved_connections.delete(with_id) }
120
- checkin conn if conn
290
+ synchronize do
291
+ conn = @reserved_connections.delete(with_id)
292
+ checkin conn if conn
293
+ end
121
294
  end
122
295
 
123
296
  # If a connection already exists yield it to the block. If no connection
@@ -139,19 +312,20 @@ module ActiveRecord
139
312
  # Disconnects all connections in the pool, and clears the pool.
140
313
  def disconnect!
141
314
  synchronize do
142
- @reserved_connections = {}
315
+ @reserved_connections.clear
143
316
  @connections.each do |conn|
144
317
  checkin conn
145
318
  conn.disconnect!
146
319
  end
147
320
  @connections = []
321
+ @available.clear
148
322
  end
149
323
  end
150
324
 
151
325
  # Clears the cache which maps classes.
152
326
  def clear_reloadable_connections!
153
327
  synchronize do
154
- @reserved_connections = {}
328
+ @reserved_connections.clear
155
329
  @connections.each do |conn|
156
330
  checkin conn
157
331
  conn.disconnect! if conn.requires_reloading?
@@ -159,121 +333,37 @@ module ActiveRecord
159
333
  @connections.delete_if do |conn|
160
334
  conn.requires_reloading?
161
335
  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!
336
+ @available.clear
337
+ @connections.each do |conn|
338
+ @available.add conn
172
339
  end
173
340
  end
174
341
  end
175
342
 
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
343
+ def clear_stale_cached_connections! # :nodoc:
344
+ reap
220
345
  end
346
+ deprecate :clear_stale_cached_connections! => "Please use #reap instead"
221
347
 
222
348
  # Check-out a database connection from the pool, indicating that you want
223
349
  # to use it. You should call #checkin when you no longer need this.
224
350
  #
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.
351
+ # This is done by either returning and leasing existing connection, or by
352
+ # creating a new connection and leasing it.
353
+ #
354
+ # If all connections are leased and the pool is at capacity (meaning the
355
+ # number of currently leased connections is greater than or equal to the
356
+ # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
232
357
  #
233
358
  # Returns: an AbstractAdapter object.
234
359
  #
235
360
  # Raises:
236
- # - ConnectionTimeoutError: no connection can be obtained from the pool
237
- # within the timeout period.
361
+ # - ConnectionTimeoutError: no connection can be obtained from the pool.
238
362
  def checkout
239
363
  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
364
+ conn = acquire_connection
365
+ conn.lease
366
+ checkout_and_verify(conn)
277
367
  end
278
368
  end
279
369
 
@@ -286,37 +376,78 @@ connection. For example: ActiveRecord::Base.connection.close
286
376
  synchronize do
287
377
  conn.run_callbacks :checkin do
288
378
  conn.expire
289
- @queue.signal
290
379
  end
291
380
 
292
381
  release conn
382
+
383
+ @available.add conn
293
384
  end
294
385
  end
295
386
 
296
- private
297
-
298
- def release(conn)
387
+ # Remove a connection from the connection pool. The connection will
388
+ # remain open and active but will no longer be managed by this pool.
389
+ def remove(conn)
299
390
  synchronize do
300
- thread_id = nil
391
+ @connections.delete conn
392
+ @available.delete conn
301
393
 
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
- }
394
+ # FIXME: we might want to store the key on the connection so that removing
395
+ # from the reserved hash will be a little easier.
396
+ release conn
397
+
398
+ @available.add checkout_new_connection if @available.any_waiting?
399
+ end
400
+ end
401
+
402
+ # Removes dead connections from the pool. A dead connection can occur
403
+ # if a programmer forgets to close a connection at the end of a thread
404
+ # or a thread dies unexpectedly.
405
+ def reap
406
+ synchronize do
407
+ stale = Time.now - @dead_connection_timeout
408
+ connections.dup.each do |conn|
409
+ remove conn if conn.in_use? && stale > conn.last_use && !conn.active?
308
410
  end
411
+ end
412
+ end
413
+
414
+ private
309
415
 
310
- @reserved_connections.delete thread_id if thread_id
416
+ # Acquire a connection by one of 1) immediately removing one
417
+ # from the queue of available connections, 2) creating a new
418
+ # connection if the pool is not at capacity, 3) waiting on the
419
+ # queue for a connection to become available.
420
+ #
421
+ # Raises:
422
+ # - ConnectionTimeoutError if a connection could not be acquired
423
+ def acquire_connection
424
+ if conn = @available.poll
425
+ conn
426
+ elsif @connections.size < @size
427
+ checkout_new_connection
428
+ else
429
+ @available.poll(@checkout_timeout)
311
430
  end
312
431
  end
313
432
 
433
+ def release(conn)
434
+ thread_id = if @reserved_connections[current_connection_id] == conn
435
+ current_connection_id
436
+ else
437
+ @reserved_connections.keys.find { |k|
438
+ @reserved_connections[k] == conn
439
+ }
440
+ end
441
+
442
+ @reserved_connections.delete thread_id if thread_id
443
+ end
444
+
314
445
  def new_connection
315
- ActiveRecord::Base.send(spec.adapter_method, spec.config)
446
+ Base.send(spec.adapter_method, spec.config)
316
447
  end
317
448
 
318
449
  def current_connection_id #:nodoc:
319
- ActiveRecord::Base.connection_id ||= Thread.current.object_id
450
+ Base.connection_id ||= Thread.current.object_id
320
451
  end
321
452
 
322
453
  def checkout_new_connection
@@ -334,10 +465,6 @@ connection. For example: ActiveRecord::Base.connection.close
334
465
  end
335
466
  c
336
467
  end
337
-
338
- def active_connections
339
- @connections.find_all { |c| c.in_use? }
340
- end
341
468
  end
342
469
 
343
470
  # ConnectionHandler is a collection of ConnectionPool objects. It is used
@@ -362,43 +489,58 @@ connection. For example: ActiveRecord::Base.connection.close
362
489
  #
363
490
  # Normally there is only a single ConnectionHandler instance, accessible via
364
491
  # ActiveRecord::Base.connection_handler. Active Record models use this to
365
- # determine that connection pool that they should use.
492
+ # determine the connection pool that they should use.
366
493
  class ConnectionHandler
367
- attr_reader :connection_pools
494
+ def initialize
495
+ # These caches are keyed by klass.name, NOT klass. Keying them by klass
496
+ # alone would lead to memory leaks in development mode as all previous
497
+ # instances of the class would stay in memory.
498
+ @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
499
+ h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
500
+ end
501
+ @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
502
+ h[k] = ThreadSafe::Cache.new
503
+ end
504
+ end
505
+
506
+ def connection_pool_list
507
+ owner_to_pool.values.compact
508
+ end
368
509
 
369
- def initialize(pools = {})
370
- @connection_pools = pools
371
- @class_to_pool = {}
510
+ def connection_pools
511
+ ActiveSupport::Deprecation.warn(
512
+ "In the next release, this will return the same as #connection_pool_list. " \
513
+ "(An array of pools, rather than a hash mapping specs to pools.)"
514
+ )
515
+ Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
372
516
  end
373
517
 
374
- def establish_connection(name, spec)
375
- @connection_pools[spec] ||= ConnectionAdapters::ConnectionPool.new(spec)
376
- @class_to_pool[name] = @connection_pools[spec]
518
+ def establish_connection(owner, spec)
519
+ @class_to_pool.clear
520
+ raise RuntimeError, "Anonymous class is not allowed." unless owner.name
521
+ owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
377
522
  end
378
523
 
379
524
  # Returns true if there are any active connections among the connection
380
525
  # pools that the ConnectionHandler is managing.
381
526
  def active_connections?
382
- connection_pools.values.any? { |pool| pool.active_connection? }
527
+ connection_pool_list.any?(&:active_connection?)
383
528
  end
384
529
 
385
- # Returns any connections in use by the current thread back to the pool.
530
+ # Returns any connections in use by the current thread back to the pool,
531
+ # and also returns connections to the pool cached by threads that are no
532
+ # longer alive.
386
533
  def clear_active_connections!
387
- @connection_pools.each_value {|pool| pool.release_connection }
534
+ connection_pool_list.each(&:release_connection)
388
535
  end
389
536
 
390
537
  # Clears the cache which maps classes.
391
538
  def clear_reloadable_connections!
392
- @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
539
+ connection_pool_list.each(&:clear_reloadable_connections!)
393
540
  end
394
541
 
395
542
  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! }
543
+ connection_pool_list.each(&:disconnect!)
402
544
  end
403
545
 
404
546
  # Locate the connection of the nearest super class. This can be an
@@ -421,54 +563,65 @@ connection. For example: ActiveRecord::Base.connection.close
421
563
  # connection and the defined connection (if they exist). The result
422
564
  # can be used as an argument for establish_connection, for easily
423
565
  # 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
566
+ def remove_connection(owner)
567
+ if pool = owner_to_pool.delete(owner.name)
568
+ @class_to_pool.clear
569
+ pool.automatic_reconnect = false
570
+ pool.disconnect!
571
+ pool.spec.config
572
+ end
432
573
  end
433
574
 
575
+ # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
576
+ # This makes retrieving the connection pool O(1) once the process is warm.
577
+ # When a connection is established or removed, we invalidate the cache.
578
+ #
579
+ # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
580
+ # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
581
+ # #fetch is significantly slower than #[]. So in the nil case, no caching will
582
+ # take place, but that's ok since the nil case is not the common one that we wish
583
+ # to optimise for.
434
584
  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
585
+ class_to_pool[klass.name] ||= begin
586
+ until pool = pool_for(klass)
587
+ klass = klass.superclass
588
+ break unless klass <= Base
589
+ end
445
590
 
446
- def initialize(body, testing = false)
447
- @body = body
448
- @testing = testing
591
+ class_to_pool[klass.name] = pool
449
592
  end
593
+ end
450
594
 
451
- def method_missing(method_sym, *arguments, &block)
452
- @body.send(method_sym, *arguments, &block)
453
- end
595
+ private
454
596
 
455
- def respond_to?(method_sym, include_private = false)
456
- super || @body.respond_to?(method_sym)
457
- end
597
+ def owner_to_pool
598
+ @owner_to_pool[Process.pid]
599
+ end
458
600
 
459
- def each(&block)
460
- body.each(&block)
461
- end
601
+ def class_to_pool
602
+ @class_to_pool[Process.pid]
603
+ end
462
604
 
463
- def close
464
- body.close if body.respond_to?(:close)
605
+ def pool_for(owner)
606
+ owner_to_pool.fetch(owner.name) {
607
+ if ancestor_pool = pool_from_any_process_for(owner)
608
+ # A connection was established in an ancestor process that must have
609
+ # subsequently forked. We can't reuse the connection, but we can copy
610
+ # the specification and establish a new connection with it.
611
+ establish_connection owner, ancestor_pool.spec
612
+ else
613
+ owner_to_pool[owner.name] = nil
614
+ end
615
+ }
616
+ end
465
617
 
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
618
+ def pool_from_any_process_for(owner)
619
+ owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
620
+ owner_to_pool && owner_to_pool[owner.name]
470
621
  end
622
+ end
471
623
 
624
+ class ConnectionManagement
472
625
  def initialize(app)
473
626
  @app = app
474
627
  end
@@ -476,9 +629,12 @@ connection. For example: ActiveRecord::Base.connection.close
476
629
  def call(env)
477
630
  testing = env.key?('rack.test')
478
631
 
479
- status, headers, body = @app.call(env)
632
+ response = @app.call(env)
633
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
634
+ ActiveRecord::Base.clear_active_connections! unless testing
635
+ end
480
636
 
481
- [status, headers, Proxy.new(body, testing)]
637
+ response
482
638
  rescue
483
639
  ActiveRecord::Base.clear_active_connections! unless testing
484
640
  raise