activerecord-bogacs 0.4.1 → 0.5.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/.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
|