~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'monitor'
 
2
require 'set'
 
3
 
 
4
module ActiveRecord
 
5
  # Raised when a connection could not be obtained within the connection
 
6
  # acquisition timeout period.
 
7
  class ConnectionTimeoutError < ConnectionNotEstablished
 
8
  end
 
9
 
 
10
  module ConnectionAdapters
 
11
    # Connection pool base class for managing ActiveRecord database
 
12
    # connections.
 
13
    #
 
14
    # == Introduction
 
15
    #
 
16
    # A connection pool synchronizes thread access to a limited number of
 
17
    # database connections. The basic idea is that each thread checks out a
 
18
    # database connection from the pool, uses that connection, and checks the
 
19
    # connection back in. ConnectionPool is completely thread-safe, and will
 
20
    # ensure that a connection cannot be used by two threads at the same time,
 
21
    # as long as ConnectionPool's contract is correctly followed. It will also
 
22
    # handle cases in which there are more threads than connections: if all
 
23
    # connections have been checked out, and a thread tries to checkout a
 
24
    # connection anyway, then ConnectionPool will wait until some other thread
 
25
    # has checked in a connection.
 
26
    #
 
27
    # == Obtaining (checking out) a connection
 
28
    #
 
29
    # Connections can be obtained and used from a connection pool in several
 
30
    # ways:
 
31
    #
 
32
    # 1. Simply use ActiveRecord::Base.connection as with ActiveRecord 2.1 and
 
33
    #    earlier (pre-connection-pooling). Eventually, when you're done with
 
34
    #    the connection(s) and wish it to be returned to the pool, you call
 
35
    #    ActiveRecord::Base.clear_active_connections!. This will be the
 
36
    #    default behavior for ActiveRecord when used in conjunction with
 
37
    #    ActionPack's request handling cycle.
 
38
    # 2. Manually check out a connection from the pool with
 
39
    #    ActiveRecord::Base.connection_pool.checkout. You are responsible for
 
40
    #    returning this connection to the pool when finished by calling
 
41
    #    ActiveRecord::Base.connection_pool.checkin(connection).
 
42
    # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
 
43
    #    obtains a connection, yields it as the sole argument to the block,
 
44
    #    and returns it to the pool after the block completes.
 
45
    #
 
46
    # Connections in the pool are actually AbstractAdapter objects (or objects
 
47
    # compatible with AbstractAdapter's interface).
 
48
    #
 
49
    # == Options
 
50
    #
 
51
    # There are two connection-pooling-related options that you can add to
 
52
    # your database connection configuration:
 
53
    #
 
54
    # * +pool+: number indicating size of connection pool (default 5)
 
55
    # * +wait_timeout+: number of seconds to block and wait for a connection
 
56
    #   before giving up and raising a timeout error (default 5 seconds).
 
57
    class ConnectionPool
 
58
      attr_reader :spec
 
59
 
 
60
      # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
 
61
      # object which describes database connection information (e.g. adapter,
 
62
      # host name, username, password, etc), as well as the maximum size for
 
63
      # this ConnectionPool.
 
64
      #
 
65
      # The default ConnectionPool maximum size is 5.
 
66
      def initialize(spec)
 
67
        @spec = spec
 
68
 
 
69
        # The cache of reserved connections mapped to threads
 
70
        @reserved_connections = {}
 
71
 
 
72
        # The mutex used to synchronize pool access
 
73
        @connection_mutex = Monitor.new
 
74
        @queue = @connection_mutex.new_cond
 
75
 
 
76
        # default 5 second timeout unless on ruby 1.9
 
77
        @timeout =
 
78
          if RUBY_VERSION < '1.9'
 
79
            spec.config[:wait_timeout] || 5
 
80
          end
 
81
 
 
82
        # default max pool size to 5
 
83
        @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
 
84
 
 
85
        @connections = []
 
86
        @checked_out = []
 
87
      end
 
88
 
 
89
      # Retrieve the connection associated with the current thread, or call
 
90
      # #checkout to obtain one if necessary.
 
91
      #
 
92
      # #connection can be called any number of times; the connection is
 
93
      # held in a hash keyed by the thread id.
 
94
      def connection
 
95
        if conn = @reserved_connections[current_connection_id]
 
96
          conn
 
97
        else
 
98
          @reserved_connections[current_connection_id] = checkout
 
99
        end
 
100
      end
 
101
 
 
102
      # Signal that the thread is finished with the current connection.
 
103
      # #release_connection releases the connection-thread association
 
104
      # and returns the connection to the pool.
 
105
      def release_connection
 
106
        conn = @reserved_connections.delete(current_connection_id)
 
107
        checkin conn if conn
 
108
      end
 
109
 
 
110
      # Reserve a connection, and yield it to a block. Ensure the connection is
 
111
      # checked back in when finished.
 
112
      def with_connection
 
113
        conn = checkout
 
114
        yield conn
 
115
      ensure
 
116
        checkin conn
 
117
      end
 
118
 
 
119
      # Returns true if a connection has already been opened.
 
120
      def connected?
 
121
        !@connections.empty?
 
122
      end
 
123
 
 
124
      # Disconnects all connections in the pool, and clears the pool.
 
125
      def disconnect!
 
126
        @reserved_connections.each do |name,conn|
 
127
          checkin conn
 
128
        end
 
129
        @reserved_connections = {}
 
130
        @connections.each do |conn|
 
131
          conn.disconnect!
 
132
        end
 
133
        @connections = []
 
134
      end
 
135
 
 
136
      # Clears the cache which maps classes
 
137
      def clear_reloadable_connections!
 
138
        @reserved_connections.each do |name, conn|
 
139
          checkin conn
 
140
        end
 
141
        @reserved_connections = {}
 
142
        @connections.each do |conn|
 
143
          conn.disconnect! if conn.requires_reloading?
 
144
        end
 
145
        @connections = []
 
146
      end
 
147
 
 
148
      # Verify active connections and remove and disconnect connections
 
149
      # associated with stale threads.
 
150
      def verify_active_connections! #:nodoc:
 
151
        clear_stale_cached_connections!
 
152
        @connections.each do |connection|
 
153
          connection.verify!
 
154
        end
 
155
      end
 
156
 
 
157
      # Return any checked-out connections back to the pool by threads that
 
158
      # are no longer alive.
 
159
      def clear_stale_cached_connections!
 
160
        remove_stale_cached_threads!(@reserved_connections) do |name, conn|
 
161
          checkin conn
 
162
        end
 
163
      end
 
164
 
 
165
      # Check-out a database connection from the pool, indicating that you want
 
166
      # to use it. You should call #checkin when you no longer need this.
 
167
      #
 
168
      # This is done by either returning an existing connection, or by creating
 
169
      # a new connection. If the maximum number of connections for this pool has
 
170
      # already been reached, but the pool is empty (i.e. they're all being used),
 
171
      # then this method will wait until a thread has checked in a connection.
 
172
      # The wait time is bounded however: if no connection can be checked out
 
173
      # within the timeout specified for this pool, then a ConnectionTimeoutError
 
174
      # exception will be raised.
 
175
      #
 
176
      # Returns: an AbstractAdapter object.
 
177
      #
 
178
      # Raises:
 
179
      # - ConnectionTimeoutError: no connection can be obtained from the pool
 
180
      #   within the timeout period.
 
181
      def checkout
 
182
        # Checkout an available connection
 
183
        @connection_mutex.synchronize do
 
184
          loop do
 
185
            conn = if @checked_out.size < @connections.size
 
186
                     checkout_existing_connection
 
187
                   elsif @connections.size < @size
 
188
                     checkout_new_connection
 
189
                   end
 
190
            return conn if conn
 
191
            # No connections available; wait for one
 
192
            if @queue.wait(@timeout)
 
193
              next
 
194
            else
 
195
              # try looting dead threads
 
196
              clear_stale_cached_connections!
 
197
              if @size == @checked_out.size
 
198
                raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}.  The max pool size is currently #{@size}; consider increasing it."
 
199
              end
 
200
            end
 
201
          end
 
202
        end
 
203
      end
 
204
 
 
205
      # Check-in a database connection back into the pool, indicating that you
 
206
      # no longer need this connection.
 
207
      #
 
208
      # +conn+: an AbstractAdapter object, which was obtained by earlier by
 
209
      # calling +checkout+ on this pool.
 
210
      def checkin(conn)
 
211
        @connection_mutex.synchronize do
 
212
          conn.run_callbacks :checkin
 
213
          @checked_out.delete conn
 
214
          @queue.signal
 
215
        end
 
216
      end
 
217
 
 
218
      synchronize :clear_reloadable_connections!, :verify_active_connections!,
 
219
        :connected?, :disconnect!, :with => :@connection_mutex
 
220
 
 
221
      private
 
222
      def new_connection
 
223
        ActiveRecord::Base.send(spec.adapter_method, spec.config)
 
224
      end
 
225
 
 
226
      def current_connection_id #:nodoc:
 
227
        Thread.current.object_id
 
228
      end
 
229
 
 
230
      # Remove stale threads from the cache.
 
231
      def remove_stale_cached_threads!(cache, &block)
 
232
        keys = Set.new(cache.keys)
 
233
 
 
234
        Thread.list.each do |thread|
 
235
          keys.delete(thread.object_id) if thread.alive?
 
236
        end
 
237
        keys.each do |key|
 
238
          next unless cache.has_key?(key)
 
239
          block.call(key, cache[key])
 
240
          cache.delete(key)
 
241
        end
 
242
      end
 
243
 
 
244
      def checkout_new_connection
 
245
        c = new_connection
 
246
        @connections << c
 
247
        checkout_and_verify(c)
 
248
      end
 
249
 
 
250
      def checkout_existing_connection
 
251
        c = (@connections - @checked_out).first
 
252
        checkout_and_verify(c)
 
253
      end
 
254
 
 
255
      def checkout_and_verify(c)
 
256
        c.verify!
 
257
        c.run_callbacks :checkout
 
258
        @checked_out << c
 
259
        c
 
260
      end
 
261
    end
 
262
 
 
263
    # ConnectionHandler is a collection of ConnectionPool objects. It is used
 
264
    # for keeping separate connection pools for ActiveRecord models that connect
 
265
    # to different databases.
 
266
    #
 
267
    # For example, suppose that you have 5 models, with the following hierarchy:
 
268
    #
 
269
    #  |
 
270
    #  +-- Book
 
271
    #  |    |
 
272
    #  |    +-- ScaryBook
 
273
    #  |    +-- GoodBook
 
274
    #  +-- Author
 
275
    #  +-- BankAccount
 
276
    #
 
277
    # Suppose that Book is to connect to a separate database (i.e. one other
 
278
    # than the default database). Then Book, ScaryBook and GoodBook will all use
 
279
    # the same connection pool. Likewise, Author and BankAccount will use the
 
280
    # same connection pool. However, the connection pool used by Author/BankAccount
 
281
    # is not the same as the one used by Book/ScaryBook/GoodBook.
 
282
    #
 
283
    # Normally there is only a single ConnectionHandler instance, accessible via
 
284
    # ActiveRecord::Base.connection_handler. ActiveRecord models use this to
 
285
    # determine that connection pool that they should use.
 
286
    class ConnectionHandler
 
287
      def initialize(pools = {})
 
288
        @connection_pools = pools
 
289
      end
 
290
 
 
291
      def connection_pools
 
292
        @connection_pools ||= {}
 
293
      end
 
294
 
 
295
      def establish_connection(name, spec)
 
296
        @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
 
297
      end
 
298
 
 
299
      # Returns any connections in use by the current thread back to the pool,
 
300
      # and also returns connections to the pool cached by threads that are no
 
301
      # longer alive.
 
302
      def clear_active_connections!
 
303
        @connection_pools.each_value {|pool| pool.release_connection }
 
304
      end
 
305
 
 
306
      # Clears the cache which maps classes
 
307
      def clear_reloadable_connections!
 
308
        @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
 
309
      end
 
310
 
 
311
      def clear_all_connections!
 
312
        @connection_pools.each_value {|pool| pool.disconnect! }
 
313
      end
 
314
 
 
315
      # Verify active connections.
 
316
      def verify_active_connections! #:nodoc:
 
317
        @connection_pools.each_value {|pool| pool.verify_active_connections! }
 
318
      end
 
319
 
 
320
      # Locate the connection of the nearest super class. This can be an
 
321
      # active or defined connection: if it is the latter, it will be
 
322
      # opened and set as the active connection for the class it was defined
 
323
      # for (not necessarily the current class).
 
324
      def retrieve_connection(klass) #:nodoc:
 
325
        pool = retrieve_connection_pool(klass)
 
326
        (pool && pool.connection) or raise ConnectionNotEstablished
 
327
      end
 
328
 
 
329
      # Returns true if a connection that's accessible to this class has
 
330
      # already been opened.
 
331
      def connected?(klass)
 
332
        conn = retrieve_connection_pool(klass)
 
333
        conn ? conn.connected? : false
 
334
      end
 
335
 
 
336
      # Remove the connection for this class. This will close the active
 
337
      # connection and the defined connection (if they exist). The result
 
338
      # can be used as an argument for establish_connection, for easily
 
339
      # re-establishing the connection.
 
340
      def remove_connection(klass)
 
341
        pool = @connection_pools[klass.name]
 
342
        @connection_pools.delete_if { |key, value| value == pool }
 
343
        pool.disconnect! if pool
 
344
        pool.spec.config if pool
 
345
      end
 
346
 
 
347
      def retrieve_connection_pool(klass)
 
348
        pool = @connection_pools[klass.name]
 
349
        return pool if pool
 
350
        return nil if ActiveRecord::Base == klass
 
351
        retrieve_connection_pool klass.superclass
 
352
      end
 
353
    end
 
354
 
 
355
    class ConnectionManagement
 
356
      def initialize(app)
 
357
        @app = app
 
358
      end
 
359
 
 
360
      def call(env)
 
361
        @app.call(env)
 
362
      ensure
 
363
        # Don't return connection (and peform implicit rollback) if
 
364
        # this request is a part of integration test
 
365
        unless env.key?("rack.test")
 
366
          ActiveRecord::Base.clear_active_connections!
 
367
        end
 
368
      end
 
369
    end
 
370
  end
 
371
end