Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 972 lines (863 sloc) 36.167 kB
d869298 @raggi Don't depend on rubygems loading thread (for Mutex)
raggi authored
1 require 'thread'
45448a5 @thedarkone Replace some global Hash usages with the new thread safe cache.
thedarkone authored
2 require 'thread_safe'
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
3 require 'monitor'
50cd4bd @nicksieger Introduce synchronization around connection pool access
nicksieger authored
4
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
5 module ActiveRecord
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
6 # Raised when a connection could not be obtained within the connection
5b7cfc5 @jrochkind ConnectionPool, unify exceptions, ConnectionTimeoutError
jrochkind authored
7 # acquisition timeout period: because max connections in pool
a30b8d3 rename AR::Model::Tag to AR::Tag - fixes #7714
Francesco Rodriguez authored
8 # are in use.
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
9 class ConnectionTimeoutError < ConnectionNotEstablished
10 end
11
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
12 # Raised when a pool was unable to get ahold of all its connections
13 # to perform a "group" action such as +ConnectionPool#disconnect!+
14 # or +ConnectionPool#clear_reloadable_connections!+.
15 class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
16 end
17
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
18 module ConnectionAdapters
f17159b @fxn edit pass: the names of Rails components have a space, ie, "Active Re…
fxn authored
19 # Connection pool base class for managing Active Record database
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
20 # connections.
21 #
a293278 @lifo Merge docrails
lifo authored
22 # == Introduction
23 #
24 # A connection pool synchronizes thread access to a limited number of
25 # database connections. The basic idea is that each thread checks out a
26 # database connection from the pool, uses that connection, and checks the
27 # connection back in. ConnectionPool is completely thread-safe, and will
28 # ensure that a connection cannot be used by two threads at the same time,
29 # as long as ConnectionPool's contract is correctly followed. It will also
30 # handle cases in which there are more threads than connections: if all
31 # connections have been checked out, and a thread tries to checkout a
32 # connection anyway, then ConnectionPool will wait until some other thread
33 # has checked in a connection.
34 #
35 # == Obtaining (checking out) a connection
36 #
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
37 # Connections can be obtained and used from a connection pool in several
38 # ways:
39 #
f17159b @fxn edit pass: the names of Rails components have a space, ie, "Active Re…
fxn authored
40 # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
817a07b @nicksieger More doco and class/method renames. Now have a strategy for integrati…
nicksieger authored
41 # earlier (pre-connection-pooling). Eventually, when you're done with
42 # the connection(s) and wish it to be returned to the pool, you call
43 # ActiveRecord::Base.clear_active_connections!. This will be the
f17159b @fxn edit pass: the names of Rails components have a space, ie, "Active Re…
fxn authored
44 # default behavior for Active Record when used in conjunction with
45 # Action Pack's request handling cycle.
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
46 # 2. Manually check out a connection from the pool with
47 # ActiveRecord::Base.connection_pool.checkout. You are responsible for
48 # returning this connection to the pool when finished by calling
49 # ActiveRecord::Base.connection_pool.checkin(connection).
50 # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
51 # obtains a connection, yields it as the sole argument to the block,
52 # and returns it to the pool after the block completes.
8e5e02b @nicksieger Collapse connection pool class hierarchy; YAGNI.
nicksieger authored
53 #
a293278 @lifo Merge docrails
lifo authored
54 # Connections in the pool are actually AbstractAdapter objects (or objects
55 # compatible with AbstractAdapter's interface).
56 #
57 # == Options
58 #
cb6f839 @jrochkind ConnectionPool wait_timeout no longer used for different types of tim…
jrochkind authored
59 # There are several connection-pooling-related options that you can add to
8e5e02b @nicksieger Collapse connection pool class hierarchy; YAGNI.
nicksieger authored
60 # your database connection configuration:
61 #
62 # * +pool+: number indicating size of connection pool (default 5)
cb6f839 @jrochkind ConnectionPool wait_timeout no longer used for different types of tim…
jrochkind authored
63 # * +checkout_timeout+: number of seconds to block and wait for a connection
8e5e02b @nicksieger Collapse connection pool class hierarchy; YAGNI.
nicksieger authored
64 # before giving up and raising a timeout error (default 5 seconds).
82b05fb @rafaelfranca Whitespaces :scissors:
rafaelfranca authored
65 # * +reaping_frequency+: frequency in seconds to periodically run the
9e457a8 @matthewd Reap connections based on owning-thread death
matthewd authored
66 # Reaper, which attempts to find and recover connections from dead
67 # threads, which can occur if a programmer forgets to close a
68 # connection at the end of a thread or a thread dies unexpectedly.
69 # Regardless of this setting, the Reaper will be invoked before every
70 # blocking wait. (Default nil, which means don't schedule the Reaper).
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
71 #
72 #--
73 # Synchronization policy:
74 # * all public methods can be called outside +synchronize+
75 # * access to these i-vars needs to be in +synchronize+:
76 # * @connections
77 # * @now_connecting
78 # * private methods that require being called in a +synchronize+ blocks
79 # are now explicitly documented
817a07b @nicksieger More doco and class/method renames. Now have a strategy for integrati…
nicksieger authored
80 class ConnectionPool
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
81 # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
82 # with which it shares a Monitor. But could be a generic Queue.
83 #
84 # The Queue in stdlib's 'thread' could replace this class except
85 # stdlib's doesn't support waiting with a timeout.
86 class Queue
87 def initialize(lock = Monitor.new)
88 @lock = lock
89 @cond = @lock.new_cond
90 @num_waiting = 0
91 @queue = []
92 end
93
94 # Test if any threads are currently waiting on the queue.
95 def any_waiting?
96 synchronize do
97 @num_waiting > 0
98 end
99 end
100
b1b9a0a @laurocaetano Typos. return -> returns. [ci skip]
laurocaetano authored
101 # Returns the number of threads currently waiting on this
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
102 # queue.
103 def num_waiting
104 synchronize do
105 @num_waiting
106 end
107 end
108
109 # Add +element+ to the queue. Never blocks.
110 def add(element)
111 synchronize do
112 @queue.push element
113 @cond.signal
114 end
115 end
116
117 # If +element+ is in the queue, remove and return it, or nil.
118 def delete(element)
119 synchronize do
120 @queue.delete(element)
121 end
122 end
123
124 # Remove all elements from the queue.
125 def clear
126 synchronize do
127 @queue.clear
128 end
129 end
130
131 # Remove the head of the queue.
132 #
133 # If +timeout+ is not given, remove and return the head the
134 # queue if the number of available elements is strictly
135 # greater than the number of threads currently waiting (that
136 # is, don't jump ahead in line). Otherwise, return nil.
137 #
6125221 @neerajdotname minor sentences fixes
neerajdotname authored
138 # If +timeout+ is given, block if there is no element
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
139 # available, waiting up to +timeout+ seconds for an element to
140 # become available.
141 #
142 # Raises:
143 # - ConnectionTimeoutError if +timeout+ is given and no element
6125221 @neerajdotname minor sentences fixes
neerajdotname authored
144 # becomes available within +timeout+ seconds,
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
145 def poll(timeout = nil)
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
146 synchronize { internal_poll(timeout) }
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
147 end
148
149 private
150
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
151 def internal_poll(timeout)
152 no_wait_poll || (timeout && wait_poll(timeout))
153 end
154
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
155 def synchronize(&block)
156 @lock.synchronize(&block)
157 end
158
159 # Test if the queue currently contains any elements.
160 def any?
161 !@queue.empty?
162 end
163
164 # A thread can remove an element from the queue without
6125221 @neerajdotname minor sentences fixes
neerajdotname authored
165 # waiting if and only if the number of currently available
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
166 # connections is strictly greater than the number of waiting
167 # threads.
168 def can_remove_no_wait?
169 @queue.size > @num_waiting
170 end
171
172 # Removes and returns the head of the queue if possible, or nil.
173 def remove
174 @queue.shift
175 end
176
177 # Remove and return the head the queue if the number of
178 # available elements is strictly greater than the number of
179 # threads currently waiting. Otherwise, return nil.
180 def no_wait_poll
181 remove if can_remove_no_wait?
182 end
183
184 # Waits on the queue up to +timeout+ seconds, then removes and
185 # returns the head of the queue.
186 def wait_poll(timeout)
187 @num_waiting += 1
188
189 t0 = Time.now
190 elapsed = 0
191 loop do
192 @cond.wait(timeout - elapsed)
193
194 return remove if any?
195
196 elapsed = Time.now - t0
5b7cfc5 @jrochkind ConnectionPool, unify exceptions, ConnectionTimeoutError
jrochkind authored
197 if elapsed >= timeout
198 msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
199 [timeout, elapsed]
200 raise ConnectionTimeoutError, msg
201 end
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
202 end
203 ensure
204 @num_waiting -= 1
205 end
206 end
207
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
208 # Adds the ability to turn a basic fair FIFO queue into one
209 # biased to some thread.
210 module BiasableQueue # :nodoc:
211 class BiasedConditionVariable # :nodoc:
212 # semantics of condition variables guarantee that +broadcast+, +broadcast_on_biased+,
213 # +signal+ and +wait+ methods are only called while holding a lock
214 def initialize(lock, other_cond, preferred_thread)
215 @real_cond = lock.new_cond
216 @other_cond = other_cond
217 @preferred_thread = preferred_thread
218 @num_waiting_on_real_cond = 0
219 end
220
221 def broadcast
222 broadcast_on_biased
223 @other_cond.broadcast
224 end
225
226 def broadcast_on_biased
227 @num_waiting_on_real_cond = 0
228 @real_cond.broadcast
229 end
230
231 def signal
232 if @num_waiting_on_real_cond > 0
233 @num_waiting_on_real_cond -= 1
234 @real_cond
235 else
236 @other_cond
237 end.signal
238 end
239
240 def wait(timeout)
241 if Thread.current == @preferred_thread
242 @num_waiting_on_real_cond += 1
243 @real_cond
244 else
245 @other_cond
246 end.wait(timeout)
247 end
248 end
249
250 def with_a_bias_for(thread)
251 previous_cond = nil
252 new_cond = nil
253 synchronize do
254 previous_cond = @cond
255 @cond = new_cond = BiasedConditionVariable.new(@lock, @cond, thread)
256 end
257 yield
258 ensure
259 synchronize do
260 @cond = previous_cond if previous_cond
261 new_cond.broadcast_on_biased if new_cond # wake up any remaining sleepers
262 end
263 end
264 end
265
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
266 # Connections must be leased while holding the main pool mutex. This is
267 # an internal subclass that also +.leases+ returned connections while
268 # still in queue's critical section (queue synchronizes with the same
269 # +@lock+ as the main pool) so that a returned connection is already
270 # leased and there is no need to re-enter synchronized block.
271 class ConnectionLeasingQueue < Queue # :nodoc:
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
272 include BiasableQueue
273
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
274 private
275 def internal_poll(timeout)
276 conn = super
277 conn.lease if conn
278 conn
279 end
280 end
281
41c24eb @tenderlove each connection pool has a reaper
tenderlove authored
282 # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
283 # A reaper instantiated with a nil frequency will never reap the
284 # connection pool.
641b43e @tenderlove updating the reaping frequency documentation
tenderlove authored
285 #
286 # Configure the frequency by setting "reaping_frequency" in your
82b05fb @rafaelfranca Whitespaces :scissors:
rafaelfranca authored
287 # database yaml file.
cde7692 @tenderlove introduce a timer class for reaping connections
tenderlove authored
288 class Reaper
289 attr_reader :pool, :frequency
290
291 def initialize(pool, frequency)
292 @pool = pool
293 @frequency = frequency
294 end
295
59f2696 @tenderlove rename start to run and use Thread.pass rather than sleeping to sched…
tenderlove authored
296 def run
cde7692 @tenderlove introduce a timer class for reaping connections
tenderlove authored
297 return unless frequency
298 Thread.new(frequency, pool) { |t, p|
299 while true
300 sleep t
301 p.reap
302 end
303 }
304 end
305 end
306
c606fe2 @tenderlove push synchronization in to each method. Reduces method calls and makes
tenderlove authored
307 include MonitorMixin
308
33fe7cc @eugeneius Apply schema cache dump when creating connections
eugeneius authored
309 attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
41c24eb @tenderlove each connection pool has a reaper
tenderlove authored
310 attr_reader :spec, :connections, :size, :reaper
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
311
a293278 @lifo Merge docrails
lifo authored
312 # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
313 # object which describes database connection information (e.g. adapter,
314 # host name, username, password, etc), as well as the maximum size for
315 # this ConnectionPool.
316 #
317 # The default ConnectionPool maximum size is 5.
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
318 def initialize(spec)
c606fe2 @tenderlove push synchronization in to each method. Reduces method calls and makes
tenderlove authored
319 super()
320
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
321 @spec = spec
dd77733 @jeremy Timeout the connection pool monitor on ruby 1.8 only
jeremy authored
322
dfeeecd @rafaelfranca Merge pull request #16542 from seamusabshere/numerify-pool-checkout-t…
rafaelfranca authored
323 @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
27955b4 @korbin fix issue with reaping_frequency type
korbin authored
324 @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
59f2696 @tenderlove rename start to run and use Thread.pass rather than sleeping to sched…
tenderlove authored
325 @reaper.run
dd77733 @jeremy Timeout the connection pool monitor on ruby 1.8 only
jeremy authored
326
8e5e02b @nicksieger Collapse connection pool class hierarchy; YAGNI.
nicksieger authored
327 # default max pool size to 5
328 @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
dd77733 @jeremy Timeout the connection pool monitor on ruby 1.8 only
jeremy authored
329
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
330 # The cache of threads mapped to reserved connections, the sole purpose
331 # of the cache is to speed-up +connection+ method, it is not the authoritative
332 # registry of which thread owns which connection, that is tracked by
333 # +connection.owner+ attr on each +connection+ instance.
334 # The invariant works like this: if there is mapping of +thread => conn+,
335 # then that +thread+ does indeed own that +conn+, however an absence of a such
336 # mapping does not mean that the +thread+ doesn't own the said connection, in
337 # that case +conn.owner+ attr should be consulted.
338 # Access and modification of +@thread_cached_conns+ does not require
339 # synchronization.
340 @thread_cached_conns = ThreadSafe::Cache.new(:initial_capacity => @size)
45448a5 @thedarkone Replace some global Hash usages with the new thread safe cache.
thedarkone authored
341
7db90aa @jonleighton Make it the responsibility of the connection to hold onto an ARel vis…
jonleighton authored
342 @connections = []
acccb72 @tenderlove column cache now lives on the connection pool
tenderlove authored
343 @automatic_reconnect = true
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
344
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
345 # Connection pool allows for concurrent (outside the main `synchronize` section)
346 # establishment of new connections. This variable tracks the number of threads
347 # currently in the process of independently establishing connections to the DB.
348 @now_connecting = 0
349
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
350 # A boolean toggle that allows/disallows new connections.
351 @new_cons_enabled = true
352
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
353 @available = ConnectionLeasingQueue.new self
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
354 end
355
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
356 # Retrieve the connection associated with the current thread, or call
357 # #checkout to obtain one if necessary.
358 #
359 # #connection can be called any number of times; the connection is
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
360 # held in a cache keyed by a thread.
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
361 def connection
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
362 @thread_cached_conns[connection_cache_key(Thread.current)] ||= checkout
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
363 end
364
d523504 @tenderlove make active_connection? return true only if there is an open connecti…
tenderlove authored
365 # Is there an open connection that is being used for the current thread?
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
366 #
367 # This method only works for connections that have been abtained through
368 # #connection or #with_connection methods, connections obtained through
369 # #checkout will not be detected by #active_connection?
4211866 @tenderlove adding active_connection? to the connection pool
tenderlove authored
370 def active_connection?
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
371 @thread_cached_conns[connection_cache_key(Thread.current)]
4211866 @tenderlove adding active_connection? to the connection pool
tenderlove authored
372 end
373
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
374 # Signal that the thread is finished with the current connection.
817a07b @nicksieger More doco and class/method renames. Now have a strategy for integrati…
nicksieger authored
375 # #release_connection releases the connection-thread association
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
376 # and returns the connection to the pool.
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
377 #
378 # This method only works for connections that have been obtained through
379 # #connection or #with_connection methods, connections obtained through
380 # #checkout will not be automatically released.
381 def release_connection(owner_thread = Thread.current)
382 if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
383 checkin conn
b8f7482 @tenderlove opening a connection will block if the pool is full
tenderlove authored
384 end
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
385 end
386
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
387 # If a connection obtained through #connection or #with_connection methods
388 # already exists yield it to the block. If no such connection
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -ex…
spastorino authored
389 # exists checkout a connection, yield it to the block, and checkin the
5501b99 @account-settings Ensure ActiveRecord::Base.connection_pool.with_connection creates a n…
account-settings authored
390 # connection when finished.
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
391 def with_connection
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
392 unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
393 conn = connection
394 fresh_connection = true
395 end
396 yield conn
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
397 ensure
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
398 release_connection if fresh_connection
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
399 end
400
029952e @nicksieger Extract a base class for connection pools, start to flesh out reserve…
nicksieger authored
401 # Returns true if a connection has already been opened.
402 def connected?
c606fe2 @tenderlove push synchronization in to each method. Reduces method calls and makes
tenderlove authored
403 synchronize { @connections.any? }
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
404 end
405
a293278 @lifo Merge docrails
lifo authored
406 # Disconnects all connections in the pool, and clears the pool.
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
407 #
408 # Raises:
409 # - +ExclusiveConnectionTimeoutError+ if unable to gain ownership of all
410 # connections in the pool within a timeout interval (default duration is
411 # +spec.config[:checkout_timeout] * 2+ seconds).
412 def disconnect(raise_on_acquisition_timeout = true)
413 with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
414 synchronize do
415 @connections.each do |conn|
416 checkin conn
417 conn.disconnect!
418 end
419 @connections = []
420 @available.clear
c606fe2 @tenderlove push synchronization in to each method. Reduces method calls and makes
tenderlove authored
421 end
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
422 end
423 end
424
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
425 # Disconnects all connections in the pool, and clears the pool.
426 #
427 # The pool first tries to gain ownership of all connections, if unable to
428 # do so within a timeout interval (default duration is
429 # +spec.config[:checkout_timeout] * 2+ seconds), the pool is forcefully
430 # disconneted wihout any regard for other connection owning threads.
431 def disconnect!
432 disconnect(false)
433 end
434
435 # Clears the cache which maps classes and re-connects connections that
436 # require reloading.
437 #
438 # Raises:
439 # - +ExclusiveConnectionTimeoutError+ if unable to gain ownership of all
440 # connections in the pool within a timeout interval (default duration is
441 # +spec.config[:checkout_timeout] * 2+ seconds).
442 def clear_reloadable_connections(raise_on_acquisition_timeout = true)
443 num_new_conns_required = 0
444
445 with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
446 synchronize do
447 @connections.each do |conn|
448 checkin conn
449 conn.disconnect! if conn.requires_reloading?
450 end
451 @connections.delete_if(&:requires_reloading?)
452
453 @available.clear
454
455 if @connections.size < @size
456 # because of the pruning done by this method, we might be running
457 # low on connections, while threads stuck in queue are helpless
458 # (not being able to establish new connections for themselves),
459 # see also more detailed explanation in +remove+
460 num_new_conns_required = num_waiting_in_queue - @connections.size
461 end
462
463 @connections.each do |conn|
464 @available.add conn
465 end
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
466 end
62c4e4d @ebeigarts Fix connection reloading in development mode. [#4929 state:resolved]
ebeigarts authored
467 end
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
468
469 bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
470 end
471
472 # Clears the cache which maps classes and re-connects connections that
473 # require reloading.
474 #
475 # The pool first tries to gain ownership of all connections, if unable to
476 # do so within a timeout interval (default duration is
477 # +spec.config[:checkout_timeout] * 2+ seconds), the pool forcefully
478 # clears the cache and reloads connections without any regard for other
479 # connection owning threads.
480 def clear_reloadable_connections!
481 clear_reloadable_connections(false)
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
482 end
483
a293278 @lifo Merge docrails
lifo authored
484 # Check-out a database connection from the pool, indicating that you want
485 # to use it. You should call #checkin when you no longer need this.
486 #
cceabe0 @tenderlove raise a pull full error when the connection pool is full and no conne…
tenderlove authored
487 # This is done by either returning and leasing existing connection, or by
488 # creating a new connection and leasing it.
489 #
490 # If all connections are leased and the pool is at capacity (meaning the
491 # number of currently leased connections is greater than or equal to the
5b7cfc5 @jrochkind ConnectionPool, unify exceptions, ConnectionTimeoutError
jrochkind authored
492 # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
a293278 @lifo Merge docrails
lifo authored
493 #
494 # Returns: an AbstractAdapter object.
495 #
496 # Raises:
5b7cfc5 @jrochkind ConnectionPool, unify exceptions, ConnectionTimeoutError
jrochkind authored
497 # - ConnectionTimeoutError: no connection can be obtained from the pool.
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
498 def checkout(checkout_timeout = @checkout_timeout)
499 checkout_and_verify(acquire_connection(checkout_timeout))
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
500 end
501
a293278 @lifo Merge docrails
lifo authored
502 # Check-in a database connection back into the pool, indicating that you
503 # no longer need this connection.
504 #
505 # +conn+: an AbstractAdapter object, which was obtained by earlier by
506 # calling +checkout+ on this pool.
8e5e02b @nicksieger Collapse connection pool class hierarchy; YAGNI.
nicksieger authored
507 def checkin(conn)
c606fe2 @tenderlove push synchronization in to each method. Reduces method calls and makes
tenderlove authored
508 synchronize do
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
509 remove_connection_from_thread_cache conn
9e457a8 @matthewd Reap connections based on owning-thread death
matthewd authored
510
beb07fb @tgxworld Revert "Revert "Reduce allocations when running AR callbacks.""
tgxworld authored
511 conn._run_checkin_callbacks do
b72b477 @tenderlove Use connection lease to determine "checked_out" connections
tenderlove authored
512 conn.expire
471a394 @nicksieger Modify connection pool callbacks to be compatible w/ new style
nicksieger authored
513 end
0210c44 @tenderlove make sure connections returned after close are marked as in_use
tenderlove authored
514
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
515 @available.add conn
8e5e02b @nicksieger Collapse connection pool class hierarchy; YAGNI.
nicksieger authored
516 end
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
517 end
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
518
17ff88c @tenderlove connections can be removed from the pool
tenderlove authored
519 # Remove a connection from the connection pool. The connection will
520 # remain open and active but will no longer be managed by this pool.
521 def remove(conn)
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
522 needs_new_connection = false
523
17ff88c @tenderlove connections can be removed from the pool
tenderlove authored
524 synchronize do
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
525 remove_connection_from_thread_cache conn
526
17ff88c @tenderlove connections can be removed from the pool
tenderlove authored
527 @connections.delete conn
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
528 @available.delete conn
e060cf0 @tenderlove deal with removing connections associated with the current thread
tenderlove authored
529
a3923e6 @thedarkone AR::ConPool - reduce post checkout critical section.
thedarkone authored
530 # @available.any_waiting? => true means that prior to removing this
531 # conn, the pool was at its max size (@connections.size == @size)
532 # this would mean that any threads stuck waiting in the queue wouldn't
533 # know they could checkout_new_connection, so let's do it for them.
534 # Because condition-wait loop is encapsulated in the Queue class
535 # (that in turn is oblivious to ConnectionPool implementation), threads
536 # that are "stuck" there are helpless, they have no way of creating
537 # new connections and are completely reliant on us feeding available
538 # connections into the Queue.
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
539 needs_new_connection = @available.any_waiting?
540 end
541
542 # This is intentionally done outside of the synchronized section as we
543 # would like not to hold the main mutex while checking out new connections,
544 # thus there is some chance that needs_new_connection information is now
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
545 # stale, we can live with that (bulk_make_new_connections will make
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
546 # sure not to exceed the pool's @size limit).
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
547 bulk_make_new_connections(1) if needs_new_connection
17ff88c @tenderlove connections can be removed from the pool
tenderlove authored
548 end
549
9e457a8 @matthewd Reap connections based on owning-thread death
matthewd authored
550 # Recover lost connections for the pool. A lost connection can occur if
551 # a programmer forgets to checkin a connection at the end of a thread
86729eb @tenderlove connections can be reaped via the `reap` method
tenderlove authored
552 # or a thread dies unexpectedly.
553 def reap
9e457a8 @matthewd Reap connections based on owning-thread death
matthewd authored
554 stale_connections = synchronize do
555 @connections.select do |conn|
556 conn.in_use? && !conn.owner.alive?
557 end
558 end
559
560 stale_connections.each do |conn|
561 synchronize do
562 if conn.active?
563 conn.reset!
564 checkin conn
565 else
34c7e73 @tenderlove use `connect_poll` on pg so that reaping does not hurt the connection
tenderlove authored
566 remove conn
567 end
86729eb @tenderlove connections can be reaped via the `reap` method
tenderlove authored
568 end
569 end
570 end
571
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
572 def num_waiting_in_queue # :nodoc:
573 @available.num_waiting
574 end
575
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
576 private
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
577 #--
578 # this is unfortunately not concurrent
579 def bulk_make_new_connections(num_new_conns_needed)
580 num_new_conns_needed.times do
581 # try_to_checkout_new_connection will not exceed pool's @size limit
582 if new_conn = try_to_checkout_new_connection
583 # make the new_conn available to the starving threads stuck @available Queue
584 checkin(new_conn)
585 end
586 end
587 end
588
589 #--
590 # From the discussion on Github:
591 # https://github.com/rails/rails/pull/14938#commitcomment-6601951
592 # This hook-in method allows for easier monkey-patching fixes needed by
593 # JRuby users that use Fibers.
594 def connection_cache_key(thread)
595 thread
596 end
597
598 # Take control of all existing connections so a "group" action such as
599 # reload/disconnect can be performed safely. It is no longer enough to
600 # wrap it in +synchronize+ because some pool's actions are allowed
601 # to be performed outside of the main +synchronize+ block.
602 def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
603 with_new_connections_blocked do
604 attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
605 yield
606 end
607 end
608
609 def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
610 collected_conns = synchronize do
611 # account for our own connections
612 @connections.select {|conn| conn.owner == Thread.current}
613 end
614
615 newly_checked_out = []
616 timeout_time = Time.now + (@checkout_timeout * 2)
617
618 @available.with_a_bias_for(Thread.current) do
619 while true
620 synchronize do
621 return if collected_conns.size == @connections.size && @now_connecting == 0
622 remaining_timeout = timeout_time - Time.now
623 remaining_timeout = 0 if remaining_timeout < 0
624 conn = checkout_for_exclusive_access(remaining_timeout)
625 collected_conns << conn
626 newly_checked_out << conn
627 end
628 end
629 end
630 rescue ExclusiveConnectionTimeoutError
631 # `raise_on_acquisition_timeout == false` means we are directed to ignore any
632 # timeouts and are expected to just give up: we've obtained as many connections
633 # as possible, note that in a case like that we don't return any of the
634 # `newly_checked_out` connections.
635
636 if raise_on_acquisition_timeout
637 release_newly_checked_out = true
638 raise
639 end
640 rescue Exception # if something else went wrong
641 # this can't be a "naked" rescue, because we have should return conns
642 # even for non-StandardErrors
643 release_newly_checked_out = true
644 raise
645 ensure
646 if release_newly_checked_out && newly_checked_out
647 # releasing only those conns that were checked out in this method, conns
648 # checked outside this method (before it was called) are not for us to release
649 newly_checked_out.each {|conn| checkin(conn)}
650 end
651 end
652
653 #--
654 # Must be called in a synchronize block.
655 def checkout_for_exclusive_access(checkout_timeout)
656 checkout(checkout_timeout)
657 rescue ConnectionTimeoutError
658 # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
659 # rescue block, because doing so would put it outside of synchronize section, without
660 # being in a critical section thread_report might become inaccurate
661 msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
662
663 thread_report = []
664 @connections.each do |conn|
665 unless conn.owner == Thread.current
666 thread_report << "#{conn} is owned by #{conn.owner}"
667 end
668 end
669
670 msg << " (#{thread_report.join(', ')})" if thread_report.any?
671
672 raise ExclusiveConnectionTimeoutError, msg
673 end
674
675 def with_new_connections_blocked
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
676 previous_value = nil
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
677 synchronize do
678 previous_value, @new_cons_enabled = @new_cons_enabled, false
a3923e6 @thedarkone AR::ConPool - reduce post checkout critical section.
thedarkone authored
679 end
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
680 yield
681 ensure
682 synchronize { @new_cons_enabled = previous_value }
a3923e6 @thedarkone AR::ConPool - reduce post checkout critical section.
thedarkone authored
683 end
7db90aa @jonleighton Make it the responsibility of the connection to hold onto an ARel vis…
jonleighton authored
684
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
685 # Acquire a connection by one of 1) immediately removing one
686 # from the queue of available connections, 2) creating a new
687 # connection if the pool is not at capacity, 3) waiting on the
688 # queue for a connection to become available.
689 #
690 # Raises:
a30b8d3 rename AR::Model::Tag to AR::Tag - fixes #7714
Francesco Rodriguez authored
691 # - ConnectionTimeoutError if a connection could not be acquired
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
692 #
693 #--
694 # Implementation detail: the connection returned by +acquire_connection+
695 # will already be "+connection.lease+ -ed" to the current thread.
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
696 def acquire_connection(checkout_timeout)
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
697 # NOTE: we rely on `@available.poll` and `try_to_checkout_new_connection` to
698 # `conn.lease` the returned connection (and to do this in a `synchronized`
699 # section), this is not the cleanest implementation, as ideally we would
700 # `synchronize { conn.lease }` in this method, but by leaving it to `@available.poll`
701 # and `try_to_checkout_new_connection` we can piggyback on `synchronize` sections
702 # of the said methods and avoid an additional `synchronize` overhead.
703 if conn = @available.poll || try_to_checkout_new_connection
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
704 conn
705 else
9e457a8 @matthewd Reap connections based on owning-thread death
matthewd authored
706 reap
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
707 @available.poll(checkout_timeout)
02b2335 @pmahoney Make connection pool fair with respect to waiting threads.
pmahoney authored
708 end
709 end
710
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
711 #--
712 # if owner_thread param is omitted, this must be called in synchronize block
713 def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
714 @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
0210c44 @tenderlove make sure connections returned after close are marked as in_use
tenderlove authored
715 end
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
716 alias_method :release, :remove_connection_from_thread_cache
0210c44 @tenderlove make sure connections returned after close are marked as in_use
tenderlove authored
717
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
718 def new_connection
33fe7cc @eugeneius Apply schema cache dump when creating connections
eugeneius authored
719 Base.send(spec.adapter_method, spec.config).tap do |conn|
720 conn.schema_cache = schema_cache.dup if schema_cache
721 end
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
722 end
723
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
724 # If the pool is not at a +@size+ limit, establish new connection. Connecting
725 # to the DB is done outside main synchronized section.
726 #--
727 # Implementation constraint: a newly established connection returned by this
728 # method must be in the +.leased+ state.
729 def try_to_checkout_new_connection
730 # first in synchronized section check if establishing new conns is allowed
731 # and increment @now_connecting, to prevent overstepping this pool's @size
732 # constraint
733 do_checkout = synchronize do
603fe20 @thedarkone AR::ConPool - remove synchronization around connection cache.
thedarkone authored
734 if @new_cons_enabled && (@connections.size + @now_connecting) < @size
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
735 @now_connecting += 1
736 end
737 end
738 if do_checkout
739 begin
740 # if successfully incremented @now_connecting establish new connection
741 # outside of synchronized section
742 conn = checkout_new_connection
743 ensure
744 synchronize do
745 if conn
746 adopt_connection(conn)
747 # returned conn needs to be already leased
748 conn.lease
749 end
750 @now_connecting -= 1
751 end
752 end
753 end
754 end
755
756 def adopt_connection(conn)
757 conn.pool = self
758 @connections << conn
759 end
760
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
761 def checkout_new_connection
acccb72 @tenderlove column cache now lives on the connection pool
tenderlove authored
762 raise ConnectionNotEstablished unless @automatic_reconnect
e92f5a9 @thedarkone AR::ConPool - establish connections outside of critical section.
thedarkone authored
763 new_connection
a96b7d4 @nicksieger Add connection reset and verification upon each connection checkout
nicksieger authored
764 end
765
766 def checkout_and_verify(c)
beb07fb @tgxworld Revert "Revert "Reduce allocations when running AR callbacks.""
tgxworld authored
767 c._run_checkout_callbacks do
471a394 @nicksieger Modify connection pool callbacks to be compatible w/ new style
nicksieger authored
768 c.verify!
769 end
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
770 c
205a561 @yoshiokatsuneo ActiveRecord: release connection on reconnect failure.
yoshiokatsuneo authored
771 rescue
33fdb7f @yoshiokatsuneo ActiveRecord: On reconnection failure, release only failed connetion.
yoshiokatsuneo authored
772 remove c
773 c.disconnect!
205a561 @yoshiokatsuneo ActiveRecord: release connection on reconnect failure.
yoshiokatsuneo authored
774 raise
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
775 end
776 end
777
a293278 @lifo Merge docrails
lifo authored
778 # ConnectionHandler is a collection of ConnectionPool objects. It is used
f17159b @fxn edit pass: the names of Rails components have a space, ie, "Active Re…
fxn authored
779 # for keeping separate connection pools for Active Record models that connect
a293278 @lifo Merge docrails
lifo authored
780 # to different databases.
781 #
782 # For example, suppose that you have 5 models, with the following hierarchy:
783 #
38be633 @JackDanger Clarify how the ConnectionHandler works
JackDanger authored
784 # class Author < ActiveRecord::Base
785 # end
a293278 @lifo Merge docrails
lifo authored
786 #
38be633 @JackDanger Clarify how the ConnectionHandler works
JackDanger authored
787 # class BankAccount < ActiveRecord::Base
788 # end
a293278 @lifo Merge docrails
lifo authored
789 #
38be633 @JackDanger Clarify how the ConnectionHandler works
JackDanger authored
790 # class Book < ActiveRecord::Base
791 # establish_connection "library_db"
792 # end
793 #
794 # class ScaryBook < Book
795 # end
796 #
797 # class GoodBook < Book
798 # end
799 #
800 # And a database.yml that looked like this:
801 #
802 # development:
803 # database: my_application
804 # host: localhost
805 #
806 # library_db:
807 # database: library
808 # host: some.library.org
809 #
810 # Your primary database in the development environment is "my_application"
811 # but the Book model connects to a separate database called "library_db"
812 # (this can even be a database on a different machine).
813 #
814 # Book, ScaryBook and GoodBook will all use the same connection pool to
815 # "library_db" while Author, BankAccount, and any other models you create
816 # will use the default connection pool to "my_application".
817 #
818 # The various connection pools are managed by a single instance of
819 # ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
820 # All Active Record models use this handler to determine the connection pool that they
821 # should use.
ca6d717 @nicksieger Deprecate allow_concurrency and make it have no effect
nicksieger authored
822 class ConnectionHandler
ba1544d @jonleighton One hash is enough
jonleighton authored
823 def initialize
45448a5 @thedarkone Replace some global Hash usages with the new thread safe cache.
thedarkone authored
824 # These caches are keyed by klass.name, NOT klass. Keying them by klass
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
825 # alone would lead to memory leaks in development mode as all previous
826 # instances of the class would stay in memory.
45448a5 @thedarkone Replace some global Hash usages with the new thread safe cache.
thedarkone authored
827 @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
828 h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
829 end
830 @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
831 h[k] = ThreadSafe::Cache.new
832 end
aaff1a4 @tenderlove database connections are automatically established after forking.
tenderlove authored
833 end
834
c3ca7ac @jonleighton Properly deprecate ConnectionHandler#connection_pools
jonleighton authored
835 def connection_pool_list
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
836 owner_to_pool.values.compact
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
837 end
94c8715 @rafaelfranca Return an array of pools from `connection_pools`
rafaelfranca authored
838 alias :connection_pools :connection_pool_list
c3ca7ac @jonleighton Properly deprecate ConnectionHandler#connection_pools
jonleighton authored
839
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
840 def establish_connection(owner, spec)
841 @class_to_pool.clear
bc43763 @dahakawang fix anonymous class issue
dahakawang authored
842 raise RuntimeError, "Anonymous class is not allowed." unless owner.name
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
843 owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
844 end
845
25f9497 @tenderlove adding active_connections? to the connection pool for finding open co…
tenderlove authored
846 # Returns true if there are any active connections among the connection
847 # pools that the ConnectionHandler is managing.
848 def active_connections?
c3ca7ac @jonleighton Properly deprecate ConnectionHandler#connection_pools
jonleighton authored
849 connection_pool_list.any?(&:active_connection?)
25f9497 @tenderlove adding active_connections? to the connection pool for finding open co…
tenderlove authored
850 end
851
d07a6b1 @nicksieger Make clear_active_connections! also return stale connections back to …
nicksieger authored
852 # Returns any connections in use by the current thread back to the pool,
853 # and also returns connections to the pool cached by threads that are no
854 # longer alive.
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
855 def clear_active_connections!
c3ca7ac @jonleighton Properly deprecate ConnectionHandler#connection_pools
jonleighton authored
856 connection_pool_list.each(&:release_connection)
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
857 end
858
8f1b141 @smartinez87 Fixed punctuation errors.
smartinez87 authored
859 # Clears the cache which maps classes.
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
860 def clear_reloadable_connections!
c3ca7ac @jonleighton Properly deprecate ConnectionHandler#connection_pools
jonleighton authored
861 connection_pool_list.each(&:clear_reloadable_connections!)
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
862 end
863
864 def clear_all_connections!
c3ca7ac @jonleighton Properly deprecate ConnectionHandler#connection_pools
jonleighton authored
865 connection_pool_list.each(&:disconnect!)
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
866 end
867
868 # Locate the connection of the nearest super class. This can be an
869 # active or defined connection: if it is the latter, it will be
870 # opened and set as the active connection for the class it was defined
871 # for (not necessarily the current class).
872 def retrieve_connection(klass) #:nodoc:
873 pool = retrieve_connection_pool(klass)
58ba48e @jeremy Distinguish ConnectionNotEstablished messages: no conn pool for the c…
jeremy authored
874 raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
875 conn = pool.connection
876 raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
877 conn
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
878 end
879
82fcd9d @nicksieger Clean up the code, get rid of reserve/release, add some more docs
nicksieger authored
880 # Returns true if a connection that's accessible to this class has
881 # already been opened.
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
882 def connected?(klass)
0832bc6 @lifo Make sure ActiveRecord::Base.connected? doesn't raise an exception fo…
lifo authored
883 conn = retrieve_connection_pool(klass)
a4458f5 @tenderlove removing useless ternary
tenderlove authored
884 conn && conn.connected?
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
885 end
886
887 # Remove the connection for this class. This will close the active
888 # connection and the defined connection (if they exist). The result
889 # can be used as an argument for establish_connection, for easily
890 # re-establishing the connection.
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
891 def remove_connection(owner)
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
892 if pool = owner_to_pool.delete(owner.name)
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
893 @class_to_pool.clear
ba1544d @jonleighton One hash is enough
jonleighton authored
894 pool.automatic_reconnect = false
895 pool.disconnect!
896 pool.spec.config
897 end
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHan…
nicksieger authored
898 end
899
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
900 # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
901 # This makes retrieving the connection pool O(1) once the process is warm.
902 # When a connection is established or removed, we invalidate the cache.
903 #
904 # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
57b0ae8 @amatsuda Gist URLs are now namespaced
amatsuda authored
905 # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
906 # #fetch is significantly slower than #[]. So in the nil case, no caching will
907 # take place, but that's ok since the nil case is not the common one that we wish
908 # to optimise for.
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
909 def retrieve_connection_pool(klass)
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
910 class_to_pool[klass.name] ||= begin
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
911 until pool = pool_for(klass)
912 klass = klass.superclass
9e4c41c @jonleighton Remove ActiveRecord::Model
jonleighton authored
913 break unless klass <= Base
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
914 end
221571b @jonleighton Make connection pool retrieval faster
jonleighton authored
915
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
916 class_to_pool[klass.name] = pool
e030f26 @jonleighton Simplify AR configuration code.
jonleighton authored
917 end
fe575dd @nicksieger Nearing the finish line. Initial fixed-size connection pool implement…
nicksieger authored
918 end
aaff1a4 @tenderlove database connections are automatically established after forking.
tenderlove authored
919
920 private
921
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
922 def owner_to_pool
923 @owner_to_pool[Process.pid]
924 end
925
aaff1a4 @tenderlove database connections are automatically established after forking.
tenderlove authored
926 def class_to_pool
decafdd @tenderlove use Process.pid rather than $$
tenderlove authored
927 @class_to_pool[Process.pid]
aaff1a4 @tenderlove database connections are automatically established after forking.
tenderlove authored
928 end
929
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
930 def pool_for(owner)
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
931 owner_to_pool.fetch(owner.name) {
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
932 if ancestor_pool = pool_from_any_process_for(owner)
4a274ed @jonleighton Refactor connection handler
jonleighton authored
933 # A connection was established in an ancestor process that must have
934 # subsequently forked. We can't reuse the connection, but we can copy
935 # the specification and establish a new connection with it.
19bc570 @eugeneius Add schema cache to new connection pool after fork
eugeneius authored
936 establish_connection(owner, ancestor_pool.spec).tap do |pool|
937 pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
938 end
aaff1a4 @tenderlove database connections are automatically established after forking.
tenderlove authored
939 else
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
940 owner_to_pool[owner.name] = nil
aaff1a4 @tenderlove database connections are automatically established after forking.
tenderlove authored
941 end
942 }
943 end
4a274ed @jonleighton Refactor connection handler
jonleighton authored
944
e96558f @jonleighton Cache the connection pool for a given class
jonleighton authored
945 def pool_from_any_process_for(owner)
68e4442 @jonleighton Fix memory leak in development mode
jonleighton authored
946 owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
947 owner_to_pool && owner_to_pool[owner.name]
4a274ed @jonleighton Refactor connection handler
jonleighton authored
948 end
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
949 end
1b22071 @josh Ensure ActiveRecord session store's connections are checked in after …
josh authored
950
951 class ConnectionManagement
952 def initialize(app)
953 @app = app
954 end
955
956 def call(env)
cb598c2 @Sirupsen ar/connection_pool: honor overriden rack.test in middleware
Sirupsen authored
957 testing = env['rack.test']
c7b7c6a @tenderlove make sure that active connections are not cleared during test when an…
tenderlove authored
958
2b81240 @lest use Rack::BodyProxy in activerecord middlewares
lest authored
959 response = @app.call(env)
960 response[2] = ::Rack::BodyProxy.new(response[2]) do
961 ActiveRecord::Base.clear_active_connections! unless testing
962 end
e524609 @tenderlove proxy body responses so we close database connections after body is f…
tenderlove authored
963
2b81240 @lest use Rack::BodyProxy in activerecord middlewares
lest authored
964 response
0b6c1f0 @vipulnsward rescue from all exceptions in `ConnectionManagement#call`
vipulnsward authored
965 rescue Exception
c7b7c6a @tenderlove make sure that active connections are not cleared during test when an…
tenderlove authored
966 ActiveRecord::Base.clear_active_connections! unless testing
3b2a032 @tenderlove clearing active connections in the ConnectionManagement middleware if…
tenderlove authored
967 raise
1b22071 @josh Ensure ActiveRecord session store's connections are checked in after …
josh authored
968 end
969 end
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
970 end
21eb18a @alk Fix race in ConnectionPool#checkout
alk authored
971 end
Something went wrong with that request. Please try again.