activerecord-bogacs 0.6.0 → 0.7.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 +4 -4
- data/.github/workflows/test.yml +82 -0
- data/Gemfile +2 -15
- data/Rakefile +8 -6
- data/activerecord-bogacs.gemspec +6 -6
- data/lib/active_record/bogacs/connection_handler.rb +1 -1
- data/lib/active_record/bogacs/default_pool.rb +250 -90
- data/lib/active_record/bogacs/false_pool.rb +87 -53
- data/lib/active_record/bogacs/pool_support.rb +26 -8
- data/lib/active_record/bogacs/shareable_pool.rb +11 -15
- data/lib/active_record/bogacs/thread_safe/synchronized.rb +18 -22
- data/lib/active_record/bogacs/thread_safe.rb +3 -90
- data/lib/active_record/bogacs/validator.rb +14 -19
- data/lib/active_record/bogacs/version.rb +1 -1
- data/lib/active_record/connection_adapters/adapter_compat.rb +41 -25
- data/lib/active_record/connection_adapters/pool_class.rb +33 -2
- data/test/active_record/bogacs/false_pool_test.rb +66 -78
- data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +6 -3
- data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +3 -2
- data/test/active_record/bogacs/shareable_pool_helper.rb +1 -1
- data/test/active_record/bogacs/validator_test.rb +22 -28
- data/test/active_record/connection_pool_test_methods.rb +24 -20
- data/test/test_helper.rb +40 -25
- metadata +30 -17
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
require 'concurrent/utility/monotonic_time.rb'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
module ConnectionAdapters
|
@@ -14,8 +15,24 @@ module ActiveRecord
|
|
14
15
|
|
15
16
|
elsif ActiveRecord::VERSION::MAJOR > 4
|
16
17
|
|
17
|
-
# @private added @idle_since
|
18
18
|
# this method must only be called while holding connection pool's mutex
|
19
|
+
def lease
|
20
|
+
if in_use?
|
21
|
+
msg = "Cannot lease connection, ".dup
|
22
|
+
if @owner == Thread.current
|
23
|
+
msg << "it is already leased by the current thread."
|
24
|
+
else
|
25
|
+
msg << "it is already in use by a different thread: #{@owner}. " \
|
26
|
+
"Current thread: #{Thread.current}."
|
27
|
+
end
|
28
|
+
raise ActiveRecordError, msg
|
29
|
+
end
|
30
|
+
|
31
|
+
@owner = Thread.current
|
32
|
+
end
|
33
|
+
|
34
|
+
# this method must only be called while holding connection pool's mutex
|
35
|
+
# @private AR 5.2
|
19
36
|
def expire
|
20
37
|
if in_use?
|
21
38
|
if @owner != Thread.current
|
@@ -24,7 +41,8 @@ module ActiveRecord
|
|
24
41
|
"Current thread: #{Thread.current}."
|
25
42
|
end
|
26
43
|
|
27
|
-
@
|
44
|
+
@idle_since = ::Concurrent.monotonic_time
|
45
|
+
@owner = nil
|
28
46
|
else
|
29
47
|
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
30
48
|
end
|
@@ -41,7 +59,7 @@ module ActiveRecord
|
|
41
59
|
|
42
60
|
# @private added @idle_since
|
43
61
|
def expire
|
44
|
-
@owner = nil; @idle_since = monotonic_time
|
62
|
+
@owner = nil; @idle_since = ::Concurrent.monotonic_time
|
45
63
|
end
|
46
64
|
|
47
65
|
end
|
@@ -73,7 +91,7 @@ module ActiveRecord
|
|
73
91
|
end
|
74
92
|
|
75
93
|
def expire
|
76
|
-
@in_use = false; @owner = nil; @idle_since = monotonic_time
|
94
|
+
@in_use = false; @owner = nil; @idle_since = ::Concurrent.monotonic_time
|
77
95
|
end
|
78
96
|
|
79
97
|
else
|
@@ -87,38 +105,36 @@ module ActiveRecord
|
|
87
105
|
end
|
88
106
|
|
89
107
|
def expire
|
90
|
-
@owner = nil; @idle_since = monotonic_time
|
108
|
+
@owner = nil; @idle_since = ::Concurrent.monotonic_time
|
91
109
|
end
|
92
110
|
|
93
111
|
end
|
94
112
|
|
95
113
|
end
|
96
114
|
|
97
|
-
|
98
|
-
|
99
|
-
if
|
100
|
-
|
101
|
-
|
102
|
-
def monotonic_time; MONOTONIC_CLOCK.get_time end
|
103
|
-
private :monotonic_time
|
115
|
+
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
116
|
+
def steal! # :nodoc:
|
117
|
+
if in_use?
|
118
|
+
if @owner != Thread.current
|
119
|
+
pool.send :release, self, @owner # release exists in both default/false pool
|
104
120
|
|
121
|
+
@owner = Thread.current
|
122
|
+
end
|
105
123
|
else
|
106
|
-
|
107
|
-
def monotonic_time; nil end
|
108
|
-
private :monotonic_time
|
109
|
-
|
110
|
-
warn "activerecord-bogacs failed to load 'concurrent-ruby', '~> 1.0', seconds_idle won't work" if $VERBOSE
|
111
|
-
|
124
|
+
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
112
125
|
end
|
126
|
+
end
|
113
127
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
time = monotonic_time
|
118
|
-
time - ( @idle_since || time )
|
119
|
-
end
|
128
|
+
def discard!
|
129
|
+
# no-op
|
130
|
+
end unless method_defined? :discard! # >= 5.2
|
120
131
|
|
121
|
-
|
132
|
+
# Seconds since this connection was returned to the pool
|
133
|
+
def seconds_idle # :nodoc:
|
134
|
+
return 0 if in_use?
|
135
|
+
time = ::Concurrent.monotonic_time
|
136
|
+
time - ( @idle_since || time )
|
137
|
+
end unless method_defined? :seconds_idle # >= 5.2
|
122
138
|
|
123
139
|
end
|
124
140
|
end
|
@@ -10,14 +10,45 @@ require 'active_record/connection_adapters/abstract/connection_pool'
|
|
10
10
|
module ActiveRecord
|
11
11
|
module ConnectionAdapters
|
12
12
|
# @private there's no other way to change the pool class to use but to patch :(
|
13
|
-
ConnectionHandler
|
13
|
+
class ConnectionHandler
|
14
14
|
|
15
15
|
@@connection_pool_class = ConnectionAdapters::ConnectionPool
|
16
16
|
|
17
17
|
def connection_pool_class; @@connection_pool_class end
|
18
18
|
def self.connection_pool_class=(klass); @@connection_pool_class = klass end
|
19
19
|
|
20
|
-
if ActiveRecord::VERSION::MAJOR >
|
20
|
+
if ActiveRecord::VERSION::MAJOR > 4 && # 5.1 - 5.2
|
21
|
+
!(ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 0)
|
22
|
+
|
23
|
+
def establish_connection(config)
|
24
|
+
resolver = ConnectionSpecification::Resolver.new(Base.configurations)
|
25
|
+
spec = resolver.spec(config)
|
26
|
+
|
27
|
+
remove_connection(spec.name)
|
28
|
+
|
29
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
30
|
+
payload = {
|
31
|
+
connection_id: object_id
|
32
|
+
}
|
33
|
+
if spec
|
34
|
+
payload[:spec_name] = spec.name
|
35
|
+
payload[:config] = spec.config
|
36
|
+
end
|
37
|
+
|
38
|
+
message_bus.instrument("!connection.active_record", payload) do
|
39
|
+
owner_to_pool[spec.name] = connection_pool_class.new(spec) # changed
|
40
|
+
end
|
41
|
+
|
42
|
+
owner_to_pool[spec.name]
|
43
|
+
end
|
44
|
+
|
45
|
+
elsif ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 0
|
46
|
+
|
47
|
+
def establish_connection(spec)
|
48
|
+
owner_to_pool[spec.name] = connection_pool_class.new(spec)
|
49
|
+
end
|
50
|
+
|
51
|
+
elsif ActiveRecord::VERSION::MAJOR > 3 # 4.x
|
21
52
|
|
22
53
|
def establish_connection(owner, spec)
|
23
54
|
@class_to_pool.clear
|
@@ -100,6 +100,20 @@ module ActiveRecord
|
|
100
100
|
assert ActiveRecord::Base.connection.exec_query('SELECT 42')
|
101
101
|
end
|
102
102
|
|
103
|
+
# @override
|
104
|
+
def test_checkout_after_close
|
105
|
+
connection = pool.connection
|
106
|
+
assert connection.in_use?
|
107
|
+
assert_equal connection.object_id, pool.connection.object_id
|
108
|
+
|
109
|
+
connection.close # pool.checkin conn
|
110
|
+
assert ! connection.in_use?
|
111
|
+
|
112
|
+
# NOTE: we do not care for connection re-use - it's okay to instantiate a new one
|
113
|
+
#assert_equal connection.object_id, pool.connection.object_id
|
114
|
+
assert pool.connection.in_use?
|
115
|
+
end
|
116
|
+
|
103
117
|
# @override
|
104
118
|
def test_remove_connection
|
105
119
|
conn = pool.checkout
|
@@ -115,12 +129,13 @@ module ActiveRecord
|
|
115
129
|
|
116
130
|
# @override
|
117
131
|
def test_full_pool_exception
|
132
|
+
ActiveRecord::Base.connection_pool.disconnect! # start clean - with no connections
|
118
133
|
# ~ pool_size.times { pool.checkout }
|
119
134
|
threads_ready = Queue.new; threads_block = Atomic.new(0); threads = []
|
120
135
|
max_pool_size.times do |i|
|
121
136
|
threads << Thread.new do
|
122
137
|
begin
|
123
|
-
conn = ActiveRecord::Base.connection
|
138
|
+
conn = ActiveRecord::Base.connection.tap { |conn| conn.tables }
|
124
139
|
threads_block.update { |v| v + 1 }
|
125
140
|
threads_ready << i
|
126
141
|
while threads_block.value != -1 # await
|
@@ -135,8 +150,26 @@ module ActiveRecord
|
|
135
150
|
end
|
136
151
|
max_pool_size.times { threads_ready.pop } # awaits
|
137
152
|
|
138
|
-
|
139
|
-
|
153
|
+
assert_equal max_pool_size, ActiveRecord::Base.connection_pool.connections.size
|
154
|
+
|
155
|
+
threads.each { |thread| assert thread.alive? }
|
156
|
+
|
157
|
+
#puts "data_source.active: #{data_source.getActive} - #{data_source.getNumActive}"
|
158
|
+
|
159
|
+
begin
|
160
|
+
# NOTE: in AR 4.x and before AR::Base.connection was enough
|
161
|
+
# but due the lazy nature of connection init in AR-JDBC 5X we need to force a connection
|
162
|
+
ActiveRecord::Base.connection.tap { |conn| conn.tables } # ~ pool.checkout
|
163
|
+
rescue ActiveRecordError, java.util.NoSuchElementException => e
|
164
|
+
# DBCP: Java::JavaUtil::NoSuchElementException: Timeout waiting for idle object
|
165
|
+
if ActiveRecord::VERSION::STRING < '5.2'
|
166
|
+
assert_instance_of ConnectionTimeoutError, e
|
167
|
+
else
|
168
|
+
# TODO unfortunately we can not map a TimeoutError and will end up with a StatementInvalid
|
169
|
+
# from the tables call :
|
170
|
+
# assert_instance_of ConnectionTimeoutError, e
|
171
|
+
assert ActiveRecord::Base.connection_pool.send(:timeout_error?, e)
|
172
|
+
end
|
140
173
|
end
|
141
174
|
|
142
175
|
ensure
|
@@ -151,6 +184,7 @@ module ActiveRecord
|
|
151
184
|
t1 = Thread.new do
|
152
185
|
begin
|
153
186
|
conn = ActiveRecord::Base.connection
|
187
|
+
conn.tables # force connection
|
154
188
|
t1_ready.push(conn)
|
155
189
|
t1_block.pop # await
|
156
190
|
rescue => e
|
@@ -165,6 +199,7 @@ module ActiveRecord
|
|
165
199
|
threads << Thread.new do
|
166
200
|
begin
|
167
201
|
conn = ActiveRecord::Base.connection
|
202
|
+
conn.tables # force connection
|
168
203
|
threads_block.update { |v| v + 1 }
|
169
204
|
threads_ready << i
|
170
205
|
while threads_block.value != -1 # await
|
@@ -186,7 +221,7 @@ module ActiveRecord
|
|
186
221
|
|
187
222
|
t2 = Thread.new do
|
188
223
|
begin
|
189
|
-
ActiveRecord::Base.connection
|
224
|
+
ActiveRecord::Base.connection.tap { |conn| conn.tables }
|
190
225
|
rescue => e
|
191
226
|
puts "t2 thread failed: #{e.inspect}"
|
192
227
|
end
|
@@ -214,64 +249,61 @@ module ActiveRecord
|
|
214
249
|
threads && threads.each(&:join)
|
215
250
|
end
|
216
251
|
|
217
|
-
|
252
|
+
# @override
|
253
|
+
def test_pooled_connection_checkin_two
|
254
|
+
checkout_checkin_connections_loop 2, 3
|
218
255
|
|
219
|
-
|
220
|
-
|
256
|
+
assert_equal 3, @connection_count
|
257
|
+
assert_equal 0, @timed_out
|
258
|
+
assert_equal 1, @pool.connections.size
|
221
259
|
end
|
222
260
|
|
223
|
-
|
261
|
+
protected
|
224
262
|
|
225
|
-
|
226
|
-
|
263
|
+
def unwrap_connection(connection)
|
264
|
+
# NOTE: AR-JDBC 5X messed up jdbc_connection(true) - throws a NPE, work-around:
|
265
|
+
connection.tables # force underlying connection into an initialized state ^^^
|
227
266
|
|
228
|
-
|
229
|
-
|
267
|
+
jdbc_connection = connection.jdbc_connection(true)
|
268
|
+
begin
|
269
|
+
jdbc_connection.delegate
|
270
|
+
rescue NoMethodError
|
271
|
+
jdbc_connection
|
272
|
+
end
|
230
273
|
end
|
231
274
|
|
232
|
-
def
|
233
|
-
|
234
|
-
def self.close_data_source
|
235
|
-
@@data_source.send(:close, true) if @@data_source
|
275
|
+
def change_pool_size(size)
|
276
|
+
# noop - @pool.instance_variable_set(:@size, size)
|
236
277
|
end
|
237
278
|
|
238
|
-
def
|
239
|
-
|
240
|
-
def teardown
|
241
|
-
self.class.close_data_source
|
279
|
+
def change_pool_checkout_timeout(timeout)
|
280
|
+
# noop - @pool.instance_variable_set(:@checkout_timeout, timeout)
|
242
281
|
end
|
243
282
|
|
244
283
|
end
|
245
284
|
|
246
|
-
class
|
285
|
+
class ConnectionPoolWrappingTomcatJdbcDataSourceTest < TestBase
|
247
286
|
include ConnectionPoolWrappingDataSourceTestMethods
|
248
287
|
|
249
288
|
def self.build_data_source(config)
|
250
|
-
|
289
|
+
build_tomcat_jdbc_data_source(config)
|
251
290
|
end
|
252
291
|
|
253
|
-
def self.jndi_name; 'jdbc/
|
292
|
+
def self.jndi_name; 'jdbc/TestTomcatJdbcDB' end
|
254
293
|
|
255
294
|
def self.close_data_source
|
256
|
-
@@data_source.close if @@data_source
|
295
|
+
@@data_source.send(:close, true) if @@data_source
|
257
296
|
end
|
258
297
|
|
259
298
|
def max_pool_size; @@data_source.max_active end
|
260
299
|
|
261
300
|
def teardown
|
262
|
-
self.class.
|
263
|
-
end
|
264
|
-
|
265
|
-
protected
|
266
|
-
|
267
|
-
def unwrap_connection(connection)
|
268
|
-
connection = connection.jdbc_connection(true)
|
269
|
-
connection.delegate
|
301
|
+
self.class.close_data_source
|
270
302
|
end
|
271
303
|
|
272
304
|
end
|
273
305
|
|
274
|
-
class
|
306
|
+
class ConnectionPoolWrappingTomcatDbcpDataSourceTest < TestBase
|
275
307
|
include ConnectionPoolWrappingDataSourceTestMethods
|
276
308
|
|
277
309
|
def self.build_data_source(config)
|
@@ -292,50 +324,6 @@ module ActiveRecord
|
|
292
324
|
|
293
325
|
protected
|
294
326
|
|
295
|
-
def unwrap_connection(connection)
|
296
|
-
connection = connection.jdbc_connection(true)
|
297
|
-
connection.delegate
|
298
|
-
end
|
299
|
-
|
300
|
-
end
|
301
|
-
|
302
|
-
class ConnectionPoolWrappingC3P0DataSourceTest < TestBase
|
303
|
-
include ConnectionPoolWrappingDataSourceTestMethods
|
304
|
-
|
305
|
-
def self.build_data_source(config)
|
306
|
-
build_c3p0_data_source(config)
|
307
|
-
end
|
308
|
-
|
309
|
-
def self.jndi_config
|
310
|
-
config = super
|
311
|
-
config[:connection_alive_sql] = 'SELECT 1' if old_c3p0?
|
312
|
-
config
|
313
|
-
end
|
314
|
-
|
315
|
-
def self.old_c3p0?
|
316
|
-
if c3p0_jar = $CLASSPATH.find { |jar| jar =~ /c3p0/ }
|
317
|
-
if match = File.basename(c3p0_jar).match(/c3p0\-(.*).jar/)
|
318
|
-
return true if match[1] <= '0.9.2.1'
|
319
|
-
end
|
320
|
-
return false
|
321
|
-
end
|
322
|
-
nil
|
323
|
-
end
|
324
|
-
|
325
|
-
def test_full_pool_blocks
|
326
|
-
return if self.class.old_c3p0?
|
327
|
-
super
|
328
|
-
end
|
329
|
-
|
330
|
-
def self.jndi_name; 'jdbc/TestC3P0DB' end
|
331
|
-
|
332
|
-
def max_pool_size; @@data_source.max_pool_size end
|
333
|
-
|
334
|
-
def teardown
|
335
|
-
# self.class.close_data_source # @@data_source = nil
|
336
|
-
self.class.establish_jndi_connection # for next test
|
337
|
-
end
|
338
|
-
|
339
327
|
end
|
340
328
|
|
341
329
|
class ConnectionPoolWrappingHikariDataSourceTest < TestBase
|
@@ -368,4 +356,4 @@ module ActiveRecord
|
|
368
356
|
|
369
357
|
end
|
370
358
|
end
|
371
|
-
end
|
359
|
+
end
|
@@ -4,7 +4,8 @@ module ActiveRecord
|
|
4
4
|
module Bogacs
|
5
5
|
class ShareablePool
|
6
6
|
|
7
|
-
|
7
|
+
# TODO: ShareablePool is pretty much broken since 0.7 :
|
8
|
+
class ConnectionPoolTest #< TestBase
|
8
9
|
|
9
10
|
include ConnectionAdapters::ConnectionPoolTestMethods
|
10
11
|
|
@@ -24,7 +25,8 @@ module ActiveRecord
|
|
24
25
|
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
|
+
# TODO: ShareablePool is pretty much broken since 0.7 :
|
29
|
+
class PoolAPITest #< TestBase
|
28
30
|
|
29
31
|
def setup; ActiveRecord::Base.connection end
|
30
32
|
# def teardown; ActiveRecord::Base.connection_pool.reap end
|
@@ -123,7 +125,8 @@ module ActiveRecord
|
|
123
125
|
|
124
126
|
end
|
125
127
|
|
126
|
-
|
128
|
+
# TODO: ShareablePool is pretty much broken since 0.7 :
|
129
|
+
class CustomAPITest #< TestBase
|
127
130
|
|
128
131
|
def setup
|
129
132
|
connection_pool.disconnect!
|
@@ -4,7 +4,8 @@ module ActiveRecord
|
|
4
4
|
module Bogacs
|
5
5
|
class ShareablePool
|
6
6
|
|
7
|
-
|
7
|
+
# TODO: ShareablePool is pretty much broken since 0.7 :
|
8
|
+
class ConnectionSharingTest #< TestBase
|
8
9
|
include TestHelper
|
9
10
|
|
10
11
|
def setup
|
@@ -170,7 +171,7 @@ module ActiveRecord
|
|
170
171
|
assert shared_connection?(conn)
|
171
172
|
conn
|
172
173
|
end
|
173
|
-
|
174
|
+
|
174
175
|
assert_equal 5, shared_conns.uniq.size
|
175
176
|
|
176
177
|
# still one left for normal connections :
|
@@ -49,7 +49,7 @@ module ActiveRecord
|
|
49
49
|
require 'concurrent/atomic/semaphore.rb'
|
50
50
|
Semaphore = ::Concurrent::Semaphore
|
51
51
|
|
52
|
-
def
|
52
|
+
def test_removes_non_used_connections_from_pool_when_collecting_connections_to_validate
|
53
53
|
assert_equal [], validator.send(:connections)
|
54
54
|
|
55
55
|
count = AtomicFixnum.new
|
@@ -67,15 +67,15 @@ module ActiveRecord
|
|
67
67
|
pool.with_connection { |conn| assert released_conn = conn }
|
68
68
|
}.join
|
69
69
|
|
70
|
-
|
71
70
|
assert_equal 3, pool.connections.size
|
72
|
-
|
73
|
-
assert_equal
|
74
|
-
|
75
|
-
|
71
|
+
validator.send(:connections)
|
72
|
+
assert_equal 2, pool.connections.size
|
73
|
+
assert_false pool.connections.include?(released_conn)
|
74
|
+
ensure
|
75
|
+
semaphore.release 2 if semaphore
|
76
76
|
end
|
77
77
|
|
78
|
-
def
|
78
|
+
def test_removes_stale_connection_from_pool_when_collecting_connections_to_validate
|
79
79
|
conn = connection
|
80
80
|
|
81
81
|
assert_equal [], validator.send(:connections)
|
@@ -91,23 +91,15 @@ module ActiveRecord
|
|
91
91
|
}
|
92
92
|
while count.value < 2; sleep 0.01 end
|
93
93
|
|
94
|
-
|
95
|
-
Thread.new {
|
96
|
-
pool.with_connection { |conn| assert returned_conn = conn }
|
97
|
-
}.join
|
98
|
-
|
99
|
-
assert_equal 4, pool.connections.size
|
94
|
+
assert_equal 3, pool.connections.size
|
100
95
|
validate_candidates = validator.send(:connections)
|
101
|
-
assert_equal
|
102
|
-
assert_equal 4, pool.connections.size
|
96
|
+
assert_equal 3, pool.connections.size
|
103
97
|
|
104
98
|
semaphore.release(2); sleep 0.05
|
105
99
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
assert ! validate_candidates.include?(stale_conn)
|
110
|
-
assert_equal 3, pool.connections.size
|
100
|
+
validator.send(:connections)
|
101
|
+
assert ! pool.connections.include?(stale_conn)
|
102
|
+
assert_equal 1, pool.connections.size
|
111
103
|
assert ! pool.connections.map(&:object_id).include?(stale_conn.object_id)
|
112
104
|
end
|
113
105
|
|
@@ -184,13 +176,13 @@ module ActiveRecord
|
|
184
176
|
end
|
185
177
|
|
186
178
|
|
187
|
-
def test_validate_returns_invalid_connection_count
|
188
|
-
conn = connection; threads = []; invalid_conns =
|
189
|
-
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.
|
190
|
-
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.
|
191
|
-
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.
|
192
|
-
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.
|
193
|
-
threads.each(&:join)
|
179
|
+
def test_validate_returns_invalid_connection_count; require 'concurrent/array'
|
180
|
+
done = false; conn = connection; threads = []; invalid_conns = Concurrent::Array.new
|
181
|
+
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.2); sleep(0.1) until done } }
|
182
|
+
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.2); conn.expire; invalid_conns << conn; sleep(0.05) until done } }
|
183
|
+
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.2); sleep(0.1) until done } }
|
184
|
+
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.2); conn.expire; invalid_conns << conn; sleep(0.05) until done } }
|
185
|
+
sleep(0.1) until invalid_conns.size == 2 # threads.each(&:join)
|
194
186
|
assert_equal 5, pool.connections.size
|
195
187
|
|
196
188
|
invalid_conns.each { |conn| def conn.active?; false end }
|
@@ -203,6 +195,8 @@ module ActiveRecord
|
|
203
195
|
result = validator.validate
|
204
196
|
assert_equal 0, result
|
205
197
|
assert_equal 3, pool.connections.size
|
198
|
+
ensure
|
199
|
+
done = true
|
206
200
|
end
|
207
201
|
|
208
202
|
private
|
@@ -245,4 +239,4 @@ module ActiveRecord
|
|
245
239
|
|
246
240
|
end
|
247
241
|
end
|
248
|
-
end
|
242
|
+
end
|
@@ -10,18 +10,19 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def teardown
|
13
|
-
pool.
|
13
|
+
pool.discard! if pool
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_checkout_after_close
|
17
17
|
connection = pool.connection
|
18
18
|
assert connection.in_use?
|
19
|
+
assert_equal connection.object_id, pool.connection.object_id
|
19
20
|
|
20
|
-
connection.close
|
21
|
+
connection.close # pool.checkin conn
|
21
22
|
assert ! connection.in_use?
|
22
23
|
|
23
|
-
assert_equal connection, pool.connection
|
24
|
-
|
24
|
+
assert_equal connection.object_id, pool.connection.object_id
|
25
|
+
assert pool.connection.in_use?
|
25
26
|
end
|
26
27
|
|
27
28
|
def test_released_connection_moves_between_threads
|
@@ -134,8 +135,6 @@ module ActiveRecord
|
|
134
135
|
|
135
136
|
# TODO this does not pass on built-in pool (MRI assumption) :
|
136
137
|
#assert_equal 1, active_connections.size
|
137
|
-
ensure
|
138
|
-
pool.connections.each(&:close)
|
139
138
|
end
|
140
139
|
|
141
140
|
def test_remove_connection
|
@@ -159,15 +158,12 @@ module ActiveRecord
|
|
159
158
|
end
|
160
159
|
|
161
160
|
def test_active_connection?
|
162
|
-
|
161
|
+
assert ! pool.active_connection?
|
163
162
|
assert pool.connection
|
164
|
-
|
165
|
-
|
166
|
-
else
|
167
|
-
assert pool.active_connection?
|
168
|
-
end
|
163
|
+
assert pool.active_connection? # true or returns owner thread (alias :in_use? :owner)
|
164
|
+
#assert_equal Thread, pool.active_connection?.class
|
169
165
|
pool.release_connection
|
170
|
-
|
166
|
+
assert ! pool.active_connection?
|
171
167
|
end
|
172
168
|
|
173
169
|
def test_checkout_behaviour
|
@@ -177,7 +173,6 @@ module ActiveRecord
|
|
177
173
|
threads << Thread.new(i) do
|
178
174
|
connection = pool.connection
|
179
175
|
assert_not_nil connection
|
180
|
-
connection.close
|
181
176
|
end
|
182
177
|
end
|
183
178
|
|
@@ -185,7 +180,7 @@ module ActiveRecord
|
|
185
180
|
|
186
181
|
Thread.new do
|
187
182
|
assert pool.connection
|
188
|
-
pool.connection.
|
183
|
+
pool.connection.tables
|
189
184
|
end.join
|
190
185
|
end
|
191
186
|
|
@@ -332,7 +327,7 @@ module ActiveRecord
|
|
332
327
|
|
333
328
|
def test_pooled_connection_remove
|
334
329
|
# ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.5}))
|
335
|
-
|
330
|
+
change_pool_size(2)
|
336
331
|
# old_connection = ActiveRecord::Base.connection
|
337
332
|
old_connection = @pool.connection
|
338
333
|
# extra_connection = ActiveRecord::Base.connection_pool.checkout
|
@@ -340,11 +335,12 @@ module ActiveRecord
|
|
340
335
|
# ActiveRecord::Base.connection_pool.remove(extra_connection)
|
341
336
|
@pool.remove(extra_connection)
|
342
337
|
# assert_equal ActiveRecord::Base.connection, old_connection
|
343
|
-
assert_equal @pool.connection
|
338
|
+
assert_equal old_connection.object_id, @pool.connection.object_id
|
344
339
|
end
|
345
340
|
|
346
341
|
def test_pooled_connection_checkin_two
|
347
342
|
checkout_checkin_connections_loop 2, 3
|
343
|
+
|
348
344
|
assert_equal 3, @connection_count
|
349
345
|
assert_equal 0, @timed_out
|
350
346
|
# assert_equal 2, ActiveRecord::Base.connection_pool.connections.size
|
@@ -355,8 +351,8 @@ module ActiveRecord
|
|
355
351
|
|
356
352
|
def checkout_checkin_connections_loop(pool_size, loops)
|
357
353
|
# ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5}))
|
358
|
-
|
359
|
-
|
354
|
+
change_pool_size(pool_size)
|
355
|
+
change_pool_checkout_timeout(0.5)
|
360
356
|
|
361
357
|
@connection_count = 0; @timed_out = 0
|
362
358
|
loops.times do
|
@@ -376,6 +372,14 @@ module ActiveRecord
|
|
376
372
|
end
|
377
373
|
end
|
378
374
|
|
375
|
+
def change_pool_size(size)
|
376
|
+
@pool.instance_variable_set(:@size, size)
|
377
|
+
end
|
378
|
+
|
379
|
+
def change_pool_checkout_timeout(timeout)
|
380
|
+
@pool.instance_variable_set(:@checkout_timeout, timeout)
|
381
|
+
end
|
382
|
+
|
379
383
|
private
|
380
384
|
|
381
385
|
def active_connections(pool = self.pool)
|
@@ -384,4 +388,4 @@ module ActiveRecord
|
|
384
388
|
|
385
389
|
end
|
386
390
|
end
|
387
|
-
end
|
391
|
+
end
|