From 41563b49a235ce84419024abcef0825e44e28877 Mon Sep 17 00:00:00 2001
From: Jonathan Rochkind <jonathan@dnil.net>
Date: Tue, 13 Mar 2012 18:00:55 -0400
Subject: ConnectionPool.checkout takes account of ruby using 'non-blocking
 condition variables' in mutex ConditionVariables

---
 .../abstract/connection_pool.rb                    | 27 ++++++++++++++--------
 1 file changed, 18 insertions(+), 9 deletions(-)

(limited to 'activerecord/lib')

diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index f198be0802..3af285c7c8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -226,8 +226,9 @@ connection.  For example: ActiveRecord::Base.connection.close
       # - ConnectionTimeoutError: no connection can be obtained from the pool
       #   within the timeout period.
       def checkout
-        # Checkout an available connection
         synchronize do
+          waited_time = 0
+
           loop do
             conn = @connections.find { |c| c.lease }
 
@@ -243,17 +244,25 @@ connection.  For example: ActiveRecord::Base.connection.close
               return conn
             end
 
-            @queue.wait(@timeout)
+            if waited_time >= @timeout
+              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."
+            end
 
-            if(active_connections.size < @connections.size)
-              next
-            else
+            # Sometimes our wait can end because a connection is available,
+            # but another thread can snatch it up first. If timeout hasn't
+            # passed but no connection is avail, looks like that happened --
+            # loop and wait again, for the time remaining on our timeout. 
+            before_wait = Time.now
+            @queue.wait( [@timeout - waited_time, 0].max )
+            waited_time += (Time.now - before_wait)
+
+            # Will go away in Rails 4, when we don't clean up
+            # after leaked connections automatically anymore. Right now, clean
+            # up after we've returned from a 'wait' if it looks like it's
+            # needed, then loop and try again. 
+            if(active_connections.size >= @connections.size)
               clear_stale_cached_connections!
-              if @size == active_connections.size
-                raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
-              end
             end
-
           end
         end
       end
-- 
cgit v1.2.3