activerecord-bogacs 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -10
- data/Gemfile +1 -1
- data/lib/active_record/bogacs.rb +2 -0
- data/lib/active_record/bogacs/default_pool.rb +22 -31
- data/lib/active_record/bogacs/false_pool.rb +6 -8
- data/lib/active_record/bogacs/pool_support.rb +8 -0
- data/lib/active_record/bogacs/reaper.rb +86 -0
- data/lib/active_record/bogacs/validator.rb +182 -0
- data/lib/active_record/bogacs/version.rb +1 -1
- data/lib/active_record/connection_adapters/adapter_compat.rb +37 -1
- data/test/active_record/bogacs/default_pool_test.rb +29 -0
- data/test/active_record/bogacs/reaper_test.rb +86 -0
- data/test/active_record/bogacs/validator_test.rb +248 -0
- data/test/active_record/connection_pool_test_methods.rb +2 -1
- data/test/test_helper.rb +11 -4
- metadata +29 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3da20a4a7e44c77643b1cf11b949e5fb818d43ca
|
4
|
+
data.tar.gz: 4bd02da78f04b586079c01ef3d3429d13821337e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc86fe344a38859c20e2c41c4017a5d5e3d1c802a2879cc80376c8f9f286d7ec9496851217f98197869558edfb08aca4c37eab0c873a4b1538a6f2d1aadbd39b
|
7
|
+
data.tar.gz: 8de7842694c6ad26a9a904c0c8656dc5b1a6924771e3c8a106a53a917f82928a59bfec09123317c93813d89c9a009e1e99c392fa2549d0e6a23e71dc6c805a65
|
data/.travis.yml
CHANGED
@@ -5,7 +5,7 @@ jdk:
|
|
5
5
|
- oraclejdk7
|
6
6
|
- oraclejdk8
|
7
7
|
rvm:
|
8
|
-
- jruby-1.7.
|
8
|
+
- jruby-1.7.24
|
9
9
|
#- 2.1.2
|
10
10
|
before_install:
|
11
11
|
- ((jruby -v | grep 1.8.7) && jruby --1.9 -S gem update --system 2.1.11) || true
|
@@ -29,35 +29,35 @@ matrix:
|
|
29
29
|
#allow_failures:
|
30
30
|
#- rvm: jruby-head
|
31
31
|
exclude:
|
32
|
-
- rvm: jruby-1.7.
|
32
|
+
- rvm: jruby-1.7.24
|
33
33
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=mysql AR_VERSION="~> 4.1.9" HIKARI_VERSION="2.3.12"
|
34
34
|
jdk: oraclejdk7
|
35
|
-
- rvm: jruby-1.7.
|
35
|
+
- rvm: jruby-1.7.24
|
36
36
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=postgresql AR_VERSION="~> 4.1.9" HIKARI_VERSION="2.3.2-java6"
|
37
37
|
jdk: oraclejdk8
|
38
|
-
- rvm: jruby-1.7.
|
38
|
+
- rvm: jruby-1.7.24
|
39
39
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=mysql AR_VERSION="~> 4.1.13" HIKARI_VERSION="2.0.1-java6"
|
40
40
|
jdk: oraclejdk8
|
41
|
-
- rvm: jruby-1.7.
|
41
|
+
- rvm: jruby-1.7.24
|
42
42
|
env: JRUBY_OPTS="--1.8 $JRUBY_OPTS" AR_ADAPTER=mysql AR_VERSION="~> 3.2.18" HIKARI_VERSION="2.2.5-java6"
|
43
43
|
jdk: oraclejdk8
|
44
44
|
include:
|
45
|
-
- rvm: jruby-9.0.
|
45
|
+
- rvm: jruby-9.0.5.0
|
46
46
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=mysql AR_VERSION="~> 4.1.13" HIKARI_VERSION="2.2.5-java6"
|
47
47
|
jdk: oraclejdk7
|
48
|
-
- rvm: jruby-9.
|
48
|
+
- rvm: jruby-9.1.2.0
|
49
49
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=mysql AR_VERSION="~> 3.2.18" HIKARI_VERSION="2.3.9"
|
50
50
|
jdk: oraclejdk8
|
51
51
|
# AR 4.2
|
52
52
|
- rvm: jruby-1.7.22
|
53
53
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=mysql AR_VERSION="~> 4.1.13" HIKARI_VERSION="2.2.5-java6"
|
54
54
|
jdk: oraclejdk7
|
55
|
-
- rvm: jruby-9.
|
55
|
+
- rvm: jruby-9.1.2.0
|
56
56
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=mysql AR_VERSION="~> 4.2.4" HIKARI_VERSION="2.3.12"
|
57
57
|
jdk: oraclejdk8
|
58
58
|
- rvm: jruby-1.7.22
|
59
59
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=postgresql AR_VERSION="~> 4.2.4" HIKARI_VERSION="2.2.5-java6"
|
60
60
|
jdk: oraclejdk8
|
61
|
-
- rvm: jruby-9.0.
|
61
|
+
- rvm: jruby-9.0.5.0
|
62
62
|
env: JRUBY_OPTS="$JRUBY_OPTS" AR_ADAPTER=postgresql AR_VERSION="~> 4.1.13" HIKARI_VERSION="2.3.12"
|
63
|
-
jdk: oraclejdk7
|
63
|
+
jdk: oraclejdk7
|
data/Gemfile
CHANGED
@@ -21,7 +21,7 @@ if RUBY_VERSION.index('1.8') == 0
|
|
21
21
|
gem 'atomic', '1.1.16' # concurrent-ruby gem only for Ruby version >= 1.9.3
|
22
22
|
gem 'thread_safe', '~> 0.3'
|
23
23
|
else
|
24
|
-
gem 'concurrent-ruby', '1.0.0
|
24
|
+
gem 'concurrent-ruby', '~> 1.0.0', :require => nil
|
25
25
|
end
|
26
26
|
|
27
27
|
platform :jruby do
|
data/lib/active_record/bogacs.rb
CHANGED
@@ -9,6 +9,8 @@ module ActiveRecord
|
|
9
9
|
autoload :DefaultPool, 'active_record/bogacs/default_pool'
|
10
10
|
autoload :FalsePool, 'active_record/bogacs/false_pool'
|
11
11
|
autoload :ShareablePool, 'active_record/bogacs/shareable_pool'
|
12
|
+
autoload :Reaper, 'active_record/bogacs/reaper'
|
13
|
+
autoload :Validator, 'active_record/bogacs/validator'
|
12
14
|
end
|
13
15
|
autoload :SharedConnection, 'active_record/shared_connection'
|
14
16
|
end
|
@@ -51,7 +51,7 @@ module ActiveRecord
|
|
51
51
|
# stdlib's doesn't support waiting with a timeout.
|
52
52
|
# @private
|
53
53
|
class Queue
|
54
|
-
def initialize(lock
|
54
|
+
def initialize(lock)
|
55
55
|
@lock = lock
|
56
56
|
@cond = @lock.new_cond
|
57
57
|
@num_waiting = 0
|
@@ -177,36 +177,13 @@ module ActiveRecord
|
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
-
# Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
|
181
|
-
# A reaper instantiated with a nil frequency will never reap the
|
182
|
-
# connection pool.
|
183
|
-
#
|
184
|
-
# Configure the frequency by setting "reaping_frequency" in your
|
185
|
-
# database yaml file.
|
186
|
-
class Reaper
|
187
|
-
attr_reader :pool, :frequency
|
188
|
-
|
189
|
-
def initialize(pool, frequency)
|
190
|
-
@pool = pool
|
191
|
-
@frequency = frequency
|
192
|
-
end
|
193
|
-
|
194
|
-
def run
|
195
|
-
return unless frequency
|
196
|
-
Thread.new(frequency, pool) { |t, p|
|
197
|
-
while true
|
198
|
-
sleep t
|
199
|
-
p.reap
|
200
|
-
end
|
201
|
-
}
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
180
|
include PoolSupport
|
206
181
|
include MonitorMixin # TODO consider avoiding ?!
|
207
182
|
|
183
|
+
require 'active_record/bogacs/reaper.rb'
|
184
|
+
|
208
185
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
209
|
-
attr_reader :spec, :connections, :size, :reaper
|
186
|
+
attr_reader :spec, :connections, :size, :reaper, :validator
|
210
187
|
attr_reader :initial_size
|
211
188
|
|
212
189
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
@@ -248,6 +225,14 @@ module ActiveRecord
|
|
248
225
|
initial_size = (@size * initial_size).to_i if initial_size <= 1.0
|
249
226
|
# NOTE: warn on onitial_size > size !
|
250
227
|
prefill_initial_connections if ( @initial_size = initial_size.to_i ) > 0
|
228
|
+
|
229
|
+
if frequency = spec.config[:validate_frequency]
|
230
|
+
require 'active_record/bogacs/validator' unless self.class.const_defined?(:Validator)
|
231
|
+
@validator = Validator.new self, frequency, spec.config[:validate_timeout]
|
232
|
+
if @validator.run && @reaping
|
233
|
+
logger && logger.info("pool: validator configured alongside with reaper")
|
234
|
+
end
|
235
|
+
end
|
251
236
|
end
|
252
237
|
|
253
238
|
# Retrieve the connection associated with the current thread, or call
|
@@ -438,6 +423,16 @@ module ActiveRecord
|
|
438
423
|
end
|
439
424
|
# NOTE: active? and reset! are >= AR 2.3
|
440
425
|
|
426
|
+
def reaper?; (@reaper ||= nil) && @reaper.frequency end
|
427
|
+
def reaping?; reaper? && @reaper.running? end
|
428
|
+
|
429
|
+
def validator?; (@validator ||= nil) && @validator.frequency end
|
430
|
+
def validating?; validator? && @validator.running? end
|
431
|
+
|
432
|
+
#@@logger = nil
|
433
|
+
def logger; ::ActiveRecord::Base.logger end
|
434
|
+
#def logger=(logger); @@logger = logger end
|
435
|
+
|
441
436
|
private
|
442
437
|
|
443
438
|
# Acquire a connection by one of 1) immediately removing one
|
@@ -490,10 +485,6 @@ module ActiveRecord
|
|
490
485
|
conns
|
491
486
|
end
|
492
487
|
|
493
|
-
#@@logger = nil
|
494
|
-
def logger; ::ActiveRecord::Base.logger end
|
495
|
-
#def logger=(logger); @@logger = logger end
|
496
|
-
|
497
488
|
end
|
498
489
|
|
499
490
|
end
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
|
19
19
|
@spec = spec
|
20
20
|
@size = nil
|
21
|
-
|
21
|
+
@automatic_reconnect = nil
|
22
22
|
|
23
23
|
@reserved_connections = ThreadSafe::Map.new #:initial_capacity => @size
|
24
24
|
end
|
@@ -105,10 +105,7 @@ module ActiveRecord
|
|
105
105
|
def verify_active_connections!
|
106
106
|
synchronize do
|
107
107
|
clear_stale_cached_connections!
|
108
|
-
|
109
|
-
connections.each do |connection|
|
110
|
-
connection.verify!
|
111
|
-
end
|
108
|
+
@reserved_connections.values.each(&:verify!)
|
112
109
|
end
|
113
110
|
end if ActiveRecord::VERSION::MAJOR < 4
|
114
111
|
|
@@ -232,18 +229,19 @@ module ActiveRecord
|
|
232
229
|
# conn
|
233
230
|
#end
|
234
231
|
|
232
|
+
TIMEOUT_ERROR = /timeout|timed.?out/i
|
233
|
+
|
235
234
|
def timeout_error?(error)
|
236
235
|
full_error = error.inspect
|
237
|
-
#
|
236
|
+
# Tomcat JDBC :
|
238
237
|
# ActiveRecord::JDBCError(<The driver encountered an unknown error:
|
239
238
|
# org.apache.tomcat.jdbc.pool.PoolExhaustedException:
|
240
239
|
# [main] Timeout: Pool empty. Unable to fetch a connection in 2 seconds,
|
241
240
|
# none available[size:10; busy:10; idle:0; lastwait:2500].>
|
242
241
|
# )
|
243
|
-
return true if full_error =~ /timeout/i
|
244
242
|
# C3P0 :
|
245
243
|
# java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
|
246
|
-
return true if full_error =~
|
244
|
+
return true if full_error =~ TIMEOUT_ERROR
|
247
245
|
# NOTE: not sure what to do on MRI and friends (C-pools not tested)
|
248
246
|
false
|
249
247
|
end
|
@@ -50,6 +50,10 @@ module ActiveRecord
|
|
50
50
|
conn.expire
|
51
51
|
end
|
52
52
|
end
|
53
|
+
rescue => e
|
54
|
+
remove conn
|
55
|
+
conn.disconnect! rescue nil
|
56
|
+
raise e
|
53
57
|
end
|
54
58
|
|
55
59
|
def _run_checkout_callbacks(conn)
|
@@ -62,6 +66,10 @@ module ActiveRecord
|
|
62
66
|
conn.verify!
|
63
67
|
end
|
64
68
|
end
|
69
|
+
rescue => e
|
70
|
+
remove conn
|
71
|
+
conn.disconnect! rescue nil
|
72
|
+
raise e
|
65
73
|
end
|
66
74
|
|
67
75
|
else
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Bogacs
|
5
|
+
|
6
|
+
# Every +frequency+ seconds, the reaper will "reap" a pool it belongs to.
|
7
|
+
# Reaping means detecting stale cached connections in the pool - those that
|
8
|
+
# were checked-out by a thread but never checked back in after the thread
|
9
|
+
# finished/died.
|
10
|
+
#
|
11
|
+
# @note This version is fail safe - raised errors won't stop the reaping process.
|
12
|
+
# Instead the thread will be restarted and reaping continues at the next tick.
|
13
|
+
#
|
14
|
+
# Configure the frequency by setting `:reaping_frequency` in your database yaml file.
|
15
|
+
#
|
16
|
+
class Reaper
|
17
|
+
|
18
|
+
attr_reader :pool, :frequency
|
19
|
+
attr_accessor :retry_error
|
20
|
+
|
21
|
+
# Reaper.new(self, spec.config[:reaping_frequency]).run
|
22
|
+
# @private
|
23
|
+
def initialize(pool, frequency)
|
24
|
+
@pool = pool;
|
25
|
+
if frequency
|
26
|
+
frequency = frequency.to_f
|
27
|
+
@frequency = frequency > 0.0 ? frequency : false
|
28
|
+
else
|
29
|
+
@frequency = nil
|
30
|
+
end
|
31
|
+
@retry_error = 1.5; @running = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
return unless frequency
|
36
|
+
@running = true; start
|
37
|
+
end
|
38
|
+
|
39
|
+
def start(delay = nil)
|
40
|
+
Thread.new { exec(delay) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def running?; @running end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def exec(delay = nil)
|
48
|
+
sleep delay if delay
|
49
|
+
while true
|
50
|
+
begin
|
51
|
+
sleep frequency
|
52
|
+
pool.reap
|
53
|
+
rescue => e
|
54
|
+
log = logger
|
55
|
+
if retry_delay = @retry_error
|
56
|
+
log && log.warn("[reaper] reaping failed: #{e.inspect} restarting after #{retry_delay}s")
|
57
|
+
start retry_delay
|
58
|
+
else
|
59
|
+
log && log.warn("[reaper] reaping failed: #{e.inspect} stopping reaper")
|
60
|
+
@running = false
|
61
|
+
end
|
62
|
+
break
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_thread_name(name)
|
68
|
+
if ( thread = Thread.current ).respond_to?(:name)
|
69
|
+
thread.name = name; return
|
70
|
+
end
|
71
|
+
if defined? JRUBY_VERSION
|
72
|
+
thread = JRuby.reference(thread).getNativeThread
|
73
|
+
thread.setName("#{name} #{thread.getName}")
|
74
|
+
else
|
75
|
+
thread[:name] = name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def logger
|
80
|
+
@logger ||= ( pool.respond_to?(:logger) ? pool.logger : nil ) rescue nil
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
begin
|
2
|
+
require 'concurrent/executors'
|
3
|
+
require 'concurrent/timer_task'
|
4
|
+
rescue LoadError => e
|
5
|
+
warn "activerecord-bogacs' validator feature needs gem 'concurrent-ruby', please install or add it to your Gemfile"
|
6
|
+
raise e
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'active_record/connection_adapters/adapter_compat'
|
10
|
+
require 'active_record/bogacs/thread_safe'
|
11
|
+
|
12
|
+
module ActiveRecord
|
13
|
+
module Bogacs
|
14
|
+
|
15
|
+
# Every +frequency+ seconds, the _validator_ will perform connection validation
|
16
|
+
# on a pool it operates.
|
17
|
+
#
|
18
|
+
# @note Do not use a reaper with the validator as reaping (stale connection detection
|
19
|
+
# and removal) is part of the validation process and will only likely slow things down
|
20
|
+
# as all pool connection updates needs to be synchronized.
|
21
|
+
#
|
22
|
+
# Configure the frequency by setting `:validate_frequency` in your AR configuration.
|
23
|
+
#
|
24
|
+
# We recommend not setting values too low as that would drain the pool's performance
|
25
|
+
# under heavy concurrent connection retrieval. Connections are also validated upon
|
26
|
+
# checkout - the validator is intended to detect long idle pooled connections "ahead
|
27
|
+
# of time" instead of upon retrieval.
|
28
|
+
#
|
29
|
+
class Validator
|
30
|
+
|
31
|
+
attr_reader :pool, :frequency, :timeout
|
32
|
+
|
33
|
+
# Validator.new(self, spec.config[:validate_frequency]).run
|
34
|
+
# @private
|
35
|
+
def initialize(pool, frequency = 60, timeout = nil)
|
36
|
+
@pool = pool; PoolAdaptor.adapt! pool
|
37
|
+
if frequency # validate every 60s by default
|
38
|
+
frequency = frequency.to_f
|
39
|
+
@frequency = frequency > 0.0 ? frequency : false
|
40
|
+
else
|
41
|
+
@frequency = nil
|
42
|
+
end
|
43
|
+
if timeout
|
44
|
+
timeout = timeout.to_f
|
45
|
+
@timeout = timeout > 0.0 ? timeout : 0
|
46
|
+
else
|
47
|
+
@timeout = @frequency
|
48
|
+
end
|
49
|
+
@running = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def run
|
53
|
+
return unless frequency
|
54
|
+
@running = true; start
|
55
|
+
end
|
56
|
+
|
57
|
+
TimerTask = ::Concurrent::TimerTask
|
58
|
+
private_constant :TimerTask rescue nil
|
59
|
+
|
60
|
+
def start
|
61
|
+
TimerTask.new(:execution_interval => frequency, :timeout_interval => timeout) do
|
62
|
+
validate_connections
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def running?; @running end
|
67
|
+
|
68
|
+
def validate
|
69
|
+
start = Time.now
|
70
|
+
conns = connections
|
71
|
+
logger && logger.debug("[validator] found #{conns.size} candidates to validate")
|
72
|
+
invalid = 0
|
73
|
+
conns.each { |connection| invalid += 1 if validate_connection(connection) == false }
|
74
|
+
logger && logger.info("[validator] validated pool in #{Time.now - start}s (removed #{invalid} connections from pool)")
|
75
|
+
invalid
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def connections
|
81
|
+
connections = pool.connections.dup
|
82
|
+
connections.map! do |conn|
|
83
|
+
if conn
|
84
|
+
owner = conn.owner
|
85
|
+
if conn.in_use? # we'll do a pool.sync-ed check ...
|
86
|
+
if owner && ! owner.alive? # stale-conn (reaping)
|
87
|
+
pool.remove conn # remove is synchronized
|
88
|
+
conn.disconnect! rescue nil
|
89
|
+
nil
|
90
|
+
elsif ! owner # NOTE: this is likely a nasty bug
|
91
|
+
logger && logger.warn("[validator] found in-use connection ##{conn.object_id} without owner - removing from pool")
|
92
|
+
pool.remove_without_owner conn # synchronized
|
93
|
+
conn.disconnect! rescue nil
|
94
|
+
nil
|
95
|
+
else
|
96
|
+
nil # owner.alive? ... do not touch
|
97
|
+
end
|
98
|
+
else
|
99
|
+
conn # conn not in-use - candidate for validation
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
connections.compact
|
104
|
+
end
|
105
|
+
|
106
|
+
def validate_connection(conn)
|
107
|
+
return nil if conn.in_use?
|
108
|
+
pool.synchronize do # make sure it won't get checked-out while validating
|
109
|
+
return nil if conn.in_use?
|
110
|
+
# NOTE: active? is assumed to behave e.g. connection_alive_timeout used
|
111
|
+
# on AR-JDBC active? might return false as the JDBC connection is lazy
|
112
|
+
# ... but that is just fine!
|
113
|
+
begin
|
114
|
+
return true if conn.active? # validate the connection - ping the DB
|
115
|
+
rescue => e
|
116
|
+
logger && logger.info("[validator] connection ##{conn.object_id} failed to validate: #{e.inspect}")
|
117
|
+
end
|
118
|
+
|
119
|
+
# TODO support last_use - only validate if certain amount since use passed
|
120
|
+
|
121
|
+
logger && logger.debug("[validator] found non-active connection ##{conn.object_id} - removing from pool")
|
122
|
+
pool.remove_without_owner conn # not active - remove
|
123
|
+
conn.disconnect! rescue nil
|
124
|
+
return false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
#def synchronize(&block); pool.synchronize(&block) end
|
129
|
+
|
130
|
+
def logger
|
131
|
+
@logger ||= ( pool.respond_to?(:logger) ? pool.logger : nil ) rescue nil
|
132
|
+
end
|
133
|
+
|
134
|
+
module PoolAdaptor
|
135
|
+
|
136
|
+
def self.adapt!(pool)
|
137
|
+
unless pool.class.include?(PoolAdaptor)
|
138
|
+
pool.class.send :include, PoolAdaptor
|
139
|
+
end
|
140
|
+
|
141
|
+
return if pool.respond_to?(:thread_cached_conns)
|
142
|
+
|
143
|
+
if pool.instance_variable_get :@reserved_connections
|
144
|
+
class << pool
|
145
|
+
attr_reader :reserved_connections
|
146
|
+
alias_method :thread_cached_conns, :reserved_connections
|
147
|
+
end
|
148
|
+
elsif pool.instance_variable_get :@thread_cached_conns
|
149
|
+
class << pool
|
150
|
+
attr_reader :thread_cached_conns
|
151
|
+
end
|
152
|
+
else
|
153
|
+
raise NotImplementedError, "could not adapt pool: #{pool}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def cached_conn_owner_id(conn)
|
158
|
+
thread_cached_conns.keys.each do |owner_id|
|
159
|
+
if thread_cached_conns[ owner_id ] == conn
|
160
|
+
return owner_id
|
161
|
+
end
|
162
|
+
end
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def remove_without_owner(conn)
|
167
|
+
remove conn # release(conn, nil) owner.object_id should do fine
|
168
|
+
release_without_owner conn
|
169
|
+
end
|
170
|
+
|
171
|
+
def release_without_owner(conn)
|
172
|
+
if owner_id = cached_conn_owner_id(conn)
|
173
|
+
thread_cached_conns.delete owner_id; return true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
@@ -6,7 +6,43 @@ module ActiveRecord
|
|
6
6
|
|
7
7
|
attr_accessor :pool unless method_defined? :pool
|
8
8
|
|
9
|
-
|
9
|
+
if method_defined? :owner # >= 4.2
|
10
|
+
|
11
|
+
attr_reader :last_use
|
12
|
+
|
13
|
+
if ActiveRecord::VERSION::MAJOR > 4
|
14
|
+
|
15
|
+
# @private added @last_use
|
16
|
+
def lease
|
17
|
+
if in_use?
|
18
|
+
msg = 'Cannot lease connection, '
|
19
|
+
if @owner == Thread.current
|
20
|
+
msg += 'it is already leased by the current thread.'
|
21
|
+
else
|
22
|
+
msg += "it is already in use by a different thread: #{@owner}. Current thread: #{Thread.current}."
|
23
|
+
end
|
24
|
+
raise ActiveRecordError, msg
|
25
|
+
end
|
26
|
+
|
27
|
+
@owner = Thread.current; @last_use = Time.now
|
28
|
+
end
|
29
|
+
|
30
|
+
else
|
31
|
+
|
32
|
+
# @private removed synchronization + added @last_use
|
33
|
+
def lease
|
34
|
+
if in_use?
|
35
|
+
if @owner == Thread.current
|
36
|
+
# NOTE: could do a warning if 4.2.x cases do not end up here ...
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@owner = Thread.current; @last_use = Time.now
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
else
|
10
46
|
|
11
47
|
attr_reader :owner
|
12
48
|
|
@@ -29,6 +29,35 @@ module ActiveRecord
|
|
29
29
|
assert_equal 0, @pool.connections.size
|
30
30
|
end
|
31
31
|
|
32
|
+
def test_connection_removed_connection_on_checkout_failure
|
33
|
+
connection = nil
|
34
|
+
|
35
|
+
Thread.new {
|
36
|
+
pool.with_connection do |conn|
|
37
|
+
connection = conn
|
38
|
+
end
|
39
|
+
}.join
|
40
|
+
|
41
|
+
assert connection
|
42
|
+
assert_equal 1, pool.connections.size
|
43
|
+
|
44
|
+
bad_connection = connection
|
45
|
+
def bad_connection.verify!
|
46
|
+
raise ThreadError, 'corrupted-connection'
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
connection = pool.connection
|
51
|
+
rescue ThreadError
|
52
|
+
else; warn 'verify! error not raised'
|
53
|
+
end
|
54
|
+
assert_equal 0, pool.connections.size # gets removed from pool
|
55
|
+
|
56
|
+
connection = pool.connection
|
57
|
+
assert bad_connection != connection, 'bad connection returned on checkout'
|
58
|
+
|
59
|
+
end if ActiveRecord::VERSION::STRING > '4.2'
|
60
|
+
|
32
61
|
def self.startup; puts "running with ActiveRecord: #{ActiveRecord::VERSION::STRING}" end
|
33
62
|
|
34
63
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.expand_path('../../test_helper', File.dirname(__FILE__))
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module Bogacs
|
6
|
+
class ReaperTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def self.startup
|
9
|
+
super; require 'active_record/bogacs/reaper'
|
10
|
+
Reaper.const_set :Thread, AbortiveThread
|
11
|
+
end
|
12
|
+
|
13
|
+
def config; AR_CONFIG end
|
14
|
+
|
15
|
+
def setup
|
16
|
+
super
|
17
|
+
ActiveRecord::Base.establish_connection config.merge :reaping_frequency => 0.25
|
18
|
+
@pool = DefaultPool.new ActiveRecord::Base.connection_pool.spec
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown; @pool.disconnect! if (@pool ||= nil) end
|
22
|
+
|
23
|
+
def test_null_reaper
|
24
|
+
ActiveRecord::Base.establish_connection config.merge :reaping_frequency => false
|
25
|
+
pool = DefaultPool.new ActiveRecord::Base.connection_pool.spec
|
26
|
+
|
27
|
+
assert ! pool.reaper?
|
28
|
+
sleep 0.1
|
29
|
+
assert ! pool.reaping?
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_parse_frequency
|
33
|
+
ActiveRecord::Base.establish_connection config.merge :reaping_frequency => '0'
|
34
|
+
pool = DefaultPool.new ActiveRecord::Base.connection_pool.spec
|
35
|
+
|
36
|
+
assert ! pool.reaper?
|
37
|
+
sleep 0.1
|
38
|
+
assert ! pool.reaping?
|
39
|
+
|
40
|
+
assert ! Reaper.new(pool, '').frequency
|
41
|
+
assert_equal 5, Reaper.new(pool, '5').frequency
|
42
|
+
assert_equal 5.5, Reaper.new(pool, '5.5').frequency
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_reaper?
|
46
|
+
assert @pool.reaper?
|
47
|
+
sleep 0.1
|
48
|
+
assert @pool.reaping?
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_reap_error_restart
|
52
|
+
logger = Logger.new str = StringIO.new
|
53
|
+
@pool.reaper.instance_variable_set :@logger, logger
|
54
|
+
def @pool.reap; raise RuntimeError, 'test_reap_error' end
|
55
|
+
|
56
|
+
assert @pool.reaper?
|
57
|
+
sleep 0.3
|
58
|
+
assert_true @pool.reaping?
|
59
|
+
assert_match /WARN.*reaping failed:.* test_reap_error.* restarting after/, str.string
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_reap_error_stop
|
63
|
+
logger = Logger.new str = StringIO.new
|
64
|
+
@pool.reaper.instance_variable_set :@logger, logger
|
65
|
+
@pool.reaper.retry_error = false
|
66
|
+
def @pool.reap; raise 'test_reap_error2' end
|
67
|
+
|
68
|
+
assert @pool.reaper?
|
69
|
+
sleep 0.3
|
70
|
+
assert_false @pool.reaping?
|
71
|
+
assert_match /WARN.*reaping failed:.* test_reap_error2.* stopping/, str.string
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
class AbortiveThread < Thread
|
77
|
+
|
78
|
+
def self.new(*args, &block)
|
79
|
+
super.tap { |thread| thread.abort_on_exception = true }
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
require File.expand_path('../../test_helper', File.dirname(__FILE__))
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module Bogacs
|
6
|
+
class ValidatorTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def self.startup
|
9
|
+
ConnectionAdapters::ConnectionHandler.connection_pool_class = DefaultPool
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.shutdown
|
13
|
+
ConnectionAdapters::ConnectionHandler.connection_pool_class = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def config; AR_CONFIG end
|
17
|
+
|
18
|
+
def teardown; @pool.disconnect! if (@pool ||= nil) end
|
19
|
+
|
20
|
+
def test_null_validator
|
21
|
+
pool = new_pool :validate_frequency => nil
|
22
|
+
|
23
|
+
assert ! pool.validator?
|
24
|
+
sleep 0.05
|
25
|
+
assert ! pool.validating?
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_parse_frequency
|
29
|
+
pool = new_pool :validate_frequency => '0'
|
30
|
+
|
31
|
+
assert ! pool.validator?
|
32
|
+
sleep 0.05
|
33
|
+
assert ! pool.validating?
|
34
|
+
|
35
|
+
assert ! Validator.new(pool, '').frequency
|
36
|
+
assert_equal 50, Validator.new(pool, '50').frequency
|
37
|
+
assert_equal 5.5, Validator.new(pool, '5.5').frequency
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_validator?
|
41
|
+
assert pool.validator?
|
42
|
+
sleep 0.1
|
43
|
+
assert pool.validating?
|
44
|
+
end
|
45
|
+
|
46
|
+
require 'concurrent/atomic/atomic_fixnum.rb'
|
47
|
+
AtomicFixnum = ::Concurrent::AtomicFixnum
|
48
|
+
|
49
|
+
require 'concurrent/atomic/semaphore.rb'
|
50
|
+
Semaphore = ::Concurrent::Semaphore
|
51
|
+
|
52
|
+
def test_selects_non_used_connections
|
53
|
+
assert_equal [], validator.send(:connections)
|
54
|
+
|
55
|
+
count = AtomicFixnum.new
|
56
|
+
semaphore = Semaphore.new(2); semaphore.drain_permits
|
57
|
+
Thread.new {
|
58
|
+
pool.with_connection { |conn| assert conn; count.increment; semaphore.acquire }
|
59
|
+
}
|
60
|
+
Thread.new {
|
61
|
+
pool.with_connection { |conn| assert conn; count.increment; semaphore.acquire }
|
62
|
+
}
|
63
|
+
while count.value < 2; sleep 0.01 end
|
64
|
+
|
65
|
+
released_conn = nil
|
66
|
+
Thread.new {
|
67
|
+
pool.with_connection { |conn| assert released_conn = conn }
|
68
|
+
}.join
|
69
|
+
|
70
|
+
|
71
|
+
assert_equal 3, pool.connections.size
|
72
|
+
assert_equal 1, validator.send(:connections).size
|
73
|
+
assert_equal [ released_conn ], validator.send(:connections)
|
74
|
+
|
75
|
+
semaphore.release 2
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_auto_removes_stale_connection_from_pool_when_collecting_connections_to_validate
|
79
|
+
conn = connection
|
80
|
+
|
81
|
+
assert_equal [], validator.send(:connections)
|
82
|
+
|
83
|
+
count = AtomicFixnum.new
|
84
|
+
semaphore = Semaphore.new(2); semaphore.drain_permits
|
85
|
+
Thread.new {
|
86
|
+
pool.with_connection { |conn| assert conn; count.increment; semaphore.acquire }
|
87
|
+
}
|
88
|
+
stale_conn = nil
|
89
|
+
Thread.new {
|
90
|
+
assert stale_conn = pool.connection; count.increment; semaphore.acquire
|
91
|
+
}
|
92
|
+
while count.value < 2; sleep 0.01 end
|
93
|
+
|
94
|
+
returned_conn = nil
|
95
|
+
Thread.new {
|
96
|
+
pool.with_connection { |conn| assert returned_conn = conn }
|
97
|
+
}.join
|
98
|
+
|
99
|
+
assert_equal 4, pool.connections.size
|
100
|
+
validate_candidates = validator.send(:connections)
|
101
|
+
assert_equal [ returned_conn ], validate_candidates
|
102
|
+
assert_equal 4, pool.connections.size
|
103
|
+
|
104
|
+
semaphore.release(2); sleep 0.05
|
105
|
+
|
106
|
+
validate_candidates = validator.send(:connections)
|
107
|
+
assert_equal 2, validate_candidates.size
|
108
|
+
assert validate_candidates.include?(returned_conn)
|
109
|
+
assert ! validate_candidates.include?(stale_conn)
|
110
|
+
assert_equal 3, pool.connections.size
|
111
|
+
assert ! pool.connections.map(&:object_id).include?(stale_conn.object_id)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_validate_connection
|
115
|
+
conn = connection; pool.remove conn
|
116
|
+
conn.expire; assert ! conn.in_use?
|
117
|
+
# lazy on AR-JDBC :
|
118
|
+
conn.tables; assert conn.active?
|
119
|
+
|
120
|
+
def conn.active_called?; @_active_called ||= false end
|
121
|
+
def conn.active?; @_active_called = true; super end
|
122
|
+
|
123
|
+
result = validator.send :validate_connection, conn
|
124
|
+
assert_true result
|
125
|
+
|
126
|
+
assert conn.active_called?
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_validate_connection_non_valid
|
130
|
+
conn = connection; pool.remove conn
|
131
|
+
conn.expire; assert ! conn.in_use?
|
132
|
+
|
133
|
+
def conn.active?; false end
|
134
|
+
|
135
|
+
result = validator.send :validate_connection, conn
|
136
|
+
assert_false result
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_validate_connection_in_use
|
140
|
+
conn = connection
|
141
|
+
assert conn.in_use?
|
142
|
+
def conn.active?; raise 'active? should not be called for a used connection' end
|
143
|
+
|
144
|
+
result = validator.send :validate_connection, conn
|
145
|
+
assert_nil result
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_validate_connection_removes_invalid_connection_from_pool
|
149
|
+
conn = connection
|
150
|
+
puts pool.connections.map(&:object_id).inspect
|
151
|
+
Thread.new { pool.with_connection { |conn| assert conn } }.join
|
152
|
+
puts pool.connections.map(&:object_id).inspect
|
153
|
+
assert_equal 2, pool.connections.size
|
154
|
+
|
155
|
+
conn.expire; assert ! conn.in_use?
|
156
|
+
|
157
|
+
def conn.active?; false end
|
158
|
+
|
159
|
+
result = validator.send :validate_connection, conn
|
160
|
+
assert_false result
|
161
|
+
|
162
|
+
assert_equal 1, pool.connections.size
|
163
|
+
assert ! pool.connections.include?(conn)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_validate_connection_considers_raising_connection_invalid
|
167
|
+
conn = connection; threads = []
|
168
|
+
threads << Thread.new { pool.with_connection { |conn| assert conn; thread_work(conn) } }
|
169
|
+
threads << Thread.new { pool.with_connection { |conn| assert conn; thread_work(conn) } }
|
170
|
+
threads.each(&:join)
|
171
|
+
assert_equal 3, pool.connections.size
|
172
|
+
|
173
|
+
conn.expire; assert ! conn.in_use?
|
174
|
+
assert pool.connections.include?(conn)
|
175
|
+
|
176
|
+
def conn.active?; raise 'a failing active? check' end
|
177
|
+
def conn.disconnect!; raise 'failing disconnect!' end
|
178
|
+
|
179
|
+
result = validator.send :validate_connection, conn
|
180
|
+
assert_false result
|
181
|
+
|
182
|
+
assert_equal 2, pool.connections.size
|
183
|
+
assert ! pool.connections.include?(conn)
|
184
|
+
end
|
185
|
+
|
186
|
+
|
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.05) } }
|
190
|
+
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.05); conn.expire; invalid_conns << conn } }
|
191
|
+
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.05) } }
|
192
|
+
threads << Thread.new { pool.with_connection { |conn| thread_work(conn, 0.05); conn.expire; invalid_conns << conn } }
|
193
|
+
threads.each(&:join)
|
194
|
+
assert_equal 5, pool.connections.size
|
195
|
+
|
196
|
+
invalid_conns.each { |conn| def conn.active?; false end }
|
197
|
+
|
198
|
+
result = validator.validate
|
199
|
+
assert_equal 2, result
|
200
|
+
assert_equal 3, pool.connections.size
|
201
|
+
|
202
|
+
conn.expire
|
203
|
+
result = validator.validate
|
204
|
+
assert_equal 0, result
|
205
|
+
assert_equal 3, pool.connections.size
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def connection
|
211
|
+
pool; ActiveRecord::Base.connection
|
212
|
+
end
|
213
|
+
|
214
|
+
def validator; pool.validator end
|
215
|
+
|
216
|
+
def pool
|
217
|
+
# self.startup: connection_pool_class = DefaultPool
|
218
|
+
@pool ||= (establish_connection; Base.connection_pool)
|
219
|
+
end
|
220
|
+
|
221
|
+
DEFAULT_OPTS = { :size => 5, :validate_frequency => 1 }
|
222
|
+
|
223
|
+
def establish_connection(opts = DEFAULT_OPTS)
|
224
|
+
ActiveRecord::Base.establish_connection config.merge opts
|
225
|
+
end
|
226
|
+
|
227
|
+
def new_pool(opts = DEFAULT_OPTS)
|
228
|
+
establish_connection config.merge opts
|
229
|
+
DefaultPool.new Base.connection_pool.spec
|
230
|
+
end
|
231
|
+
|
232
|
+
def thread_work(conn, sleep = 0.1)
|
233
|
+
Thread.current.abort_on_exception = true
|
234
|
+
conn.tables; sleep(sleep) if sleep
|
235
|
+
end
|
236
|
+
|
237
|
+
class TimerTaskStub
|
238
|
+
|
239
|
+
# :execution_interval => frequency, :timeout_interval => timeout
|
240
|
+
def self.new(opts, &block)
|
241
|
+
raise 'noop'
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -35,9 +35,9 @@ ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
35
35
|
# ActiveRecord::ConnectionAdapters::ConnectionHandler.connection_pool_class = pool_class
|
36
36
|
#end
|
37
37
|
|
38
|
-
config = { :
|
39
|
-
config[:
|
40
|
-
config[:
|
38
|
+
config = { :adapter => ENV['AR_ADAPTER'] || 'sqlite3' }
|
39
|
+
config[:username] = ENV['AR_USERNAME'] if ENV['AR_USERNAME']
|
40
|
+
config[:password] = ENV['AR_PASSWORD'] if ENV['AR_PASSWORD']
|
41
41
|
if url = ENV['AR_URL'] || ENV['JDBC_URL']
|
42
42
|
config[:'url'] = url
|
43
43
|
else
|
@@ -51,7 +51,7 @@ config[:'connect_timeout'] = connect_timeout
|
|
51
51
|
prepared_statements = ENV['AR_PREPARED_STATEMENTS'] # || true
|
52
52
|
config[:'prepared_statements'] = prepared_statements if prepared_statements
|
53
53
|
#jdbc_properties = { 'logUnclosedConnections' => true, 'loginTimeout' => 5 }
|
54
|
-
#config['properties'] = jdbc_properties
|
54
|
+
#config[:'properties'] = jdbc_properties
|
55
55
|
|
56
56
|
checkout_timeout = ENV['AR_POOL_CHECKOUT_TIMEOUT'] || checkout_timeout
|
57
57
|
config[:'checkout_timeout'] = checkout_timeout.to_f if checkout_timeout
|
@@ -404,6 +404,9 @@ module ActiveRecord
|
|
404
404
|
if ar_jdbc_config[:password]
|
405
405
|
hikari_config.addDataSourceProperty 'password', ar_jdbc_config[:password]
|
406
406
|
end
|
407
|
+
( ar_jdbc_config[:properties] || {} ).each do |name, val|
|
408
|
+
hikari_config.addDataSourceProperty name.to_s, val.to_s
|
409
|
+
end
|
407
410
|
when /postgres/i
|
408
411
|
hikari_config.setDataSourceClassName 'org.postgresql.ds.PGSimpleDataSource'
|
409
412
|
hikari_config.addDataSourceProperty 'serverName', ar_jdbc_config[:host] || 'localhost'
|
@@ -415,12 +418,16 @@ module ActiveRecord
|
|
415
418
|
if ar_jdbc_config[:password]
|
416
419
|
hikari_config.addDataSourceProperty 'password', ar_jdbc_config[:password]
|
417
420
|
end
|
421
|
+
( ar_jdbc_config[:properties] || {} ).each do |name, val|
|
422
|
+
hikari_config.addDataSourceProperty name.to_s, val.to_s
|
423
|
+
end
|
418
424
|
else
|
419
425
|
hikari_config.setDriverClassName driver
|
420
426
|
hikari_config.setJdbcUrl ar_jdbc_config[:url]
|
421
427
|
hikari_config.setUsername ar_jdbc_config[:username] if ar_jdbc_config[:username]
|
422
428
|
hikari_config.setPassword ar_jdbc_config[:password] if ar_jdbc_config[:password]
|
423
429
|
end
|
430
|
+
hikari_config.setJdbcUrl ar_jdbc_config[:url] if ar_jdbc_config[:url]
|
424
431
|
|
425
432
|
# TODO: we shall handle raw properties ?!
|
426
433
|
#if ar_jdbc_config[:properties]
|
metadata
CHANGED
@@ -1,57 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-bogacs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karol Bucek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: concurrent-ruby
|
15
|
-
version_requirements: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0.9'
|
20
14
|
requirement: !ruby/object:Gem::Requirement
|
21
15
|
requirements:
|
22
|
-
- -
|
16
|
+
- - ">="
|
23
17
|
- !ruby/object:Gem::Version
|
24
18
|
version: '0.9'
|
19
|
+
name: concurrent-ruby
|
25
20
|
prerelease: false
|
26
21
|
type: :development
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
22
|
version_requirements: !ruby/object:Gem::Requirement
|
30
23
|
requirements:
|
31
|
-
- -
|
24
|
+
- - ">="
|
32
25
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
26
|
+
version: '0.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
34
28
|
requirement: !ruby/object:Gem::Requirement
|
35
29
|
requirements:
|
36
|
-
- - ~>
|
30
|
+
- - "~>"
|
37
31
|
- !ruby/object:Gem::Version
|
38
32
|
version: '10.3'
|
33
|
+
name: rake
|
39
34
|
prerelease: false
|
40
35
|
type: :development
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: test-unit
|
43
36
|
version_requirements: !ruby/object:Gem::Requirement
|
44
37
|
requirements:
|
45
|
-
- - ~>
|
38
|
+
- - "~>"
|
46
39
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
40
|
+
version: '10.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
48
42
|
requirement: !ruby/object:Gem::Requirement
|
49
43
|
requirements:
|
50
|
-
- - ~>
|
44
|
+
- - "~>"
|
51
45
|
- !ruby/object:Gem::Version
|
52
46
|
version: '2.5'
|
47
|
+
name: test-unit
|
53
48
|
prerelease: false
|
54
49
|
type: :development
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.5'
|
55
55
|
description: Improved ActiveRecord::ConnectionAdapters::ConnectionPool alternatives
|
56
56
|
email:
|
57
57
|
- self@kares.org
|
@@ -59,8 +59,8 @@ executables: []
|
|
59
59
|
extensions: []
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
|
-
- .gitignore
|
63
|
-
- .travis.yml
|
62
|
+
- ".gitignore"
|
63
|
+
- ".travis.yml"
|
64
64
|
- Gemfile
|
65
65
|
- LICENSE.txt
|
66
66
|
- README.md
|
@@ -70,17 +70,21 @@ files:
|
|
70
70
|
- lib/active_record/bogacs/default_pool.rb
|
71
71
|
- lib/active_record/bogacs/false_pool.rb
|
72
72
|
- lib/active_record/bogacs/pool_support.rb
|
73
|
+
- lib/active_record/bogacs/reaper.rb
|
73
74
|
- lib/active_record/bogacs/shareable_pool.rb
|
74
75
|
- lib/active_record/bogacs/thread_safe.rb
|
75
76
|
- lib/active_record/bogacs/thread_safe/synchronized.rb
|
77
|
+
- lib/active_record/bogacs/validator.rb
|
76
78
|
- lib/active_record/bogacs/version.rb
|
77
79
|
- lib/active_record/connection_adapters/adapter_compat.rb
|
78
80
|
- lib/active_record/shared_connection.rb
|
79
81
|
- test/active_record/bogacs/default_pool_test.rb
|
80
82
|
- test/active_record/bogacs/false_pool_test.rb
|
83
|
+
- test/active_record/bogacs/reaper_test.rb
|
81
84
|
- test/active_record/bogacs/shareable_pool/connection_pool_test.rb
|
82
85
|
- test/active_record/bogacs/shareable_pool/connection_sharing_test.rb
|
83
86
|
- test/active_record/bogacs/shareable_pool_helper.rb
|
87
|
+
- test/active_record/bogacs/validator_test.rb
|
84
88
|
- test/active_record/builtin_pool_test.rb
|
85
89
|
- test/active_record/connection_pool_test_methods.rb
|
86
90
|
- test/test_helper.rb
|
@@ -94,12 +98,12 @@ require_paths:
|
|
94
98
|
- lib
|
95
99
|
required_ruby_version: !ruby/object:Gem::Requirement
|
96
100
|
requirements:
|
97
|
-
- -
|
101
|
+
- - ">="
|
98
102
|
- !ruby/object:Gem::Version
|
99
103
|
version: '0'
|
100
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
105
|
requirements:
|
102
|
-
- -
|
106
|
+
- - ">="
|
103
107
|
- !ruby/object:Gem::Version
|
104
108
|
version: '0'
|
105
109
|
requirements: []
|
@@ -114,9 +118,11 @@ summary: 'Bogacs contains several pool implementations that can be used as a rep
|
|
114
118
|
test_files:
|
115
119
|
- test/active_record/bogacs/default_pool_test.rb
|
116
120
|
- test/active_record/bogacs/false_pool_test.rb
|
121
|
+
- test/active_record/bogacs/reaper_test.rb
|
117
122
|
- test/active_record/bogacs/shareable_pool/connection_pool_test.rb
|
118
123
|
- test/active_record/bogacs/shareable_pool/connection_sharing_test.rb
|
119
124
|
- test/active_record/bogacs/shareable_pool_helper.rb
|
125
|
+
- test/active_record/bogacs/validator_test.rb
|
120
126
|
- test/active_record/builtin_pool_test.rb
|
121
127
|
- test/active_record/connection_pool_test_methods.rb
|
122
128
|
- test/test_helper.rb
|