activerecord-bogacs 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +26 -0
- data/Gemfile +33 -0
- data/LICENSE.txt +22 -0
- data/README.md +124 -0
- data/Rakefile +167 -0
- data/activerecord-bogacs.gemspec +26 -0
- data/lib/active_record/bogacs.rb +55 -0
- data/lib/active_record/bogacs/default_pool.rb +672 -0
- data/lib/active_record/bogacs/false_pool.rb +259 -0
- data/lib/active_record/bogacs/pool_support.rb +21 -0
- data/lib/active_record/bogacs/shareable_pool.rb +255 -0
- data/lib/active_record/bogacs/version.rb +5 -0
- data/lib/active_record/connection_adapters/adapter_compat.rb +57 -0
- data/lib/active_record/shared_connection.rb +24 -0
- data/test/active_record/bogacs/default_pool_test.rb +34 -0
- data/test/active_record/bogacs/false_pool_test.rb +200 -0
- data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +186 -0
- data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +429 -0
- data/test/active_record/bogacs/shareable_pool_helper.rb +81 -0
- data/test/active_record/builtin_pool_test.rb +18 -0
- data/test/active_record/connection_pool_test_methods.rb +336 -0
- data/test/test_helper.rb +304 -0
- metadata +130 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
class BuiltinPoolTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include ConnectionAdapters::ConnectionPoolTestMethods
|
8
|
+
|
9
|
+
def config; AR_CONFIG end
|
10
|
+
|
11
|
+
def setup
|
12
|
+
super
|
13
|
+
@pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
# NOTE: based on connection pool test (from AR's test suite)
|
2
|
+
module ActiveRecord
|
3
|
+
module ConnectionAdapters
|
4
|
+
module ConnectionPoolTestMethods
|
5
|
+
|
6
|
+
attr_reader :pool
|
7
|
+
|
8
|
+
def setup
|
9
|
+
ActiveRecord::Base.establish_connection(config)
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
pool.disconnect! if pool
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_checkout_after_close
|
17
|
+
connection = pool.connection
|
18
|
+
assert connection.in_use?
|
19
|
+
|
20
|
+
connection.close
|
21
|
+
assert ! connection.in_use?
|
22
|
+
|
23
|
+
assert pool.connection.in_use?
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_released_connection_moves_between_threads
|
27
|
+
thread_conn = nil
|
28
|
+
|
29
|
+
Thread.new {
|
30
|
+
pool.with_connection do |conn|
|
31
|
+
thread_conn = conn
|
32
|
+
end
|
33
|
+
}.join
|
34
|
+
|
35
|
+
assert thread_conn
|
36
|
+
|
37
|
+
Thread.new {
|
38
|
+
pool.with_connection do |conn|
|
39
|
+
assert_equal thread_conn, conn
|
40
|
+
end
|
41
|
+
}.join
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_with_connection
|
45
|
+
assert_equal 0, active_connections(pool).size
|
46
|
+
|
47
|
+
main_thread = pool.connection
|
48
|
+
assert_equal 1, active_connections(pool).size
|
49
|
+
|
50
|
+
Thread.new {
|
51
|
+
pool.with_connection do |conn|
|
52
|
+
assert conn
|
53
|
+
assert_equal 2, active_connections(pool).size
|
54
|
+
end
|
55
|
+
assert_equal 1, active_connections(pool).size
|
56
|
+
}.join
|
57
|
+
|
58
|
+
main_thread.close
|
59
|
+
assert_equal 0, active_connections(pool).size
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_active_connection_in_use
|
63
|
+
assert !pool.active_connection?
|
64
|
+
main_thread = pool.connection
|
65
|
+
|
66
|
+
assert pool.active_connection?
|
67
|
+
|
68
|
+
main_thread.close
|
69
|
+
|
70
|
+
assert !pool.active_connection?
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_full_pool_exception
|
74
|
+
pool.size.times { pool.checkout }
|
75
|
+
assert_raise(ConnectionTimeoutError) do
|
76
|
+
pool.checkout
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_full_pool_blocks
|
81
|
+
cs = pool.size.times.map { pool.checkout }
|
82
|
+
t = Thread.new { pool.checkout }
|
83
|
+
|
84
|
+
# make sure our thread is in the timeout section
|
85
|
+
Thread.pass until t.status == "sleep"
|
86
|
+
|
87
|
+
connection = cs.first
|
88
|
+
connection.close
|
89
|
+
assert_equal connection, t.join.value
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_removing_releases_latch
|
93
|
+
cs = pool.size.times.map { pool.checkout }
|
94
|
+
t = Thread.new { pool.checkout }
|
95
|
+
|
96
|
+
# make sure our thread is in the timeout section
|
97
|
+
Thread.pass until t.status == "sleep"
|
98
|
+
|
99
|
+
connection = cs.first
|
100
|
+
pool.remove connection
|
101
|
+
assert_respond_to t.join.value, :execute
|
102
|
+
connection.close
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_reap_and_active
|
106
|
+
pool.checkout
|
107
|
+
pool.checkout
|
108
|
+
pool.checkout
|
109
|
+
|
110
|
+
connections = pool.connections.dup
|
111
|
+
|
112
|
+
pool.reap
|
113
|
+
|
114
|
+
assert_equal connections.length, pool.connections.length
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_reap_inactive
|
118
|
+
ready = Queue.new
|
119
|
+
pool.checkout
|
120
|
+
child = Thread.new do
|
121
|
+
pool.checkout
|
122
|
+
pool.checkout
|
123
|
+
ready.push 42
|
124
|
+
# Thread.stop
|
125
|
+
end
|
126
|
+
ready.pop # awaits
|
127
|
+
|
128
|
+
assert_equal 3, active_connections.size
|
129
|
+
|
130
|
+
child.terminate
|
131
|
+
child.join
|
132
|
+
pool.reap
|
133
|
+
|
134
|
+
# TODO this does not pass on built-in pool (MRI assumption) :
|
135
|
+
#assert_equal 1, active_connections.size
|
136
|
+
ensure
|
137
|
+
pool.connections.each(&:close)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_remove_connection
|
141
|
+
conn = pool.checkout
|
142
|
+
assert conn.in_use?
|
143
|
+
|
144
|
+
length = pool.connections.length
|
145
|
+
pool.remove conn
|
146
|
+
assert conn.in_use?
|
147
|
+
assert_equal(length - 1, pool.connections.length)
|
148
|
+
ensure
|
149
|
+
conn.close if conn
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_remove_connection_for_thread
|
153
|
+
conn = pool.connection
|
154
|
+
pool.remove conn
|
155
|
+
assert_not_equal(conn, pool.connection)
|
156
|
+
ensure
|
157
|
+
conn.close if conn
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_active_connection?
|
161
|
+
assert_false pool.active_connection?
|
162
|
+
assert pool.connection
|
163
|
+
assert_true pool.active_connection?
|
164
|
+
pool.release_connection
|
165
|
+
assert_false pool.active_connection?
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_checkout_behaviour
|
169
|
+
assert connection = pool.connection
|
170
|
+
threads = []
|
171
|
+
4.times do |i|
|
172
|
+
threads << Thread.new(i) do
|
173
|
+
connection = pool.connection
|
174
|
+
assert_not_nil connection
|
175
|
+
connection.close
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
threads.each(&:join)
|
180
|
+
|
181
|
+
Thread.new do
|
182
|
+
assert pool.connection
|
183
|
+
pool.connection.close
|
184
|
+
end.join
|
185
|
+
end
|
186
|
+
|
187
|
+
# The connection pool is "fair" if threads waiting for
|
188
|
+
# connections receive them the order in which they began
|
189
|
+
# waiting. This ensures that we don't timeout one HTTP request
|
190
|
+
# even while well under capacity in a multi-threaded environment
|
191
|
+
# such as a Java servlet container.
|
192
|
+
#
|
193
|
+
# We don't need strict fairness: if two connections become
|
194
|
+
# available at the same time, it's fine of two threads that were
|
195
|
+
# waiting acquire the connections out of order.
|
196
|
+
#
|
197
|
+
# Thus this test prepares waiting threads and then trickles in
|
198
|
+
# available connections slowly, ensuring the wakeup order is
|
199
|
+
# correct in this case.
|
200
|
+
def test_checkout_fairness
|
201
|
+
@pool.instance_variable_set(:@size, 10)
|
202
|
+
expected = (1..@pool.size).to_a.freeze
|
203
|
+
# check out all connections so our threads start out waiting
|
204
|
+
conns = expected.map { @pool.checkout }
|
205
|
+
mutex = Mutex.new
|
206
|
+
order = []
|
207
|
+
errors = []
|
208
|
+
|
209
|
+
threads = expected.map do |i|
|
210
|
+
t = Thread.new {
|
211
|
+
begin
|
212
|
+
@pool.checkout # never checked back in
|
213
|
+
mutex.synchronize { order << i }
|
214
|
+
rescue => e
|
215
|
+
mutex.synchronize { errors << e }
|
216
|
+
end
|
217
|
+
}
|
218
|
+
sleep(0.01) # "tuning" for JRuby
|
219
|
+
Thread.pass until t.status == 'sleep'
|
220
|
+
t
|
221
|
+
end
|
222
|
+
|
223
|
+
# this should wake up the waiting threads one by one in order
|
224
|
+
conns.each { |conn| @pool.checkin(conn); sleep 0.1 }
|
225
|
+
|
226
|
+
threads.each(&:join)
|
227
|
+
|
228
|
+
raise errors.first if errors.any?
|
229
|
+
|
230
|
+
assert_equal(expected, order)
|
231
|
+
end
|
232
|
+
|
233
|
+
# As mentioned in #test_checkout_fairness, we don't care about
|
234
|
+
# strict fairness. This test creates two groups of threads:
|
235
|
+
# group1 whose members all start waiting before any thread in
|
236
|
+
# group2. Enough connections are checked in to wakeup all
|
237
|
+
# group1 threads, and the fact that only group1 and no group2
|
238
|
+
# threads acquired a connection is enforced.
|
239
|
+
def test_checkout_fairness_by_group
|
240
|
+
@pool.instance_variable_set(:@size, 10)
|
241
|
+
# take all the connections
|
242
|
+
conns = (1..10).map { @pool.checkout }
|
243
|
+
mutex = Mutex.new
|
244
|
+
successes = [] # threads that successfully got a connection
|
245
|
+
errors = []
|
246
|
+
|
247
|
+
make_thread = proc do |i|
|
248
|
+
t = Thread.new {
|
249
|
+
begin
|
250
|
+
@pool.checkout # never checked back in
|
251
|
+
mutex.synchronize { successes << i }
|
252
|
+
rescue => e
|
253
|
+
mutex.synchronize { errors << e }
|
254
|
+
end
|
255
|
+
}
|
256
|
+
sleep(0.01) # "tuning" for JRuby
|
257
|
+
Thread.pass until t.status == 'sleep'
|
258
|
+
t
|
259
|
+
end
|
260
|
+
|
261
|
+
# all group1 threads start waiting before any in group2
|
262
|
+
group1 = (1..5).map(&make_thread)
|
263
|
+
sleep(0.05) # "tuning" for JRuby
|
264
|
+
group2 = (6..10).map(&make_thread)
|
265
|
+
|
266
|
+
# checkin n connections back to the pool
|
267
|
+
checkin = proc do |n|
|
268
|
+
n.times do
|
269
|
+
c = conns.pop
|
270
|
+
@pool.checkin(c)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
checkin.call(group1.size) # should wake up all group1
|
275
|
+
|
276
|
+
loop do
|
277
|
+
sleep 0.1
|
278
|
+
break if mutex.synchronize { (successes.size + errors.size) == group1.size }
|
279
|
+
end
|
280
|
+
|
281
|
+
winners = mutex.synchronize { successes.dup }
|
282
|
+
checkin.call(group2.size) # should wake up everyone remaining
|
283
|
+
|
284
|
+
group1.each(&:join); group2.each(&:join)
|
285
|
+
|
286
|
+
assert_equal (1..group1.size).to_a, winners.sort
|
287
|
+
|
288
|
+
if errors.any?
|
289
|
+
raise errors.first
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_automatic_reconnect=
|
294
|
+
assert pool.automatic_reconnect
|
295
|
+
assert pool.connection
|
296
|
+
|
297
|
+
pool.disconnect!
|
298
|
+
assert pool.connection
|
299
|
+
|
300
|
+
pool.disconnect!
|
301
|
+
pool.automatic_reconnect = false
|
302
|
+
|
303
|
+
assert_raise(ConnectionNotEstablished) do
|
304
|
+
pool.connection
|
305
|
+
end
|
306
|
+
|
307
|
+
assert_raise(ConnectionNotEstablished) do
|
308
|
+
pool.with_connection
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def test_pool_sets_connection_visitor
|
313
|
+
assert pool.connection.visitor.is_a?(Arel::Visitors::ToSql)
|
314
|
+
end
|
315
|
+
|
316
|
+
# make sure exceptions are thrown when establish_connection
|
317
|
+
# is called with an anonymous class
|
318
|
+
#def test_anonymous_class_exception
|
319
|
+
#anonymous = Class.new(ActiveRecord::Base)
|
320
|
+
#handler = ActiveRecord::Base.connection_handler
|
321
|
+
|
322
|
+
#assert_raises(RuntimeError) {
|
323
|
+
# handler.establish_connection anonymous, nil
|
324
|
+
#}
|
325
|
+
# assert_raises { handler.establish_connection anonymous, nil }
|
326
|
+
#end
|
327
|
+
|
328
|
+
private
|
329
|
+
|
330
|
+
def active_connections(pool = self.pool)
|
331
|
+
pool.connections.find_all(&:in_use?)
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,304 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
# ENV variables supported :
|
4
|
+
# - AR_POOL_SIZE=42 for testing out with higher pools
|
5
|
+
# - AR_POOL_CHECKOUT_TIMEOUT=2 for changing the pool connection acquire timeout
|
6
|
+
# - AR_POOL_PREFILL=10 how many connections to "prefill" the pool with on start
|
7
|
+
# - AR_POOL_SHARED=true/false or size/percentage (0.0-1.0) connection sharing
|
8
|
+
# - AR_PREPARED_STATEMENTS=true/false
|
9
|
+
|
10
|
+
connect_timeout = 5
|
11
|
+
checkout_timeout = 2.5 # default is to wait 5 seconds when all connections used
|
12
|
+
pool_size = 10
|
13
|
+
#pool_prefill = 10 # (or simply true/false) how many connections to initialize
|
14
|
+
shared_pool = 0.75 # shareable pool true/false or size (integer or percentage)
|
15
|
+
ENV['DB_POOL_SHARED'] ||= 0.5.to_s
|
16
|
+
|
17
|
+
# NOTE: max concurrent threads handled before explicit locking with shared :
|
18
|
+
# pool_size - ( pool_size * shared_pool ) * MAX_THREAD_SHARING (5)
|
19
|
+
# e.g. 40 - ( 40 * 0.75 ) * 5 = 160
|
20
|
+
|
21
|
+
require 'active_record'
|
22
|
+
|
23
|
+
require 'logger'
|
24
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
25
|
+
|
26
|
+
#shared_pool = ENV['AR_POOL_SHARED'] ? # with AR_POOL_SHARED=true use default
|
27
|
+
# ( ENV['AR_POOL_SHARED'] == 'true' ? shared_pool : ENV['AR_POOL_SHARED'] ) :
|
28
|
+
# shared_pool
|
29
|
+
#if shared_pool && shared_pool.to_s != 'false'
|
30
|
+
# shared_pool = Float(shared_pool) rescue nil # to number if number
|
31
|
+
# require 'active_record/connection_adapters/shareable_connection_pool'
|
32
|
+
# # ActiveRecord::Base.default_connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
33
|
+
# pool_class = ActiveRecord::ConnectionAdapters::ShareableConnectionPool
|
34
|
+
# ActiveRecord::ConnectionAdapters::ConnectionHandler.connection_pool_class = pool_class
|
35
|
+
#end
|
36
|
+
|
37
|
+
config = { :'adapter' => ENV['AR_ADAPTER'] || 'sqlite3' }
|
38
|
+
config[:'username'] = ENV['AR_USERNAME'] if ENV['AR_USERNAME']
|
39
|
+
config[:'password'] = ENV['AR_PASSWORD'] if ENV['AR_PASSWORD']
|
40
|
+
if url = ENV['AR_URL'] || ENV['JDBC_URL']
|
41
|
+
config[:'url'] = url
|
42
|
+
else
|
43
|
+
config[:'database'] = ENV['AR_DATABASE'] || 'ar_basin'
|
44
|
+
end
|
45
|
+
|
46
|
+
config[:'pool'] = ENV['AR_POOL_SIZE'] ? ENV['AR_POOL_SIZE'].to_i : pool_size
|
47
|
+
config[:'shared_pool'] = ENV['AR_POOL_SHARED'] || shared_pool
|
48
|
+
config[:'connect_timeout'] = connect_timeout
|
49
|
+
prepared_statements = ENV['AR_PREPARED_STATEMENTS'] # || true
|
50
|
+
config[:'prepared_statements'] = prepared_statements if prepared_statements
|
51
|
+
#jdbc_properties = { 'logUnclosedConnections' => true, 'loginTimeout' => 5 }
|
52
|
+
#config['properties'] = jdbc_properties
|
53
|
+
|
54
|
+
checkout_timeout = ENV['AR_POOL_CHECKOUT_TIMEOUT'] || checkout_timeout
|
55
|
+
config[:'checkout_timeout'] = checkout_timeout.to_f if checkout_timeout
|
56
|
+
|
57
|
+
AR_CONFIG = config
|
58
|
+
|
59
|
+
pool_prefill = ENV['AR_POOL_PREFILL']
|
60
|
+
pool_prefill = config[:'pool'] if pool_prefill.to_s == 'true'
|
61
|
+
pool_prefill = 0 if pool_prefill.to_s == 'false'
|
62
|
+
config[:'pool_prefill'] = pool_prefill.to_i if pool_prefill # NOTE: not yet used
|
63
|
+
|
64
|
+
unless ENV['Rake'] == 'true'
|
65
|
+
gem 'test-unit'
|
66
|
+
require 'test/unit'
|
67
|
+
|
68
|
+
ActiveRecord::Base.logger.debug "database configuration: #{config.inspect}"
|
69
|
+
end
|
70
|
+
|
71
|
+
#if ( pool_prefill = ( pool_prefill || 10 ).to_i ) > 0
|
72
|
+
# pool_prefill = pool.size if pool_prefill > pool.size
|
73
|
+
#
|
74
|
+
# conns = []; start = Time.now
|
75
|
+
# ActiveRecord::Base.logger.info "pre-filling pool with #{pool_prefill}/#{pool.size} connections"
|
76
|
+
# begin
|
77
|
+
# pool_prefill.times { conns << pool.checkout }
|
78
|
+
# ensure
|
79
|
+
# conns.each { |conn| pool.checkin(conn) }
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# # NOTE: for 50 connections ~ 2 seconds but in real time this might get
|
83
|
+
# # slower due synchronization - more threads using the pool = more time
|
84
|
+
# ActiveRecord::Base.logger.debug "pre-filling connection pool took #{Time.now - start}"
|
85
|
+
#end
|
86
|
+
#
|
87
|
+
#if ENV['AR_POOL_BENCHMARK'] && ENV['AR_POOL_BENCHMARK'].to_s != 'false'
|
88
|
+
# require 'active_record/connection_adapters/pool_benchmark'
|
89
|
+
# pool.extend ActiveRecord::ConnectionAdapters::PoolBenchmark
|
90
|
+
#end
|
91
|
+
|
92
|
+
require 'active_record/bogacs'
|
93
|
+
|
94
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
|
95
|
+
|
96
|
+
module ActiveRecord
|
97
|
+
module ConnectionAdapters
|
98
|
+
ConnectionPool.class_eval do
|
99
|
+
attr_reader :size # only since 4.x
|
100
|
+
attr_reader :available # the custom Queue
|
101
|
+
attr_reader :reserved_connections # Thread-Cache
|
102
|
+
# attr_reader :connections # created connections
|
103
|
+
end
|
104
|
+
autoload :ConnectionPoolTestMethods, 'active_record/connection_pool_test_methods'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module ActiveRecord
|
109
|
+
module Bogacs
|
110
|
+
|
111
|
+
module TestHelper
|
112
|
+
|
113
|
+
def _test_name
|
114
|
+
@method_name # @__name__ on mini-test
|
115
|
+
end
|
116
|
+
private :_test_name
|
117
|
+
|
118
|
+
def with_connection(config)
|
119
|
+
ActiveRecord::Base.establish_connection config
|
120
|
+
yield ActiveRecord::Base.connection
|
121
|
+
ensure
|
122
|
+
ActiveRecord::Base.connection.disconnect!
|
123
|
+
end
|
124
|
+
|
125
|
+
def with_connection_removed
|
126
|
+
connection = ActiveRecord::Base.remove_connection
|
127
|
+
begin
|
128
|
+
yield
|
129
|
+
ensure
|
130
|
+
ActiveRecord::Base.establish_connection connection
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# def with_connection_removed
|
135
|
+
# configurations = ActiveRecord::Base.configurations
|
136
|
+
# connection_config = current_connection_config
|
137
|
+
# # ActiveRecord::Base.connection.disconnect!
|
138
|
+
# ActiveRecord::Base.remove_connection
|
139
|
+
# begin
|
140
|
+
# yield connection_config.dup
|
141
|
+
# ensure
|
142
|
+
# # ActiveRecord::Base.connection.disconnect!
|
143
|
+
# ActiveRecord::Base.remove_connection
|
144
|
+
# ActiveRecord::Base.configurations = configurations
|
145
|
+
# ActiveRecord::Base.establish_connection connection_config
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
|
149
|
+
module_function
|
150
|
+
|
151
|
+
def current_connection_config
|
152
|
+
if ActiveRecord::Base.respond_to?(:connection_config)
|
153
|
+
ActiveRecord::Base.connection_config
|
154
|
+
else
|
155
|
+
ActiveRecord::Base.connection_pool.spec.config
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def silence_deprecations(&block)
|
160
|
+
ActiveSupport::Deprecation.silence(&block)
|
161
|
+
end
|
162
|
+
|
163
|
+
# def disable_logger(connection, &block)
|
164
|
+
# raise "need a block" unless block_given?
|
165
|
+
# return disable_connection_logger(connection, &block) if connection
|
166
|
+
# logger = ActiveRecord::Base.logger
|
167
|
+
# begin
|
168
|
+
# ActiveRecord::Base.logger = nil
|
169
|
+
# yield
|
170
|
+
# ensure
|
171
|
+
# ActiveRecord::Base.logger = logger
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# def disable_connection_logger(connection)
|
176
|
+
# logger = connection.send(:instance_variable_get, :@logger)
|
177
|
+
# begin
|
178
|
+
# connection.send(:instance_variable_set, :@logger, nil)
|
179
|
+
# yield
|
180
|
+
# ensure
|
181
|
+
# connection.send(:instance_variable_set, :@logger, logger)
|
182
|
+
# end
|
183
|
+
# end
|
184
|
+
|
185
|
+
protected
|
186
|
+
|
187
|
+
@@sample_query = ENV['SAMPLE_QUERY']
|
188
|
+
@@test_query = ENV['TEST_QUERY'] || @@sample_query
|
189
|
+
|
190
|
+
def sample_query
|
191
|
+
@@sample_query ||= begin
|
192
|
+
case current_connection_config[:adapter]
|
193
|
+
when /mysql/ then 'SHOW VARIABLES LIKE "%version%"'
|
194
|
+
when /postgresql/ then 'SELECT version()'
|
195
|
+
else 'SELECT 42'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_query
|
201
|
+
@@test_query ||= begin
|
202
|
+
case current_connection_config[:adapter]
|
203
|
+
when /mysql/ then 'SELECT DATABASE() FROM DUAL'
|
204
|
+
when /postgresql/ then 'SELECT current_database()'
|
205
|
+
else sample_query
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
module JndiTestHelper
|
213
|
+
|
214
|
+
def setup_jdbc_context
|
215
|
+
load 'test/jars/tomcat-juli.jar'
|
216
|
+
load 'test/jars/tomcat-catalina.jar'
|
217
|
+
|
218
|
+
java.lang.System.set_property(
|
219
|
+
javax.naming.Context::INITIAL_CONTEXT_FACTORY,
|
220
|
+
'org.apache.naming.java.javaURLContextFactory'
|
221
|
+
)
|
222
|
+
java.lang.System.set_property(
|
223
|
+
javax.naming.Context::URL_PKG_PREFIXES,
|
224
|
+
'org.apache.naming'
|
225
|
+
)
|
226
|
+
|
227
|
+
init_context = javax.naming.InitialContext.new
|
228
|
+
begin
|
229
|
+
init_context.create_subcontext 'jdbc'
|
230
|
+
rescue javax.naming.NameAlreadyBoundException
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def init_tomcat_jdbc_data_source(ar_jdbc_config = AR_CONFIG)
|
235
|
+
load 'test/jars/tomcat-jdbc.jar'
|
236
|
+
|
237
|
+
data_source = org.apache.tomcat.jdbc.pool.DataSource.new
|
238
|
+
configure_dbcp_data_source_attributes(data_source, ar_jdbc_config)
|
239
|
+
|
240
|
+
data_source.setJmxEnabled false
|
241
|
+
|
242
|
+
data_source
|
243
|
+
end
|
244
|
+
|
245
|
+
def configure_dbcp_data_source_attributes(data_source, ar_jdbc_config)
|
246
|
+
unless driver = ar_jdbc_config[:driver]
|
247
|
+
jdbc_driver_module.load_driver
|
248
|
+
driver = jdbc_driver_module.driver_name
|
249
|
+
end
|
250
|
+
|
251
|
+
data_source.setDriverClassName driver
|
252
|
+
data_source.setUrl ar_jdbc_config[:url]
|
253
|
+
data_source.setUsername ar_jdbc_config[:username] if ar_jdbc_config[:username]
|
254
|
+
data_source.setPassword ar_jdbc_config[:password] if ar_jdbc_config[:password]
|
255
|
+
if ar_jdbc_config[:properties]
|
256
|
+
properties = java.util.Properties.new
|
257
|
+
properties.putAll ar_jdbc_config[:properties]
|
258
|
+
data_source.setDbProperties properties
|
259
|
+
end
|
260
|
+
# JDBC pool tunings (some mapped from AR configuration) :
|
261
|
+
if ar_jdbc_config[:pool] # default is 100
|
262
|
+
data_source.setMaxActive ar_jdbc_config[:pool]
|
263
|
+
if prefill = ar_jdbc_config[:pool_prefill]
|
264
|
+
data_source.setInitialSize prefill
|
265
|
+
end
|
266
|
+
if data_source.max_active < data_source.max_idle
|
267
|
+
data_source.setMaxIdle data_source.max_active
|
268
|
+
end
|
269
|
+
end
|
270
|
+
max_wait = ar_jdbc_config[:checkout_timeout] || 5
|
271
|
+
data_source.setMaxWait max_wait * 1000 # default is 30s
|
272
|
+
|
273
|
+
data_source.setTestWhileIdle false # default
|
274
|
+
|
275
|
+
#data_source.setRemoveAbandoned false
|
276
|
+
#data_source.setLogAbandoned true
|
277
|
+
end
|
278
|
+
private :configure_dbcp_data_source_attributes
|
279
|
+
|
280
|
+
def bind_data_source(data_source, jndi_name = jndi_config[:jndi])
|
281
|
+
load_driver
|
282
|
+
javax.naming.InitialContext.new.bind jndi_name, data_source
|
283
|
+
end
|
284
|
+
|
285
|
+
def load_driver
|
286
|
+
jdbc_driver_module.load_driver
|
287
|
+
end
|
288
|
+
|
289
|
+
def jdbc_driver_module
|
290
|
+
driver = jndi_config[:adapter]
|
291
|
+
driver = 'postgres' if driver == 'postgresql'
|
292
|
+
require "jdbc/#{driver}"
|
293
|
+
::Jdbc.const_get ::Jdbc.constants.first
|
294
|
+
end
|
295
|
+
|
296
|
+
def jndi_config
|
297
|
+
@jndi_config ||= { :adapter => AR_CONFIG[:adapter], :jndi => jndi_name }
|
298
|
+
end
|
299
|
+
|
300
|
+
def jndi_name; 'jdbc/TestDB' end
|
301
|
+
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|