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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- 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"
|
40
|
-
# before_validation(:
|
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
|
302
|
+
def create_record #:nodoc:
|
268
303
|
run_callbacks(:create) { super }
|
269
304
|
end
|
270
305
|
|
271
|
-
def
|
306
|
+
def update_record(*) #:nodoc:
|
272
307
|
run_callbacks(:update) { super }
|
273
308
|
end
|
274
309
|
end
|
@@ -1,12 +1,9 @@
|
|
1
|
-
|
2
|
-
# :stopdoc:
|
3
|
-
module Coders
|
4
|
-
class YAMLColumn
|
5
|
-
RESCUE_ERRORS = [ ArgumentError ]
|
1
|
+
require 'yaml'
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
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
|
-
# * +
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
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
|
-
|
80
|
-
@
|
81
|
-
|
82
|
-
@
|
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
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
163
|
-
|
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
|
177
|
-
|
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
|
226
|
-
# a new connection
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
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
|
-
|
241
|
-
|
242
|
-
|
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
|
-
|
297
|
-
|
298
|
-
def
|
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
|
-
|
391
|
+
@connections.delete conn
|
392
|
+
@available.delete conn
|
301
393
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
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
|
-
|
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
|
-
|
446
|
+
Base.send(spec.adapter_method, spec.config)
|
316
447
|
end
|
317
448
|
|
318
449
|
def current_connection_id #:nodoc:
|
319
|
-
|
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
|
492
|
+
# determine the connection pool that they should use.
|
366
493
|
class ConnectionHandler
|
367
|
-
|
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
|
370
|
-
|
371
|
-
|
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(
|
375
|
-
@
|
376
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
539
|
+
connection_pool_list.each(&:clear_reloadable_connections!)
|
393
540
|
end
|
394
541
|
|
395
542
|
def clear_all_connections!
|
396
|
-
|
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(
|
425
|
-
pool =
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
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
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
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
|
-
|
447
|
-
@body = body
|
448
|
-
@testing = testing
|
591
|
+
class_to_pool[klass.name] = pool
|
449
592
|
end
|
593
|
+
end
|
450
594
|
|
451
|
-
|
452
|
-
@body.send(method_sym, *arguments, &block)
|
453
|
-
end
|
595
|
+
private
|
454
596
|
|
455
|
-
|
456
|
-
|
457
|
-
|
597
|
+
def owner_to_pool
|
598
|
+
@owner_to_pool[Process.pid]
|
599
|
+
end
|
458
600
|
|
459
|
-
|
460
|
-
|
461
|
-
|
601
|
+
def class_to_pool
|
602
|
+
@class_to_pool[Process.pid]
|
603
|
+
end
|
462
604
|
|
463
|
-
|
464
|
-
|
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
|
-
|
467
|
-
|
468
|
-
|
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
|
-
|
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
|
-
|
637
|
+
response
|
482
638
|
rescue
|
483
639
|
ActiveRecord::Base.clear_active_connections! unless testing
|
484
640
|
raise
|