public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Initial conversion to connection pool

So far so good, tests still run clean. Next steps: synchronize connection pool 
access
and modification, and change allow_concurrency to simply switch a real lock for 
a
null lock.
Nick (author)
Fri Apr 18 22:24:01 -0700 2008
commit  6edaa267174dfedaf5b152b9eba25b4eb5e34c99
tree    c5dbbcf112bb933e653f2f89d1c1dca17c377aca
parent  1a81f156bd81990a50ecf13986b2131dbd20a62b
...
14
15
16
17
 
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 
 
 
68
69
70
71
72
73
74
75
76
77
78
79
80
 
81
82
83
84
85
86
 
 
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
 
 
110
111
112
113
114
115
116
117
118
119
120
121
122
123
 
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
 
 
 
167
168
169
...
208
209
210
211
212
213
 
214
215
216
...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
 
 
 
264
265
 
 
 
 
 
266
267
268
269
270
 
271
272
273
...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
 
 
 
 
307
308
309
...
14
15
16
 
17
18
19
 
 
 
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
22
23
24
25
26
27
28
29
 
 
 
 
 
 
 
30
31
32
33
34
 
 
35
36
37
38
39
40
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
43
44
45
46
47
48
 
 
 
 
 
 
 
 
 
49
50
51
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
54
55
56
57
58
...
97
98
99
 
 
 
100
101
102
103
...
135
136
137
 
 
 
 
 
 
 
 
 
 
 
 
 
138
139
140
141
 
142
143
144
145
146
147
148
149
150
 
151
152
153
154
...
156
157
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
160
161
162
163
164
165
0
@@ -14,156 +14,45 @@ module ActiveRecord
0
     cattr_accessor :verification_timeout, :instance_writer => false
0
     @@verification_timeout = 0
0
 
0
-    # The class -> [adapter_method, config] map
0
+    # The class -> connection pool map
0
     @@defined_connections = {}
0
 
0
-    # The class -> thread id -> adapter cache. (class -> adapter if not allow_concurrency)
0
-    @@active_connections = {}
0
-
0
     class << self
0
-      # Retrieve the connection cache.
0
-      def thread_safe_active_connections #:nodoc:
0
-        @@active_connections[Thread.current.object_id] ||= {}
0
-      end
0
-     
0
-      def single_threaded_active_connections #:nodoc:
0
-        @@active_connections
0
-      end
0
-     
0
-      # pick up the right active_connection method from @@allow_concurrency
0
-      if @@allow_concurrency
0
-        alias_method :active_connections, :thread_safe_active_connections
0
-      else
0
-        alias_method :active_connections, :single_threaded_active_connections
0
-      end
0
-     
0
-      # set concurrency support flag (not thread safe, like most of the methods in this file)
0
-      def allow_concurrency=(threaded) #:nodoc:
0
-        logger.debug "allow_concurrency=#{threaded}" if logger
0
-        return if @@allow_concurrency == threaded
0
-        clear_all_cached_connections!
0
-        @@allow_concurrency = threaded
0
-        method_prefix = threaded ? "thread_safe" : "single_threaded"
0
-        sing = (class << self; self; end)
0
-        [:active_connections, :scoped_methods].each do |method|
0
-          sing.send(:alias_method, method, "#{method_prefix}_#{method}")
0
-        end
0
-        log_connections if logger
0
-      end
0
-      
0
-      def active_connection_name #:nodoc:
0
-        @active_connection_name ||=
0
-           if active_connections[name] || @@defined_connections[name]
0
-             name
0
-           elsif self == ActiveRecord::Base
0
-             nil
0
-           else
0
-             superclass.active_connection_name
0
-           end
0
-      end
0
-
0
-      def clear_active_connection_name #:nodoc:
0
-        @active_connection_name = nil
0
-        subclasses.each { |klass| klass.clear_active_connection_name }
0
+      # for internal use only
0
+      def active_connections
0
+        @@defined_connections.inject([]) {|arr,kv| arr << kv.last.active_connection}.compact.uniq
0
       end
0
 
0
       # Returns the connection currently associated with the class. This can
0
       # also be used to "borrow" the connection to do database work unrelated
0
       # to any of the specific Active Records.
0
       def connection
0
-        if defined?(@active_connection_name) && (conn = active_connections[@active_connection_name])
0
-          conn
0
-        else
0
-          # retrieve_connection sets the cache key.
0
-          conn = retrieve_connection
0
-          active_connections[@active_connection_name] = conn
0
-        end
0
+        retrieve_connection
0
       end
0
 
0
       # Clears the cache which maps classes to connections.
0
       def clear_active_connections!
0
-        clear_cache!(@@active_connections) do |name, conn|
0
-          conn.disconnect!
0
+        clear_cache!(@@defined_connections) do |name, pool|
0
+          pool.disconnect!
0
         end
0
       end
0
       
0
       # Clears the cache which maps classes 
0
       def clear_reloadable_connections!
0
-        if @@allow_concurrency
0
-          # With concurrent connections @@active_connections is
0
-          # a hash keyed by thread id.
0
-          @@active_connections.each do |thread_id, conns|
0
-            conns.each do |name, conn|
0
-              if conn.requires_reloading?
0
-                conn.disconnect!
0
-                @@active_connections[thread_id].delete(name)
0
-              end
0
-            end
0
-          end
0
-        else
0
-          @@active_connections.each do |name, conn|
0
-            if conn.requires_reloading?
0
-              conn.disconnect!
0
-              @@active_connections.delete(name)
0
-            end
0
-          end
0
+        clear_cache!(@@defined_connections) do |name, pool|
0
+          pool.clear_reloadable_connections!
0
         end
0
       end
0
 
0
       # Verify active connections.
0
       def verify_active_connections! #:nodoc:
0
-        if @@allow_concurrency
0
-          remove_stale_cached_threads!(@@active_connections) do |name, conn|
0
-            conn.disconnect!
0
-          end
0
-        end
0
-        
0
-        active_connections.each_value do |connection|
0
-          connection.verify!(@@verification_timeout)
0
-        end
0
+        @@defined_connections.each_value {|pool| pool.verify_active_connections!}
0
       end
0
 
0
       private
0
-        def clear_cache!(cache, thread_id = nil, &block)
0
-          if cache
0
-            if @@allow_concurrency
0
-              thread_id ||= Thread.current.object_id
0
-              thread_cache, cache = cache, cache[thread_id]
0
-              return unless cache
0
-            end
0
-
0
-            cache.each(&block) if block_given?
0
-            cache.clear
0
-          end
0
-        ensure
0
-          if thread_cache && @@allow_concurrency
0
-            thread_cache.delete(thread_id)
0
-          end
0
-        end
0
-
0
-        # Remove stale threads from the cache.
0
-        def remove_stale_cached_threads!(cache, &block)
0
-          stale = Set.new(cache.keys)
0
-
0
-          Thread.list.each do |thread|
0
-            stale.delete(thread.object_id) if thread.alive?
0
-          end
0
-
0
-          stale.each do |thread_id|
0
-            clear_cache!(cache, thread_id, &block)
0
-          end
0
-        end
0
-        
0
-        def clear_all_cached_connections!
0
-          if @@allow_concurrency
0
-            @@active_connections.each_value do |connection_hash_for_thread|
0
-              connection_hash_for_thread.each_value {|conn| conn.disconnect! }
0
-              connection_hash_for_thread.clear
0
-            end
0
-          else
0
-            @@active_connections.each_value {|conn| conn.disconnect! }
0
-          end
0
-          @@active_connections.clear          
0
+        def clear_cache!(cache, &block)
0
+          cache.each(&block) if block_given?
0
+          cache.clear
0
         end
0
     end
0
 
0
@@ -208,9 +97,7 @@ module ActiveRecord
0
           raise AdapterNotSpecified unless defined? RAILS_ENV
0
           establish_connection(RAILS_ENV)
0
         when ConnectionSpecification
0
-          clear_active_connection_name
0
-          @active_connection_name = name
0
-          @@defined_connections[name] = spec
0
+          @@defined_connections[name] = ConnectionAdapters::ConnectionPool.new(spec)
0
         when Symbol, String
0
           if configuration = configurations[spec.to_s]
0
             establish_connection(configuration)
0
@@ -248,26 +135,20 @@ module ActiveRecord
0
     # opened and set as the active connection for the class it was defined
0
     # for (not necessarily the current class).
0
     def self.retrieve_connection #:nodoc:
0
-      # Name is nil if establish_connection hasn't been called for
0
-      # some class along the inheritance chain up to AR::Base yet.
0
-      if name = active_connection_name
0
-        if conn = active_connections[name]
0
-          # Verify the connection.
0
-          conn.verify!(@@verification_timeout)
0
-        elsif spec = @@defined_connections[name]
0
-          # Activate this connection specification.
0
-          klass = name.constantize
0
-          klass.connection = spec
0
-          conn = active_connections[name]
0
-        end
0
-      end
0
+      pool = retrieve_connection_pool
0
+      (pool && pool.connection) or raise ConnectionNotEstablished
0
+    end
0
 
0
-      conn or raise ConnectionNotEstablished
0
+    def self.retrieve_connection_pool
0
+      pool = @@defined_connections[name]
0
+      return pool if pool
0
+      return nil if ActiveRecord::Base == self
0
+      superclass.retrieve_connection_pool
0
     end
0
 
0
     # Returns true if a connection that's accessible to this class has already been opened.
0
     def self.connected?
0
-      active_connections[active_connection_name] ? true : false
0
+      retrieve_connection_pool.connected?
0
     end
0
 
0
     # Remove the connection for this class. This will close the active
0
@@ -275,35 +156,10 @@ module ActiveRecord
0
     # can be used as an argument for establish_connection, for easily
0
     # re-establishing the connection.
0
     def self.remove_connection(klass=self)
0
-      spec = @@defined_connections[klass.name]
0
-      konn = active_connections[klass.name]
0
-      @@defined_connections.delete_if { |key, value| value == spec }
0
-      active_connections.delete_if { |key, value| value == konn }
0
-      konn.disconnect! if konn
0
-      spec.config if spec
0
-    end
0
-
0
-    # Set the connection for the class.
0
-    def self.connection=(spec) #:nodoc:
0
-      if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
0
-        active_connections[name] = spec
0
-      elsif spec.kind_of?(ConnectionSpecification)
0
-        config = spec.config.reverse_merge(:allow_concurrency => @@allow_concurrency)
0
-        self.connection = self.send(spec.adapter_method, config)
0
-      elsif spec.nil?
0
-        raise ConnectionNotEstablished
0
-      else
0
-        establish_connection spec
0
-      end
0
-    end
0
-
0
-    # connection state logging
0
-    def self.log_connections #:nodoc:
0
-      if logger
0
-        logger.info "Defined connections: #{@@defined_connections.inspect}"
0
-        logger.info "Active connections: #{active_connections.inspect}"
0
-        logger.info "Active connection name: #{@active_connection_name}"
0
-      end
0
+      pool = @@defined_connections[klass.name]
0
+      @@defined_connections.delete_if { |key, value| value == pool }
0
+      pool.disconnect! if pool
0
+      pool.spec.config if pool
0
     end
0
   end
0
 end
...
8
9
10
 
11
12
13
...
8
9
10
11
12
13
14
0
@@ -8,6 +8,7 @@ require 'active_record/connection_adapters/abstract/schema_statements'
0
 require 'active_record/connection_adapters/abstract/database_statements'
0
 require 'active_record/connection_adapters/abstract/quoting'
0
 require 'active_record/connection_adapters/abstract/connection_specification'
0
+require 'active_record/connection_adapters/abstract/connection_pool'
0
 require 'active_record/connection_adapters/abstract/query_cache'
0
 
0
 module ActiveRecord
...
8
9
10
11
12
13
14
15
 
 
 
 
16
17
18
19
20
21
22
23
24
 
 
 
 
 
 
25
26
27
28
 
 
29
30
31
32
33
 
34
35
36
37
38
39
40
41
42
43
44
45
 
 
46
47
48
...
8
9
10
 
 
 
 
 
11
12
13
14
15
 
 
 
 
 
 
 
 
16
17
18
19
20
21
22
 
 
 
23
24
25
26
27
28
 
29
30
31
32
33
34
 
 
 
 
 
 
 
35
36
37
38
39
0
@@ -8,41 +8,32 @@ unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name
0
 
0
     fixtures :topics
0
 
0
-  def setup
0
-    @connection = ActiveRecord::Base.remove_connection
0
-    @connections = []
0
-    @allow_concurrency = ActiveRecord::Base.allow_concurrency
0
-  end
0
+    def setup
0
+      @connection = ActiveRecord::Base.remove_connection
0
+      @connections = []
0
+    end
0
 
0
-  def teardown
0
-    # clear the connection cache
0
-    ActiveRecord::Base.send(:clear_all_cached_connections!)
0
-    # set allow_concurrency to saved value
0
-    ActiveRecord::Base.allow_concurrency = @allow_concurrency
0
-    # reestablish old connection
0
-    ActiveRecord::Base.establish_connection(@connection)
0
-  end
0
+    def teardown
0
+      # clear the connection cache
0
+      ActiveRecord::Base.clear_active_connections!
0
+      # reestablish old connection
0
+      ActiveRecord::Base.establish_connection(@connection)
0
+    end
0
 
0
-  def gather_connections(use_threaded_connections)
0
-    ActiveRecord::Base.allow_concurrency = use_threaded_connections
0
-    ActiveRecord::Base.establish_connection(@connection)
0
+    def gather_connections
0
+      ActiveRecord::Base.establish_connection(@connection)
0
 
0
       5.times do
0
         Thread.new do
0
           Topic.find :first
0
-          @connections << ActiveRecord::Base.active_connections.values.first
0
+          @connections << ActiveRecord::Base.active_connections.first
0
         end.join
0
       end
0
     end
0
 
0
     def test_threaded_connections
0
-      gather_connections(true)
0
-      assert_equal @connections.uniq.length, 5
0
-    end
0
-
0
-    def test_unthreaded_connections
0
-      gather_connections(false)
0
-      assert_equal @connections.uniq.length, 1
0
+      gather_connections
0
+      assert_equal @connections.length, 5
0
     end
0
   end
0
 end

Comments