Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 239 lines (203 sloc) 7.926 kb
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHandle...
nicksieger authored
1 require 'monitor'
50cd4bd @nicksieger Introduce synchronization around connection pool access
nicksieger authored
2 require 'set'
3
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
4 module ActiveRecord
5 module ConnectionAdapters
6 class ConnectionPool
50cd4bd @nicksieger Introduce synchronization around connection pool access
nicksieger authored
7 delegate :verification_timeout, :to => "::ActiveRecord::Base"
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
8 attr_reader :active_connections, :spec
9
10 def initialize(spec)
11 # The thread id -> adapter cache.
12 @active_connections = {}
13
14 # The ConnectionSpecification for this pool
15 @spec = spec
cab76ce @nicksieger Add synchronization to connection pool also
nicksieger authored
16
17 # The mutex used to synchronize pool access
18 @connection_mutex = Monitor.new
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
19 end
20
21 def active_connection_name #:nodoc:
22 Thread.current.object_id
23 end
24
25 def active_connection
26 active_connections[active_connection_name]
27 end
28
29 # Returns the connection currently associated with the class. This can
30 # also be used to "borrow" the connection to do database work unrelated
31 # to any of the specific Active Records.
32 def connection
33 if conn = active_connections[active_connection_name]
34 conn
35 else
36 # retrieve_connection sets the cache key.
37 conn = retrieve_connection
38 active_connections[active_connection_name] = conn
39 end
40 end
41
42 # Clears the cache which maps classes to connections.
43 def clear_active_connections!
44 clear_entries!(@active_connections, [active_connection_name]) do |name, conn|
45 conn.disconnect!
46 end
47 end
48
50cd4bd @nicksieger Introduce synchronization around connection pool access
nicksieger authored
49 # Clears the cache which maps classes
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
50 def clear_reloadable_connections!
51 @active_connections.each do |name, conn|
52 if conn.requires_reloading?
53 conn.disconnect!
54 @active_connections.delete(name)
55 end
56 end
57 end
58
59 # Verify active connections.
60 def verify_active_connections! #:nodoc:
61 remove_stale_cached_threads!(@active_connections) do |name, conn|
62 conn.disconnect!
63 end
64 active_connections.each_value do |connection|
50cd4bd @nicksieger Introduce synchronization around connection pool access
nicksieger authored
65 connection.verify!(verification_timeout)
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
66 end
67 end
68
69 def retrieve_connection #:nodoc:
70 # Name is nil if establish_connection hasn't been called for
71 # some class along the inheritance chain up to AR::Base yet.
72 name = active_connection_name
73 if conn = active_connections[name]
74 # Verify the connection.
50cd4bd @nicksieger Introduce synchronization around connection pool access
nicksieger authored
75 conn.verify!(verification_timeout)
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
76 else
cab76ce @nicksieger Add synchronization to connection pool also
nicksieger authored
77 self.set_connection spec
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
78 conn = active_connections[name]
79 end
80
81 conn or raise ConnectionNotEstablished
82 end
83
84 # Returns true if a connection that's accessible to this class has already been opened.
85 def connected?
86 active_connections[active_connection_name] ? true : false
87 end
88
cab76ce @nicksieger Add synchronization to connection pool also
nicksieger authored
89 # Disconnect all connections in the pool.
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
90 def disconnect!
91 clear_cache!(@active_connections) do |name, conn|
92 conn.disconnect!
93 end
94 end
95
96 # Set the connection for the class.
cab76ce @nicksieger Add synchronization to connection pool also
nicksieger authored
97 def set_connection(spec) #:nodoc:
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
98 if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
99 active_connections[active_connection_name] = spec
100 elsif spec.kind_of?(ActiveRecord::Base::ConnectionSpecification)
37b0b36 @nicksieger Fix failure to retain value of allow_concurrency
nicksieger authored
101 config = spec.config.reverse_merge(:allow_concurrency => ActiveRecord::Base.allow_concurrency)
102 self.set_connection ActiveRecord::Base.send(spec.adapter_method, config)
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
103 else
104 raise ConnectionNotEstablished
105 end
106 end
107
cab76ce @nicksieger Add synchronization to connection pool also
nicksieger authored
108 synchronize :active_connection, :connection, :clear_active_connections!,
109 :clear_reloadable_connections!, :verify_active_connections!, :retrieve_connection,
110 :connected?, :disconnect!, :set_connection, :with => :@connection_mutex
111
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
112 private
113 def clear_cache!(cache, &block)
114 cache.each(&block) if block_given?
115 cache.clear
116 end
117
118 # Remove stale threads from the cache.
119 def remove_stale_cached_threads!(cache, &block)
120 stale = Set.new(cache.keys)
121
122 Thread.list.each do |thread|
123 stale.delete(thread.object_id) if thread.alive?
124 end
125 clear_entries!(cache, stale, &block)
126 end
127
128 def clear_entries!(cache, keys, &block)
129 keys.each do |key|
50cd4bd @nicksieger Introduce synchronization around connection pool access
nicksieger authored
130 next unless cache.has_key?(key)
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
131 block.call(key, cache[key])
132 cache.delete(key)
133 end
134 end
135 end
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHandle...
nicksieger authored
136
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
137 module ConnectionHandlerMethods
138 def initialize(pools = {})
139 @connection_pools = pools
140 end
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHandle...
nicksieger authored
141
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
142 def connection_pools
143 @connection_pools ||= {}
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHandle...
nicksieger authored
144 end
145
146 def establish_connection(name, spec)
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
147 @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHandle...
nicksieger authored
148 end
149
150 # for internal use only and for testing
151 def active_connections #:nodoc:
152 @connection_pools.inject({}) do |hash,kv|
153 hash[kv.first] = kv.last.active_connection
154 hash.delete(kv.first) unless hash[kv.first]
155 hash
156 end
157 end
158
159 # Clears the cache which maps classes to connections.
160 def clear_active_connections!
161 @connection_pools.each_value {|pool| pool.clear_active_connections! }
162 end
163
164 # Clears the cache which maps classes
165 def clear_reloadable_connections!
166 @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
167 end
168
169 def clear_all_connections!
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
170 @connection_pools.each_value {|pool| pool.disconnect! }
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHandle...
nicksieger authored
171 end
172
173 # Verify active connections.
174 def verify_active_connections! #:nodoc:
175 @connection_pools.each_value {|pool| pool.verify_active_connections!}
176 end
177
178 # Locate the connection of the nearest super class. This can be an
179 # active or defined connection: if it is the latter, it will be
180 # opened and set as the active connection for the class it was defined
181 # for (not necessarily the current class).
182 def retrieve_connection(klass) #:nodoc:
183 pool = retrieve_connection_pool(klass)
184 (pool && pool.connection) or raise ConnectionNotEstablished
185 end
186
187 # Returns true if a connection that's accessible to this class has already been opened.
188 def connected?(klass)
189 retrieve_connection_pool(klass).connected?
190 end
191
192 # Remove the connection for this class. This will close the active
193 # connection and the defined connection (if they exist). The result
194 # can be used as an argument for establish_connection, for easily
195 # re-establishing the connection.
196 def remove_connection(klass)
197 pool = @connection_pools[klass.name]
198 @connection_pools.delete_if { |key, value| value == pool }
199 pool.disconnect! if pool
200 pool.spec.config if pool
201 end
202
203 private
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
204 def retrieve_connection_pool(klass)
205 loop do
206 pool = @connection_pools[klass.name]
207 return pool if pool
208 return nil if ActiveRecord::Base == klass
209 klass = klass.superclass
210 end
ff97e9d @nicksieger Connection handling methods extracted out into separate ConnectionHandle...
nicksieger authored
211 end
212 end
72d959d @nicksieger Split connection handler into single- and multiple-thread versions.
nicksieger authored
213
214 # This connection handler is not thread-safe, as it does not protect access
215 # to the underlying connection pools.
216 class SingleThreadConnectionHandler
217 include ConnectionHandlerMethods
218 end
219
220 # This connection handler is thread-safe. Each access or modification of a thread
221 # pool is synchronized by an internal monitor.
222 class MultipleThreadConnectionHandler
223 attr_reader :connection_pools_lock
224 include ConnectionHandlerMethods
225
226 def initialize(pools = {})
227 super
228 @connection_pools_lock = Monitor.new
229 end
230
231 # Apply monitor to all public methods that access the pool.
232 synchronize :establish_connection, :retrieve_connection,
233 :connected?, :remove_connection, :active_connections,
234 :clear_active_connections!, :clear_reloadable_connections!,
235 :clear_all_connections!, :verify_active_connections!,
236 :with => :connection_pools_lock
237 end
6edaa26 @nicksieger Initial conversion to connection pool
nicksieger authored
238 end
239 end
Something went wrong with that request. Please try again.