activerecord_threadsafe_fix 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: