activerecord_threadsafe_fix 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,130 @@
1
+ require 'thread'
2
+ require 'monitor'
3
+ require 'set'
4
+
5
+ require 'active_record'
6
+ require 'active_record/connection_adapters/abstract/connection_pool'
7
+
8
+ module ActiveRecord
9
+ # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
10
+ # with which it shares a Monitor. But could be a generic Queue.
11
+ class Queue
12
+ def initialize(lock = Monitor.new)
13
+ @lock = lock
14
+ @cond = @lock.new_cond
15
+ @num_waiting = 0
16
+ @queue = []
17
+ end
18
+
19
+ # Test if any threads are currently waiting on the queue.
20
+ def any_waiting?
21
+ synchronize do
22
+ @num_waiting > 0
23
+ end
24
+ end
25
+
26
+ # Return the number of threads currently waiting on this
27
+ # queue.
28
+ def num_waiting
29
+ synchronize do
30
+ @num_waiting
31
+ end
32
+ end
33
+
34
+ # Add +element+ to the queue. Never blocks.
35
+ def add(element)
36
+ synchronize do
37
+ @queue.push element
38
+ @cond.signal
39
+ end
40
+ end
41
+
42
+ # If +element+ is in the queue, remove and return it, or nil.
43
+ def delete(element)
44
+ synchronize do
45
+ @queue.delete(element)
46
+ end
47
+ end
48
+
49
+ # Remove all elements from the queue.
50
+ def clear
51
+ synchronize do
52
+ @queue.clear
53
+ end
54
+ end
55
+
56
+ # Remove the head of the queue.
57
+ #
58
+ # If +timeout+ is not given, remove and return the head the
59
+ # queue if the number of available elements is strictly
60
+ # greater than the number of threads currently waiting (that
61
+ # is, don't jump ahead in line). Otherwise, return nil.
62
+ #
63
+ # If +timeout+ is given, block if it there is no element
64
+ # available, waiting up to +timeout+ seconds for an element to
65
+ # become available.
66
+ #
67
+ # Raises:
68
+ # - ConnectionTimeoutError if +timeout+ is given and no element
69
+ # becomes available after +timeout+ seconds,
70
+ def poll(timeout = nil)
71
+ synchronize do
72
+ if timeout
73
+ no_wait_poll || wait_poll(timeout)
74
+ else
75
+ no_wait_poll
76
+ end
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def synchronize(&block)
83
+ @lock.synchronize(&block)
84
+ end
85
+
86
+ # Test if the queue currently contains any elements.
87
+ def any?
88
+ !@queue.empty?
89
+ end
90
+
91
+ # A thread can remove an element from the queue without
92
+ # waiting if an only if the number of currently available
93
+ # connections is strictly greater than the number of waiting
94
+ # threads.
95
+ def can_remove_no_wait?
96
+ @queue.size > @num_waiting
97
+ end
98
+
99
+ # Removes and returns the head of the queue if possible, or nil.
100
+ def remove
101
+ @queue.shift
102
+ end
103
+
104
+ # Remove and return the head the queue if the number of
105
+ # available elements is strictly greater than the number of
106
+ # threads currently waiting. Otherwise, return nil.
107
+ def no_wait_poll
108
+ remove if can_remove_no_wait?
109
+ end
110
+
111
+ # Waits on the queue up to +timeout+ seconds, then removes and
112
+ # returns the head of the queue.
113
+ def wait_poll(timeout)
114
+ @num_waiting += 1
115
+
116
+ t0 = Time.now
117
+ elapsed = 0
118
+ loop do
119
+ @cond.wait(timeout - elapsed)
120
+
121
+ return remove if any?
122
+
123
+ elapsed = Time.now - t0
124
+ raise ConnectionTimeoutError if elapsed >= timeout
125
+ end
126
+ ensure
127
+ @num_waiting -= 1
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,12 @@
1
+ case ActiveRecord::VERSION::MAJOR
2
+ when 3
3
+ case ActiveRecord::VERSION::MINOR
4
+ when 0
5
+ require 'activerecord_threadsafe_fix_3_0'
6
+ when 1
7
+ require 'activerecord_threadsafe_fix_3_1'
8
+ else
9
+ raise 'ActiveRecord threadsafe fix required, but no fix for ActiveRecord' +
10
+ "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
11
+ end
12
+ end
@@ -0,0 +1,371 @@
1
+ #######################################################################
2
+ #
3
+ # This is a fixed version of the connection pool making it threadsafe
4
+ # and fair. Mostly it's copied from 3.0.12's but with the Queue class
5
+ # added -pmahoney
6
+ #
7
+ #######################################################################
8
+
9
+ require 'active_record'
10
+ require 'active_record/connection_adapters/abstract/connection_pool'
11
+ require 'active_record/queue'
12
+
13
+ require 'active_support/core_ext/module/synchronization'
14
+
15
+
16
+ module ActiveRecord
17
+ # Raised when a connection could not be obtained within the connection
18
+ # acquisition timeout period.
19
+ class ConnectionTimeoutError < ConnectionNotEstablished
20
+ end
21
+
22
+ module ConnectionAdapters
23
+ # Connection pool base class for managing Active Record database
24
+ # connections.
25
+ #
26
+ # == Introduction
27
+ #
28
+ # A connection pool synchronizes thread access to a limited number of
29
+ # database connections. The basic idea is that each thread checks out a
30
+ # database connection from the pool, uses that connection, and checks the
31
+ # connection back in. ConnectionPool is completely thread-safe, and will
32
+ # ensure that a connection cannot be used by two threads at the same time,
33
+ # as long as ConnectionPool's contract is correctly followed. It will also
34
+ # handle cases in which there are more threads than connections: if all
35
+ # connections have been checked out, and a thread tries to checkout a
36
+ # connection anyway, then ConnectionPool will wait until some other thread
37
+ # has checked in a connection.
38
+ #
39
+ # == Obtaining (checking out) a connection
40
+ #
41
+ # Connections can be obtained and used from a connection pool in several
42
+ # ways:
43
+ #
44
+ # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
45
+ # earlier (pre-connection-pooling). Eventually, when you're done with
46
+ # the connection(s) and wish it to be returned to the pool, you call
47
+ # ActiveRecord::Base.clear_active_connections!. This will be the
48
+ # default behavior for Active Record when used in conjunction with
49
+ # Action Pack's request handling cycle.
50
+ # 2. Manually check out a connection from the pool with
51
+ # ActiveRecord::Base.connection_pool.checkout. You are responsible for
52
+ # returning this connection to the pool when finished by calling
53
+ # ActiveRecord::Base.connection_pool.checkin(connection).
54
+ # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
55
+ # obtains a connection, yields it as the sole argument to the block,
56
+ # and returns it to the pool after the block completes.
57
+ #
58
+ # Connections in the pool are actually AbstractAdapter objects (or objects
59
+ # compatible with AbstractAdapter's interface).
60
+ #
61
+ # == Options
62
+ #
63
+ # There are two connection-pooling-related options that you can add to
64
+ # your database connection configuration:
65
+ #
66
+ # * +pool+: number indicating size of connection pool (default 5)
67
+ # * +wait_timeout+: number of seconds to block and wait for a connection
68
+ # before giving up and raising a timeout error (default 5 seconds).
69
+ class ConnectionPool
70
+ attr_reader :spec, :connections
71
+
72
+ # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
73
+ # object which describes database connection information (e.g. adapter,
74
+ # host name, username, password, etc), as well as the maximum size for
75
+ # this ConnectionPool.
76
+ #
77
+ # The default ConnectionPool maximum size is 5.
78
+ def initialize(spec)
79
+ @spec = spec
80
+
81
+ # The cache of reserved connections mapped to threads
82
+ @reserved_connections = {}
83
+
84
+ # The mutex used to synchronize pool access
85
+ @lock = Monitor.new
86
+ # @queue = @lock.new_cond
87
+ @queue = Queue.new(@lock)
88
+
89
+
90
+ # default 5 second timeout unless on ruby 1.9
91
+ @timeout = spec.config[:wait_timeout] || 5
92
+
93
+ # default max pool size to 5
94
+ @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
95
+
96
+ @connections = []
97
+ end
98
+
99
+ # Retrieve the connection associated with the current thread, or call
100
+ # #checkout to obtain one if necessary.
101
+ #
102
+ # #connection can be called any number of times; the connection is
103
+ # held in a hash keyed by the thread id.
104
+ def connection
105
+ synchronize do
106
+ @reserved_connections[current_connection_id] ||= checkout
107
+ end
108
+ end
109
+
110
+ # Signal that the thread is finished with the current connection.
111
+ # #release_connection releases the connection-thread association
112
+ # and returns the connection to the pool.
113
+ def release_connection(with_id = current_connection_id)
114
+ synchronize do
115
+ conn = @reserved_connections.delete(with_id)
116
+ checkin conn if conn
117
+ end
118
+ end
119
+
120
+ # If a connection already exists yield it to the block. If no connection
121
+ # exists checkout a connection, yield it to the block, and checkin the
122
+ # connection when finished.
123
+ def with_connection
124
+ connection_id = current_connection_id
125
+ fresh_connection = synchronize do
126
+ true unless @reserved_connections[connection_id]
127
+ end
128
+ yield connection
129
+ ensure
130
+ release_connection(connection_id) if fresh_connection
131
+ end
132
+
133
+ # Returns true if a connection has already been opened.
134
+ def connected?
135
+ synchronize do
136
+ !@connections.empty?
137
+ end
138
+ end
139
+
140
+ # Disconnects all connections in the pool, and clears the pool.
141
+ def disconnect!
142
+ synchronize do
143
+ @reserved_connections.each do |name,conn|
144
+ checkin conn
145
+ end
146
+ @reserved_connections = {}
147
+ @connections.each do |conn|
148
+ conn.disconnect!
149
+ end
150
+ @connections = []
151
+ end
152
+ end
153
+
154
+ # Clears the cache which maps classes
155
+ def clear_reloadable_connections!
156
+ synchronize do
157
+ @reserved_connections.each do |name, conn|
158
+ checkin conn
159
+ end
160
+ @reserved_connections = {}
161
+ @connections.each do |conn|
162
+ conn.disconnect! if conn.requires_reloading?
163
+ end
164
+ @connections.delete_if do |conn|
165
+ conn.requires_reloading?
166
+ end
167
+ end
168
+ end
169
+
170
+ # Verify active connections and remove and disconnect connections
171
+ # associated with stale threads.
172
+ def verify_active_connections! #:nodoc:
173
+ synchronize do
174
+ clear_stale_cached_connections!
175
+ @connections.each do |connection|
176
+ connection.verify!
177
+ end
178
+ end
179
+ end
180
+
181
+ # Return any checked-out connections back to the pool by threads that
182
+ # are no longer alive.
183
+ def clear_stale_cached_connections!
184
+ synchronize do
185
+ keys = @reserved_connections.keys - Thread.list.find_all { |t|
186
+ t.alive?
187
+ }.map { |thread| thread.object_id }
188
+ keys.each do |key|
189
+ checkin @reserved_connections[key]
190
+ @reserved_connections.delete(key)
191
+ end
192
+ end
193
+ end
194
+
195
+ # Check-out a database connection from the pool, indicating that you want
196
+ # to use it. You should call #checkin when you no longer need this.
197
+ #
198
+ # This is done by either returning an existing connection, or by creating
199
+ # a new connection. If the maximum number of connections for this pool has
200
+ # already been reached, but the pool is empty (i.e. they're all being used),
201
+ # then this method will wait until a thread has checked in a connection.
202
+ # The wait time is bounded however: if no connection can be checked out
203
+ # within the timeout specified for this pool, then a ConnectionTimeoutError
204
+ # exception will be raised.
205
+ #
206
+ # Returns: an AbstractAdapter object.
207
+ #
208
+ # Raises:
209
+ # - ConnectionTimeoutError: no connection can be obtained from the pool
210
+ # within the timeout period.
211
+ def checkout
212
+ if conn = @queue.poll
213
+ checkout_and_verify(conn)
214
+ elsif @connections.size < @size
215
+ checkout_new_connection
216
+ else
217
+ # clear_stale_cached_connections!
218
+ checkout_and_verify(@queue.poll(@timeout))
219
+ end
220
+ end
221
+
222
+ # Check-in a database connection back into the pool, indicating that you
223
+ # no longer need this connection.
224
+ #
225
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
226
+ # calling +checkout+ on this pool.
227
+ def checkin(conn)
228
+ synchronize do
229
+ conn.send(:_run_checkin_callbacks) do
230
+ @queue.add conn
231
+ end
232
+ end
233
+ end
234
+
235
+ private
236
+
237
+ def synchronize(&block)
238
+ @lock.synchronize(&block)
239
+ end
240
+
241
+ def new_connection
242
+ ActiveRecord::Base.send(spec.adapter_method, spec.config)
243
+ end
244
+
245
+ def current_connection_id #:nodoc:
246
+ Thread.current.object_id
247
+ end
248
+
249
+ def checkout_new_connection
250
+ c = new_connection
251
+ @connections << c
252
+ checkout_and_verify(c)
253
+ end
254
+
255
+ def checkout_and_verify(c)
256
+ c.run_callbacks :checkout do
257
+ c.verify!
258
+ end
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 Active Record 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. Active Record models use this to
285
+ # determine that connection pool that they should use.
286
+ class ConnectionHandler
287
+ attr_reader :connection_pools
288
+
289
+ def initialize(pools = {})
290
+ @connection_pools = pools
291
+ end
292
+
293
+ def establish_connection(name, spec)
294
+ @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
295
+ end
296
+
297
+ # Returns any connections in use by the current thread back to the pool,
298
+ # and also returns connections to the pool cached by threads that are no
299
+ # longer alive.
300
+ def clear_active_connections!
301
+ @connection_pools.each_value {|pool| pool.release_connection }
302
+ end
303
+
304
+ # Clears the cache which maps classes
305
+ def clear_reloadable_connections!
306
+ @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
307
+ end
308
+
309
+ def clear_all_connections!
310
+ @connection_pools.each_value {|pool| pool.disconnect! }
311
+ end
312
+
313
+ # Verify active connections.
314
+ def verify_active_connections! #:nodoc:
315
+ @connection_pools.each_value {|pool| pool.verify_active_connections! }
316
+ end
317
+
318
+ # Locate the connection of the nearest super class. This can be an
319
+ # active or defined connection: if it is the latter, it will be
320
+ # opened and set as the active connection for the class it was defined
321
+ # for (not necessarily the current class).
322
+ def retrieve_connection(klass) #:nodoc:
323
+ pool = retrieve_connection_pool(klass)
324
+ (pool && pool.connection) or raise ConnectionNotEstablished
325
+ end
326
+
327
+ # Returns true if a connection that's accessible to this class has
328
+ # already been opened.
329
+ def connected?(klass)
330
+ conn = retrieve_connection_pool(klass)
331
+ conn && conn.connected?
332
+ end
333
+
334
+ # Remove the connection for this class. This will close the active
335
+ # connection and the defined connection (if they exist). The result
336
+ # can be used as an argument for establish_connection, for easily
337
+ # re-establishing the connection.
338
+ def remove_connection(klass)
339
+ pool = @connection_pools[klass.name]
340
+ return nil unless pool
341
+
342
+ @connection_pools.delete_if { |key, value| value == pool }
343
+ pool.disconnect!
344
+ pool.spec.config
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 perform 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
@@ -0,0 +1,114 @@
1
+ #######################################################################
2
+ #
3
+ # This is a fixed version of the connection pool making it threadsafe
4
+ # and fair. Mostly it's copied from 3.0.12's but with the Queue class
5
+ # added -pmahoney
6
+ #
7
+ #######################################################################
8
+
9
+ require 'active_record'
10
+ require 'active_record/connection_adapters/abstract/connection_pool'
11
+ require 'active_record/queue'
12
+
13
+ require 'active_support/core_ext/module/synchronization'
14
+
15
+ module ActiveRecord
16
+ module ConnectionAdapters
17
+ # Monkey Patch time!
18
+ class ConnectionPool
19
+
20
+ alias :orig_initialize :initialize
21
+
22
+ class << self
23
+ def make_synchronized!(*syms)
24
+ syms.each do |sym|
25
+ orig = "__orig_#{sym}__".to_sym
26
+ alias_method orig, sym
27
+ define_method(sym) do |*args, &block|
28
+ synchronize do
29
+ send(orig, *args, &block)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ make_synchronized!(:connection, :active_connection?, :release_connection, :connected?,
37
+ :disconnect!, :clear_reloadable_connections!, :verify_active_connections!,
38
+ :clear_stale_cached_connections!)
39
+
40
+
41
+ def initialize(spec)
42
+ orig_initialize(spec)
43
+
44
+ # The mutex used to synchronize pool access
45
+ @lock = Monitor.new
46
+
47
+ @queue = Queue.new(@lock)
48
+ end
49
+
50
+ def with_connection
51
+ connection_id = current_connection_id
52
+ fresh_connection = synchronize do
53
+ true unless @reserved_connections[connection_id]
54
+ end
55
+ yield connection
56
+ ensure
57
+ release_connection(connection_id) if fresh_connection
58
+ end
59
+
60
+ # Check-out a database connection from the pool, indicating that you want
61
+ # to use it. You should call #checkin when you no longer need this.
62
+ #
63
+ # This is done by either returning an existing connection, or by creating
64
+ # a new connection. If the maximum number of connections for this pool has
65
+ # already been reached, but the pool is empty (i.e. they're all being used),
66
+ # then this method will wait until a thread has checked in a connection.
67
+ # The wait time is bounded however: if no connection can be checked out
68
+ # within the timeout specified for this pool, then a ConnectionTimeoutError
69
+ # exception will be raised.
70
+ #
71
+ # Returns: an AbstractAdapter object.
72
+ #
73
+ # Raises:
74
+ # - ConnectionTimeoutError: no connection can be obtained from the pool
75
+ # within the timeout period.
76
+ def checkout
77
+ if conn = @queue.poll
78
+ checkout_and_verify(conn)
79
+ elsif @connections.size < @size
80
+ checkout_new_connection
81
+ else
82
+ # clear_stale_cached_connections!
83
+ checkout_and_verify(@queue.poll(@timeout))
84
+ end
85
+ end
86
+
87
+ # Check-in a database connection back into the pool, indicating that you
88
+ # no longer need this connection.
89
+ #
90
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
91
+ # calling +checkout+ on this pool.
92
+ def checkin(conn)
93
+ synchronize do
94
+ conn.run_callbacks :checkin do
95
+ @queue.add conn
96
+ end
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def synchronize(&block)
103
+ @lock.synchronize(&block)
104
+ end
105
+
106
+ def checkout_and_verify(c)
107
+ c.run_callbacks :checkout do
108
+ c.verify!
109
+ end
110
+ c
111
+ end
112
+ end
113
+ end
114
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord_threadsafe_fix
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - CG Labs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: version
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1'
62
+ description: Patch ActiveRecord to have a fair, threadsafe connection pool
63
+ email:
64
+ - eng@commongroundpublishing.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - lib/active_record/queue.rb
70
+ - lib/activerecord_threadsafe_fix.rb
71
+ - lib/activerecord_threadsafe_fix_3_0.rb
72
+ - lib/activerecord_threadsafe_fix_3_1.rb
73
+ homepage:
74
+ licenses: []
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 1.8.23
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Patch ActiveRecord to have a fair, threadsafe connection pool
97
+ test_files: []
98
+ has_rdoc: