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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- 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 +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- 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 +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- 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/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- 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 +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -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/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- 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,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,21 @@ 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
|
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
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
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!
|
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
|
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
|
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
|
226
|
-
# a new connection
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
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
|
-
|
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
|
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
|
-
|
297
|
-
|
298
|
-
def
|
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
|
-
|
383
|
+
@connections.delete conn
|
384
|
+
@available.delete conn
|
301
385
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
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
|
-
|
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
|
-
|
440
|
+
Base.send(spec.adapter_method, spec.config)
|
316
441
|
end
|
317
442
|
|
318
443
|
def current_connection_id #:nodoc:
|
319
|
-
|
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
|
486
|
+
# determine the connection pool that they should use.
|
366
487
|
class ConnectionHandler
|
367
|
-
|
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
|
370
|
-
|
371
|
-
|
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(
|
375
|
-
@
|
376
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
533
|
+
connection_pool_list.each(&:clear_reloadable_connections!)
|
393
534
|
end
|
394
535
|
|
395
536
|
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! }
|
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(
|
425
|
-
pool =
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
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
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
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
|
-
|
447
|
-
@body = body
|
448
|
-
@testing = testing
|
585
|
+
class_to_pool[klass.name] = pool
|
449
586
|
end
|
587
|
+
end
|
450
588
|
|
451
|
-
|
452
|
-
@body.send(method_sym, *arguments, &block)
|
453
|
-
end
|
589
|
+
private
|
454
590
|
|
455
|
-
|
456
|
-
|
457
|
-
|
591
|
+
def owner_to_pool
|
592
|
+
@owner_to_pool[Process.pid]
|
593
|
+
end
|
458
594
|
|
459
|
-
|
460
|
-
|
461
|
-
|
595
|
+
def class_to_pool
|
596
|
+
@class_to_pool[Process.pid]
|
597
|
+
end
|
462
598
|
|
463
|
-
|
464
|
-
|
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
|
-
|
467
|
-
|
468
|
-
|
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
|
-
|
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
|
-
|
631
|
+
response
|
482
632
|
rescue
|
483
633
|
ActiveRecord::Base.clear_active_connections! unless testing
|
484
634
|
raise
|