connection_pool 2.1.2 → 2.2.3
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 +5 -5
- data/.travis.yml +7 -9
- data/Changes.md +35 -0
- data/Gemfile +2 -0
- data/README.md +27 -12
- data/Rakefile +4 -3
- data/connection_pool.gemspec +13 -14
- data/lib/connection_pool.rb +45 -72
- data/lib/connection_pool/timed_stack.rb +10 -35
- data/lib/connection_pool/version.rb +1 -1
- data/lib/connection_pool/wrapper.rb +43 -0
- data/test/helper.rb +4 -4
- data/test/test_connection_pool.rb +201 -112
- data/test/test_connection_pool_timed_stack.rb +28 -18
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e9fefa40cd9db0f5add54482a9f980d808b5bb578e83a5a8bf287c636c41704d
|
4
|
+
data.tar.gz: ec25d36c42cfb863e768bf49b18316d955397edaa8862f92d27687ab2a089bf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8653437078b6334be998d16f4851e12a21e8caca8338a07559c9bfbe646dc6023a7370aad6e33be905b05d6eaab8f249387eaa72b16a478f57c9c497adbbf30
|
7
|
+
data.tar.gz: 2a437d085a3f11376338ee32debe5e7ff38fc28fdc8d0d318287c2890bc90ce2d282468981dde43c8391798d113e1391d7998a5f5e571f1021ae99dfff6893e2
|
data/.travis.yml
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
---
|
2
|
-
sudo: false
|
3
2
|
cache: bundler
|
4
3
|
language: ruby
|
5
4
|
rvm:
|
6
|
-
-
|
7
|
-
- 2.
|
8
|
-
- 2.
|
9
|
-
- 2.
|
5
|
+
- 2.3
|
6
|
+
- 2.4
|
7
|
+
- 2.5
|
8
|
+
- 2.6
|
9
|
+
- 2.7
|
10
10
|
- jruby
|
11
|
-
|
12
|
-
|
13
|
-
email:
|
14
|
-
- drbrain@segment7.net
|
11
|
+
jdk:
|
12
|
+
- openjdk11
|
data/Changes.md
CHANGED
@@ -1,3 +1,38 @@
|
|
1
|
+
# connection_pool Changelog
|
2
|
+
|
3
|
+
2.2.3
|
4
|
+
------
|
5
|
+
|
6
|
+
- Pool now throws `ConnectionPool::TimeoutError` on timeout. [#130]
|
7
|
+
- Use monotonic clock present in all modern Rubies [Tero Tasanen, #109]
|
8
|
+
- Remove code hacks necessary for JRuby 1.7
|
9
|
+
- Expose wrapped pool from ConnectionPool::Wrapper [Thomas Lecavelier, #113]
|
10
|
+
|
11
|
+
2.2.2
|
12
|
+
------
|
13
|
+
|
14
|
+
- Add pool `size` and `available` accessors for metrics and monitoring
|
15
|
+
purposes [#97, robholland]
|
16
|
+
|
17
|
+
2.2.1
|
18
|
+
------
|
19
|
+
|
20
|
+
- Allow CP::Wrapper to use an existing pool [#87, etiennebarrie]
|
21
|
+
- Use monotonic time for more accurate timeouts [#84, jdantonio]
|
22
|
+
|
23
|
+
2.2.0
|
24
|
+
------
|
25
|
+
|
26
|
+
- Rollback `Timeout` handling introduced in 2.1.1 and 2.1.2. It seems
|
27
|
+
impossible to safely work around the issue. Please never, ever use
|
28
|
+
`Timeout.timeout` in your code or you will see rare but mysterious bugs. [#75]
|
29
|
+
|
30
|
+
2.1.3
|
31
|
+
------
|
32
|
+
|
33
|
+
- Don't increment created count until connection is successfully
|
34
|
+
created. [mylesmegyesi, #73]
|
35
|
+
|
1
36
|
2.1.2
|
2
37
|
------
|
3
38
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -45,11 +45,13 @@ sections when a resource is not available, or conversely if you are comfortable
|
|
45
45
|
blocking longer on a particular resource. This is not implemented in the below
|
46
46
|
`ConnectionPool::Wrapper` class.
|
47
47
|
|
48
|
+
## Migrating to a Connection Pool
|
49
|
+
|
48
50
|
You can use `ConnectionPool::Wrapper` to wrap a single global connection,
|
49
|
-
making it easier to
|
51
|
+
making it easier to migrate existing connection code over time:
|
50
52
|
|
51
53
|
``` ruby
|
52
|
-
$redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.
|
54
|
+
$redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.new }
|
53
55
|
$redis.sadd('foo', 1)
|
54
56
|
$redis.smembers('foo')
|
55
57
|
```
|
@@ -69,19 +71,36 @@ end
|
|
69
71
|
Once you've ported your entire system to use `with`, you can simply remove
|
70
72
|
`Wrapper` and use the simpler and faster `ConnectionPool`.
|
71
73
|
|
74
|
+
|
75
|
+
## Shutdown
|
76
|
+
|
72
77
|
You can shut down a ConnectionPool instance once it should no longer be used.
|
73
78
|
Further checkout attempts will immediately raise an error but existing checkouts
|
74
79
|
will work.
|
75
80
|
|
76
81
|
```ruby
|
77
82
|
cp = ConnectionPool.new { Redis.new }
|
78
|
-
cp.shutdown { |conn| conn.
|
83
|
+
cp.shutdown { |conn| conn.quit }
|
79
84
|
```
|
80
85
|
|
81
86
|
Shutting down a connection pool will block until all connections are checked in and closed.
|
82
|
-
Note that shutting down is completely optional
|
87
|
+
**Note that shutting down is completely optional**; Ruby's garbage collector will reclaim
|
83
88
|
unreferenced pools under normal circumstances.
|
84
89
|
|
90
|
+
## Current State
|
91
|
+
|
92
|
+
There are several methods that return information about a pool.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
cp = ConnectionPool.new(size: 10) { Redis.new }
|
96
|
+
cp.size # => 10
|
97
|
+
cp.available # => 10
|
98
|
+
|
99
|
+
cp.with do |conn|
|
100
|
+
cp.size # => 10
|
101
|
+
cp.available # => 9
|
102
|
+
end
|
103
|
+
```
|
85
104
|
|
86
105
|
Notes
|
87
106
|
-----
|
@@ -90,14 +109,10 @@ Notes
|
|
90
109
|
- There is no provision for repairing or checking the health of a connection;
|
91
110
|
connections should be self-repairing. This is true of the Dalli and Redis
|
92
111
|
clients.
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
```
|
99
|
-
$ gem install connection_pool
|
100
|
-
```
|
112
|
+
- **WARNING**: Don't ever use `Timeout.timeout` in your Ruby code or you will see
|
113
|
+
occasional silent corruption and mysterious errors. The Timeout API is unsafe
|
114
|
+
and cannot be used correctly, ever. Use proper socket timeout options as
|
115
|
+
exposed by Net::HTTP, Redis, Dalli, etc.
|
101
116
|
|
102
117
|
|
103
118
|
Author
|
data/Rakefile
CHANGED
data/connection_pool.gemspec
CHANGED
@@ -1,21 +1,20 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
require "./lib/connection_pool/version"
|
3
2
|
|
4
3
|
Gem::Specification.new do |s|
|
5
|
-
s.name
|
6
|
-
s.version
|
7
|
-
s.platform
|
8
|
-
s.authors
|
9
|
-
s.email
|
10
|
-
s.homepage
|
11
|
-
s.description = s.summary =
|
4
|
+
s.name = "connection_pool"
|
5
|
+
s.version = ConnectionPool::VERSION
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Mike Perham", "Damian Janowski"]
|
8
|
+
s.email = ["mperham@gmail.com", "damian@educabilia.com"]
|
9
|
+
s.homepage = "https://github.com/mperham/connection_pool"
|
10
|
+
s.description = s.summary = "Generic connection pool for Ruby"
|
12
11
|
|
13
|
-
s.files
|
14
|
-
s.test_files
|
15
|
-
s.executables
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
16
15
|
s.require_paths = ["lib"]
|
17
16
|
s.license = "MIT"
|
18
|
-
s.add_development_dependency
|
19
|
-
s.add_development_dependency
|
20
|
-
s.add_development_dependency
|
17
|
+
s.add_development_dependency "bundler"
|
18
|
+
s.add_development_dependency "minitest", ">= 5.0.0"
|
19
|
+
s.add_development_dependency "rake"
|
21
20
|
end
|
data/lib/connection_pool.rb
CHANGED
@@ -1,20 +1,25 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require "timeout"
|
2
|
+
require "connection_pool/version"
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
class ConnectionPool
|
5
|
+
class Error < ::RuntimeError; end
|
6
|
+
class PoolShuttingDownError < ::ConnectionPool::Error; end
|
7
|
+
class TimeoutError < ::Timeout::Error; end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Generic connection pool class for sharing a limited number of objects or network connections
|
11
|
+
# among many threads. Note: pool elements are lazily created.
|
6
12
|
#
|
7
13
|
# Example usage with block (faster):
|
8
14
|
#
|
9
15
|
# @pool = ConnectionPool.new { Redis.new }
|
10
|
-
#
|
11
16
|
# @pool.with do |redis|
|
12
17
|
# redis.lpop('my-list') if redis.llen('my-list') > 0
|
13
18
|
# end
|
14
19
|
#
|
15
20
|
# Using optional timeout override (for that single invocation)
|
16
21
|
#
|
17
|
-
# @pool.with(:
|
22
|
+
# @pool.with(timeout: 2.0) do |redis|
|
18
23
|
# redis.lpop('my-list') if redis.llen('my-list') > 0
|
19
24
|
# end
|
20
25
|
#
|
@@ -33,60 +38,57 @@ require_relative 'connection_pool/timed_stack'
|
|
33
38
|
class ConnectionPool
|
34
39
|
DEFAULTS = {size: 5, timeout: 5}
|
35
40
|
|
36
|
-
class Error < RuntimeError
|
37
|
-
end
|
38
|
-
|
39
41
|
def self.wrap(options, &block)
|
40
42
|
Wrapper.new(options, &block)
|
41
43
|
end
|
42
44
|
|
43
45
|
def initialize(options = {}, &block)
|
44
|
-
raise ArgumentError,
|
46
|
+
raise ArgumentError, "Connection pool requires a block" unless block
|
45
47
|
|
46
48
|
options = DEFAULTS.merge(options)
|
47
49
|
|
48
|
-
@size = options.fetch(:size)
|
50
|
+
@size = Integer(options.fetch(:size))
|
49
51
|
@timeout = options.fetch(:timeout)
|
50
52
|
|
51
53
|
@available = TimedStack.new(@size, &block)
|
52
|
-
@key = :"
|
54
|
+
@key = :"pool-#{@available.object_id}"
|
55
|
+
@key_count = :"pool-#{@available.object_id}-count"
|
53
56
|
end
|
54
57
|
|
55
58
|
def with(options = {})
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
success = true # means the connection wasn't interrupted
|
64
|
-
result
|
65
|
-
ensure
|
66
|
-
if success
|
67
|
-
# everything is roses, we can safely check the connection back in
|
59
|
+
Thread.handle_interrupt(Exception => :never) do
|
60
|
+
conn = checkout(options)
|
61
|
+
begin
|
62
|
+
Thread.handle_interrupt(Exception => :immediate) do
|
63
|
+
yield conn
|
64
|
+
end
|
65
|
+
ensure
|
68
66
|
checkin
|
69
|
-
else
|
70
|
-
@available.discard!(pop_connection)
|
71
67
|
end
|
72
68
|
end
|
73
69
|
end
|
74
70
|
|
75
71
|
def checkout(options = {})
|
76
|
-
|
77
|
-
|
78
|
-
@
|
72
|
+
if ::Thread.current[@key]
|
73
|
+
::Thread.current[@key_count] += 1
|
74
|
+
::Thread.current[@key]
|
79
75
|
else
|
80
|
-
|
76
|
+
::Thread.current[@key_count] = 1
|
77
|
+
::Thread.current[@key] = @available.pop(options[:timeout] || @timeout)
|
81
78
|
end
|
82
|
-
|
83
|
-
stack.push conn
|
84
|
-
conn
|
85
79
|
end
|
86
80
|
|
87
81
|
def checkin
|
88
|
-
|
89
|
-
|
82
|
+
if ::Thread.current[@key]
|
83
|
+
if ::Thread.current[@key_count] == 1
|
84
|
+
@available.push(::Thread.current[@key])
|
85
|
+
::Thread.current[@key] = nil
|
86
|
+
else
|
87
|
+
::Thread.current[@key_count] -= 1
|
88
|
+
end
|
89
|
+
else
|
90
|
+
raise ConnectionPool::Error, "no connections are checked out"
|
91
|
+
end
|
90
92
|
|
91
93
|
nil
|
92
94
|
end
|
@@ -95,43 +97,14 @@ class ConnectionPool
|
|
95
97
|
@available.shutdown(&block)
|
96
98
|
end
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
def pop_connection
|
101
|
-
if stack.empty?
|
102
|
-
raise ConnectionPool::Error, 'no connections are checked out'
|
103
|
-
else
|
104
|
-
stack.pop
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def stack
|
109
|
-
::Thread.current[@key] ||= []
|
110
|
-
end
|
111
|
-
|
112
|
-
class Wrapper < ::BasicObject
|
113
|
-
METHODS = [:with, :pool_shutdown]
|
114
|
-
|
115
|
-
def initialize(options = {}, &block)
|
116
|
-
@pool = ::ConnectionPool.new(options, &block)
|
117
|
-
end
|
118
|
-
|
119
|
-
def with(&block)
|
120
|
-
@pool.with(&block)
|
121
|
-
end
|
100
|
+
# Size of this connection pool
|
101
|
+
attr_reader :size
|
122
102
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
def respond_to?(id, *args)
|
128
|
-
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
|
129
|
-
end
|
130
|
-
|
131
|
-
def method_missing(name, *args, &block)
|
132
|
-
with do |connection|
|
133
|
-
connection.send(name, *args, &block)
|
134
|
-
end
|
135
|
-
end
|
103
|
+
# Number of pool entries available for checkout at this instant.
|
104
|
+
def available
|
105
|
+
@available.length
|
136
106
|
end
|
137
107
|
end
|
108
|
+
|
109
|
+
require "connection_pool/timed_stack"
|
110
|
+
require "connection_pool/wrapper"
|
@@ -1,12 +1,3 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'timeout'
|
3
|
-
|
4
|
-
##
|
5
|
-
# Raised when you attempt to retrieve a connection from a pool that has been
|
6
|
-
# shut down.
|
7
|
-
|
8
|
-
class ConnectionPool::PoolShuttingDownError < RuntimeError; end
|
9
|
-
|
10
1
|
##
|
11
2
|
# The TimedStack manages a pool of homogeneous connections (or any resource
|
12
3
|
# you wish to manage). Connections are created lazily up to a given maximum
|
@@ -27,6 +18,7 @@ class ConnectionPool::PoolShuttingDownError < RuntimeError; end
|
|
27
18
|
# #=> raises Timeout::Error after 5 seconds
|
28
19
|
|
29
20
|
class ConnectionPool::TimedStack
|
21
|
+
attr_reader :max
|
30
22
|
|
31
23
|
##
|
32
24
|
# Creates a new pool with +size+ connections that are created from the given
|
@@ -57,7 +49,7 @@ class ConnectionPool::TimedStack
|
|
57
49
|
@resource.broadcast
|
58
50
|
end
|
59
51
|
end
|
60
|
-
|
52
|
+
alias << push
|
61
53
|
|
62
54
|
##
|
63
55
|
# Retrieves a connection from the stack. If a connection is available it is
|
@@ -72,7 +64,7 @@ class ConnectionPool::TimedStack
|
|
72
64
|
options, timeout = timeout, 0.5 if Hash === timeout
|
73
65
|
timeout = options.fetch :timeout, timeout
|
74
66
|
|
75
|
-
deadline =
|
67
|
+
deadline = current_time + timeout
|
76
68
|
@mutex.synchronize do
|
77
69
|
loop do
|
78
70
|
raise ConnectionPool::PoolShuttingDownError if @shutdown_block
|
@@ -81,8 +73,8 @@ class ConnectionPool::TimedStack
|
|
81
73
|
connection = try_create(options)
|
82
74
|
return connection if connection
|
83
75
|
|
84
|
-
to_wait = deadline -
|
85
|
-
raise
|
76
|
+
to_wait = deadline - current_time
|
77
|
+
raise ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0
|
86
78
|
@resource.wait(@mutex, to_wait)
|
87
79
|
end
|
88
80
|
end
|
@@ -117,30 +109,12 @@ class ConnectionPool::TimedStack
|
|
117
109
|
@max - @created + @que.length
|
118
110
|
end
|
119
111
|
|
120
|
-
|
121
|
-
# Indicates that a connection isn't coming back, allowing a new one to be
|
122
|
-
# created to replace it.
|
123
|
-
|
124
|
-
def discard!(obj)
|
125
|
-
@mutex.synchronize do
|
126
|
-
if @shutdown_block
|
127
|
-
@shutdown_block.call(obj)
|
128
|
-
else
|
129
|
-
# try to shut down the connection before throwing it away
|
130
|
-
if obj.respond_to?(:close) # Dalli::Client
|
131
|
-
obj.close rescue nil
|
132
|
-
elsif obj.respond_to?(:disconnect!) # Redis
|
133
|
-
obj.disconnect! rescue nil
|
134
|
-
end
|
135
|
-
@created -= 1
|
136
|
-
end
|
112
|
+
private
|
137
113
|
|
138
|
-
|
139
|
-
|
114
|
+
def current_time
|
115
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
140
116
|
end
|
141
117
|
|
142
|
-
private
|
143
|
-
|
144
118
|
##
|
145
119
|
# This is an extension point for TimedStack and is called with a mutex.
|
146
120
|
#
|
@@ -188,8 +162,9 @@ class ConnectionPool::TimedStack
|
|
188
162
|
|
189
163
|
def try_create(options = nil)
|
190
164
|
unless @created == @max
|
165
|
+
object = @create_block.call
|
191
166
|
@created += 1
|
192
|
-
|
167
|
+
object
|
193
168
|
end
|
194
169
|
end
|
195
170
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class ConnectionPool
|
2
|
+
class Wrapper < ::BasicObject
|
3
|
+
METHODS = [:with, :pool_shutdown, :wrapped_pool]
|
4
|
+
|
5
|
+
def initialize(options = {}, &block)
|
6
|
+
@pool = options.fetch(:pool) { ::ConnectionPool.new(options, &block) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def wrapped_pool
|
10
|
+
@pool
|
11
|
+
end
|
12
|
+
|
13
|
+
def with(&block)
|
14
|
+
@pool.with(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def pool_shutdown(&block)
|
18
|
+
@pool.shutdown(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def pool_size
|
22
|
+
@pool.size
|
23
|
+
end
|
24
|
+
|
25
|
+
def pool_available
|
26
|
+
@pool.available
|
27
|
+
end
|
28
|
+
|
29
|
+
def respond_to?(id, *args)
|
30
|
+
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# rubocop:disable Style/MethodMissingSuper
|
34
|
+
# rubocop:disable Style/MissingRespondToMissing
|
35
|
+
def method_missing(name, *args, &block)
|
36
|
+
with do |connection|
|
37
|
+
connection.send(name, *args, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
# rubocop:enable Style/MethodMissingSuper
|
41
|
+
# rubocop:enable Style/MissingRespondToMissing
|
42
|
+
end
|
43
|
+
end
|
data/test/helper.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "helper"
|
2
2
|
|
3
3
|
class TestConnectionPool < Minitest::Test
|
4
|
-
|
5
4
|
class NetworkConnection
|
5
|
+
SLEEP_TIME = 0.1
|
6
|
+
|
6
7
|
def initialize
|
7
8
|
@x = 0
|
8
9
|
end
|
9
10
|
|
10
11
|
def do_something
|
11
12
|
@x += 1
|
12
|
-
sleep
|
13
|
+
sleep SLEEP_TIME
|
13
14
|
@x
|
14
15
|
end
|
15
16
|
|
@@ -19,7 +20,7 @@ class TestConnectionPool < Minitest::Test
|
|
19
20
|
|
20
21
|
def do_something_with_block
|
21
22
|
@x += yield
|
22
|
-
sleep
|
23
|
+
sleep SLEEP_TIME
|
23
24
|
@x
|
24
25
|
end
|
25
26
|
|
@@ -41,12 +42,12 @@ class TestConnectionPool < Minitest::Test
|
|
41
42
|
end
|
42
43
|
|
43
44
|
def use_pool(pool, size)
|
44
|
-
Array.new(size)
|
45
|
+
Array.new(size) {
|
45
46
|
Thread.new do
|
46
|
-
pool.with
|
47
|
+
pool.with { sleep }
|
47
48
|
end
|
48
|
-
|
49
|
-
Thread.pass until thread.status ==
|
49
|
+
}.each do |thread|
|
50
|
+
Thread.pass until thread.status == "sleep"
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -58,33 +59,38 @@ class TestConnectionPool < Minitest::Test
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def test_basic_multithreaded_usage
|
61
|
-
|
62
|
+
pool_size = 5
|
63
|
+
pool = ConnectionPool.new(size: pool_size) { NetworkConnection.new }
|
64
|
+
|
65
|
+
start = Time.new
|
62
66
|
|
63
|
-
|
67
|
+
generations = 3
|
68
|
+
|
69
|
+
result = Array.new(pool_size * generations) {
|
64
70
|
Thread.new do
|
65
71
|
pool.with do |net|
|
66
72
|
net.do_something
|
67
73
|
end
|
68
74
|
end
|
69
|
-
|
75
|
+
}.map(&:value)
|
70
76
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
77
|
+
finish = Time.new
|
78
|
+
|
79
|
+
assert_equal((1..generations).cycle(pool_size).sort, result.sort)
|
80
|
+
|
81
|
+
assert_operator(finish - start, :>, generations * NetworkConnection::SLEEP_TIME)
|
76
82
|
end
|
77
83
|
|
78
84
|
def test_timeout
|
79
|
-
pool = ConnectionPool.new(:
|
80
|
-
thread = Thread.new
|
85
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
86
|
+
thread = Thread.new {
|
81
87
|
pool.with do |net|
|
82
88
|
net.do_something
|
83
89
|
sleep 0.01
|
84
90
|
end
|
85
|
-
|
91
|
+
}
|
86
92
|
|
87
|
-
Thread.pass while thread.status ==
|
93
|
+
Thread.pass while thread.status == "run"
|
88
94
|
|
89
95
|
assert_raises Timeout::Error do
|
90
96
|
pool.with { |net| net.do_something }
|
@@ -98,79 +104,126 @@ class TestConnectionPool < Minitest::Test
|
|
98
104
|
end
|
99
105
|
|
100
106
|
def test_with
|
101
|
-
pool = ConnectionPool.new(:
|
107
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
102
108
|
|
103
109
|
pool.with do
|
104
|
-
|
105
|
-
|
106
|
-
|
110
|
+
Thread.new {
|
111
|
+
assert_raises Timeout::Error do
|
112
|
+
pool.checkout
|
113
|
+
end
|
114
|
+
}.join
|
107
115
|
end
|
108
116
|
|
109
117
|
assert Thread.new { pool.checkout }.join
|
110
118
|
end
|
111
119
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
120
|
+
def test_with_timeout
|
121
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
122
|
+
|
123
|
+
assert_raises Timeout::Error do
|
124
|
+
Timeout.timeout(0.01) do
|
125
|
+
pool.with do |obj|
|
126
|
+
assert_equal 0, pool.available
|
127
|
+
sleep 0.015
|
128
|
+
end
|
119
129
|
end
|
120
130
|
end
|
131
|
+
assert_equal 1, pool.available
|
132
|
+
end
|
121
133
|
|
122
|
-
|
123
|
-
|
134
|
+
def test_invalid_size
|
135
|
+
assert_raises ArgumentError, TypeError do
|
136
|
+
ConnectionPool.new(timeout: 0, size: nil) { Object.new }
|
137
|
+
end
|
138
|
+
assert_raises ArgumentError, TypeError do
|
139
|
+
ConnectionPool.new(timeout: 0, size: "") { Object.new }
|
140
|
+
end
|
141
|
+
end
|
124
142
|
|
125
|
-
|
126
|
-
|
143
|
+
def test_handle_interrupt_ensures_checkin
|
144
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
145
|
+
def pool.checkout(options)
|
146
|
+
sleep 0.015
|
147
|
+
super
|
148
|
+
end
|
127
149
|
|
128
|
-
|
150
|
+
did_something = false
|
129
151
|
|
130
|
-
|
131
|
-
Timeout.timeout(
|
132
|
-
pool.with do
|
133
|
-
|
134
|
-
|
135
|
-
|
152
|
+
action = lambda do
|
153
|
+
Timeout.timeout(0.01) do
|
154
|
+
pool.with do |obj|
|
155
|
+
did_something = true
|
156
|
+
# Timeout::Error will be triggered by any non-trivial Ruby code
|
157
|
+
# executed here since it couldn't be raised during checkout.
|
158
|
+
# It looks like setting the local variable above does not trigger
|
159
|
+
# the Timeout check in MRI 2.2.1.
|
160
|
+
obj.tap { obj.hash }
|
136
161
|
end
|
137
162
|
end
|
138
163
|
end
|
139
164
|
|
140
|
-
|
165
|
+
if RUBY_ENGINE == "ruby"
|
166
|
+
# These asserts rely on the Ruby implementation reaching `did_something =
|
167
|
+
# true` before the interrupt is detected by the thread. Interrupt
|
168
|
+
# detection timing is implementation-specific in practice, with JRuby,
|
169
|
+
# Rubinius, and TruffleRuby all having different interrupt timings to MRI.
|
170
|
+
# In fact they generally detect interrupts more quickly than MRI, so they
|
171
|
+
# may not reach `did_something = true` before detecting the interrupt.
|
172
|
+
|
173
|
+
assert_raises Timeout::Error, &action
|
174
|
+
|
175
|
+
assert did_something
|
176
|
+
else
|
177
|
+
action.call
|
178
|
+
end
|
179
|
+
|
180
|
+
assert_equal 1, pool.available
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_explicit_return
|
184
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) {
|
185
|
+
mock = Minitest::Mock.new
|
186
|
+
def mock.disconnect!
|
187
|
+
raise "should not disconnect upon explicit return"
|
188
|
+
end
|
189
|
+
mock
|
190
|
+
}
|
141
191
|
|
142
|
-
|
143
|
-
|
192
|
+
pool.with do |conn|
|
193
|
+
return true
|
194
|
+
end
|
144
195
|
end
|
145
196
|
|
146
197
|
def test_with_timeout_override
|
147
|
-
pool = ConnectionPool.new(:
|
198
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
148
199
|
|
149
|
-
t = Thread.new
|
200
|
+
t = Thread.new {
|
150
201
|
pool.with do |net|
|
151
202
|
net.do_something
|
152
203
|
sleep 0.01
|
153
204
|
end
|
154
|
-
|
205
|
+
}
|
155
206
|
|
156
|
-
Thread.pass while t.status ==
|
207
|
+
Thread.pass while t.status == "run"
|
157
208
|
|
158
209
|
assert_raises Timeout::Error do
|
159
210
|
pool.with { |net| net.do_something }
|
160
211
|
end
|
161
212
|
|
162
|
-
pool.with(:
|
213
|
+
pool.with(timeout: 2 * NetworkConnection::SLEEP_TIME) do |conn|
|
163
214
|
refute_nil conn
|
164
215
|
end
|
165
216
|
end
|
166
217
|
|
167
218
|
def test_checkin
|
168
|
-
pool = ConnectionPool.new(:
|
219
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
169
220
|
conn = pool.checkout
|
170
221
|
|
171
|
-
|
172
|
-
|
173
|
-
|
222
|
+
Thread.new {
|
223
|
+
assert_raises Timeout::Error do
|
224
|
+
pool.checkout
|
225
|
+
end
|
226
|
+
}.join
|
174
227
|
|
175
228
|
pool.checkin
|
176
229
|
|
@@ -178,22 +231,19 @@ class TestConnectionPool < Minitest::Test
|
|
178
231
|
end
|
179
232
|
|
180
233
|
def test_returns_value
|
181
|
-
pool = ConnectionPool.new(:
|
182
|
-
assert_equal 1, pool.with {|o| 1 }
|
234
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
235
|
+
assert_equal 1, pool.with { |o| 1 }
|
183
236
|
end
|
184
237
|
|
185
238
|
def test_checkin_never_checkout
|
186
|
-
pool = ConnectionPool.new(:
|
187
|
-
|
188
|
-
e = assert_raises ConnectionPool::Error do
|
189
|
-
pool.checkin
|
190
|
-
end
|
239
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
191
240
|
|
192
|
-
|
241
|
+
e = assert_raises(ConnectionPool::Error) { pool.checkin }
|
242
|
+
assert_equal "no connections are checked out", e.message
|
193
243
|
end
|
194
244
|
|
195
245
|
def test_checkin_no_current_checkout
|
196
|
-
pool = ConnectionPool.new(:
|
246
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
197
247
|
|
198
248
|
pool.checkout
|
199
249
|
pool.checkin
|
@@ -204,18 +254,18 @@ class TestConnectionPool < Minitest::Test
|
|
204
254
|
end
|
205
255
|
|
206
256
|
def test_checkin_twice
|
207
|
-
pool = ConnectionPool.new(:
|
257
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
208
258
|
|
209
259
|
pool.checkout
|
210
260
|
pool.checkout
|
211
261
|
|
212
262
|
pool.checkin
|
213
263
|
|
214
|
-
|
215
|
-
|
264
|
+
Thread.new {
|
265
|
+
assert_raises Timeout::Error do
|
216
266
|
pool.checkout
|
217
|
-
end
|
218
|
-
|
267
|
+
end
|
268
|
+
}.join
|
219
269
|
|
220
270
|
pool.checkin
|
221
271
|
|
@@ -223,7 +273,7 @@ class TestConnectionPool < Minitest::Test
|
|
223
273
|
end
|
224
274
|
|
225
275
|
def test_checkout
|
226
|
-
pool = ConnectionPool.new(:
|
276
|
+
pool = ConnectionPool.new(size: 1) { NetworkConnection.new }
|
227
277
|
|
228
278
|
conn = pool.checkout
|
229
279
|
|
@@ -233,18 +283,18 @@ class TestConnectionPool < Minitest::Test
|
|
233
283
|
end
|
234
284
|
|
235
285
|
def test_checkout_multithread
|
236
|
-
pool = ConnectionPool.new(:
|
286
|
+
pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
|
237
287
|
conn = pool.checkout
|
238
288
|
|
239
|
-
t = Thread.new
|
289
|
+
t = Thread.new {
|
240
290
|
pool.checkout
|
241
|
-
|
291
|
+
}
|
242
292
|
|
243
293
|
refute_same conn, t.value
|
244
294
|
end
|
245
295
|
|
246
296
|
def test_checkout_timeout
|
247
|
-
pool = ConnectionPool.new(:
|
297
|
+
pool = ConnectionPool.new(timeout: 0, size: 0) { Object.new }
|
248
298
|
|
249
299
|
assert_raises Timeout::Error do
|
250
300
|
pool.checkout
|
@@ -252,26 +302,26 @@ class TestConnectionPool < Minitest::Test
|
|
252
302
|
end
|
253
303
|
|
254
304
|
def test_checkout_timeout_override
|
255
|
-
pool = ConnectionPool.new(:
|
305
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
256
306
|
|
257
|
-
thread = Thread.new
|
307
|
+
thread = Thread.new {
|
258
308
|
pool.with do |net|
|
259
309
|
net.do_something
|
260
310
|
sleep 0.01
|
261
311
|
end
|
262
|
-
|
312
|
+
}
|
263
313
|
|
264
|
-
Thread.pass while thread.status ==
|
314
|
+
Thread.pass while thread.status == "run"
|
265
315
|
|
266
316
|
assert_raises Timeout::Error do
|
267
317
|
pool.checkout
|
268
318
|
end
|
269
319
|
|
270
|
-
assert pool.checkout :
|
320
|
+
assert pool.checkout timeout: 2 * NetworkConnection::SLEEP_TIME
|
271
321
|
end
|
272
322
|
|
273
323
|
def test_passthru
|
274
|
-
pool = ConnectionPool.wrap(:
|
324
|
+
pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
|
275
325
|
assert_equal 1, pool.do_something
|
276
326
|
assert_equal 2, pool.do_something
|
277
327
|
assert_equal 5, pool.do_something_with_block { 3 }
|
@@ -279,7 +329,7 @@ class TestConnectionPool < Minitest::Test
|
|
279
329
|
end
|
280
330
|
|
281
331
|
def test_passthru_respond_to
|
282
|
-
pool = ConnectionPool.wrap(:
|
332
|
+
pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
|
283
333
|
assert pool.respond_to?(:with)
|
284
334
|
assert pool.respond_to?(:do_something)
|
285
335
|
assert pool.respond_to?(:do_magic)
|
@@ -287,67 +337,67 @@ class TestConnectionPool < Minitest::Test
|
|
287
337
|
end
|
288
338
|
|
289
339
|
def test_return_value
|
290
|
-
pool = ConnectionPool.new(:
|
291
|
-
result = pool.with
|
340
|
+
pool = ConnectionPool.new(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
|
341
|
+
result = pool.with { |net|
|
292
342
|
net.fast
|
293
|
-
|
343
|
+
}
|
294
344
|
assert_equal 1, result
|
295
345
|
end
|
296
346
|
|
297
347
|
def test_heavy_threading
|
298
|
-
pool = ConnectionPool.new(:
|
348
|
+
pool = ConnectionPool.new(timeout: 0.5, size: 3) { NetworkConnection.new }
|
299
349
|
|
300
|
-
threads = Array.new(20)
|
350
|
+
threads = Array.new(20) {
|
301
351
|
Thread.new do
|
302
352
|
pool.with do |net|
|
303
353
|
sleep 0.01
|
304
354
|
end
|
305
355
|
end
|
306
|
-
|
356
|
+
}
|
307
357
|
|
308
358
|
threads.map { |thread| thread.join }
|
309
359
|
end
|
310
360
|
|
311
361
|
def test_reuses_objects_when_pool_not_saturated
|
312
|
-
pool = ConnectionPool.new(:
|
362
|
+
pool = ConnectionPool.new(size: 5) { NetworkConnection.new }
|
313
363
|
|
314
|
-
ids = 10.times.map
|
364
|
+
ids = 10.times.map {
|
315
365
|
pool.with { |c| c.object_id }
|
316
|
-
|
366
|
+
}
|
317
367
|
|
318
368
|
assert_equal 1, ids.uniq.size
|
319
369
|
end
|
320
370
|
|
321
371
|
def test_nested_checkout
|
322
372
|
recorder = Recorder.new
|
323
|
-
pool = ConnectionPool.new(:
|
373
|
+
pool = ConnectionPool.new(size: 1) { recorder }
|
324
374
|
pool.with do |r_outer|
|
325
|
-
@other = Thread.new
|
375
|
+
@other = Thread.new { |t|
|
326
376
|
pool.with do |r_other|
|
327
|
-
r_other.do_work(
|
377
|
+
r_other.do_work("other")
|
328
378
|
end
|
329
|
-
|
379
|
+
}
|
330
380
|
|
331
381
|
pool.with do |r_inner|
|
332
|
-
r_inner.do_work(
|
382
|
+
r_inner.do_work("inner")
|
333
383
|
end
|
334
384
|
|
335
385
|
Thread.pass
|
336
386
|
|
337
|
-
r_outer.do_work(
|
387
|
+
r_outer.do_work("outer")
|
338
388
|
end
|
339
389
|
|
340
390
|
@other.join
|
341
391
|
|
342
|
-
assert_equal [
|
392
|
+
assert_equal ["inner", "outer", "other"], recorder.calls
|
343
393
|
end
|
344
394
|
|
345
395
|
def test_shutdown_is_executed_for_all_connections
|
346
396
|
recorders = []
|
347
397
|
|
348
|
-
pool = ConnectionPool.new(:
|
398
|
+
pool = ConnectionPool.new(size: 3) {
|
349
399
|
Recorder.new.tap { |r| recorders << r }
|
350
|
-
|
400
|
+
}
|
351
401
|
|
352
402
|
threads = use_pool pool, 3
|
353
403
|
|
@@ -361,9 +411,9 @@ class TestConnectionPool < Minitest::Test
|
|
361
411
|
end
|
362
412
|
|
363
413
|
def test_raises_error_after_shutting_down
|
364
|
-
pool = ConnectionPool.new(:
|
414
|
+
pool = ConnectionPool.new(size: 1) { true }
|
365
415
|
|
366
|
-
pool.shutdown {
|
416
|
+
pool.shutdown {}
|
367
417
|
|
368
418
|
assert_raises ConnectionPool::PoolShuttingDownError do
|
369
419
|
pool.checkout
|
@@ -373,9 +423,9 @@ class TestConnectionPool < Minitest::Test
|
|
373
423
|
def test_runs_shutdown_block_asynchronously_if_connection_was_in_use
|
374
424
|
recorders = []
|
375
425
|
|
376
|
-
pool = ConnectionPool.new(:
|
426
|
+
pool = ConnectionPool.new(size: 3) {
|
377
427
|
Recorder.new.tap { |r| recorders << r }
|
378
|
-
|
428
|
+
}
|
379
429
|
|
380
430
|
threads = use_pool pool, 2
|
381
431
|
|
@@ -395,7 +445,7 @@ class TestConnectionPool < Minitest::Test
|
|
395
445
|
end
|
396
446
|
|
397
447
|
def test_raises_an_error_if_shutdown_is_called_without_a_block
|
398
|
-
pool = ConnectionPool.new(:
|
448
|
+
pool = ConnectionPool.new(size: 1) {}
|
399
449
|
|
400
450
|
assert_raises ArgumentError do
|
401
451
|
pool.shutdown
|
@@ -405,9 +455,9 @@ class TestConnectionPool < Minitest::Test
|
|
405
455
|
def test_shutdown_is_executed_for_all_connections_in_wrapped_pool
|
406
456
|
recorders = []
|
407
457
|
|
408
|
-
wrapper = ConnectionPool::Wrapper.new(:
|
458
|
+
wrapper = ConnectionPool::Wrapper.new(size: 3) {
|
409
459
|
Recorder.new.tap { |r| recorders << r }
|
410
|
-
|
460
|
+
}
|
411
461
|
|
412
462
|
threads = use_pool wrapper, 3
|
413
463
|
|
@@ -420,6 +470,11 @@ class TestConnectionPool < Minitest::Test
|
|
420
470
|
assert_equal [["shutdown"]] * 3, recorders.map { |r| r.calls }
|
421
471
|
end
|
422
472
|
|
473
|
+
def test_wrapper_wrapped_pool
|
474
|
+
wrapper = ConnectionPool::Wrapper.new { NetworkConnection.new }
|
475
|
+
assert_equal ConnectionPool, wrapper.wrapped_pool.class
|
476
|
+
end
|
477
|
+
|
423
478
|
def test_wrapper_method_missing
|
424
479
|
wrapper = ConnectionPool::Wrapper.new { NetworkConnection.new }
|
425
480
|
|
@@ -436,17 +491,17 @@ class TestConnectionPool < Minitest::Test
|
|
436
491
|
end
|
437
492
|
|
438
493
|
def test_wrapper_with
|
439
|
-
wrapper = ConnectionPool::Wrapper.new(:
|
494
|
+
wrapper = ConnectionPool::Wrapper.new(timeout: 0, size: 1) { Object.new }
|
440
495
|
|
441
496
|
wrapper.with do
|
442
|
-
|
443
|
-
|
444
|
-
wrapper.with { flunk
|
445
|
-
end
|
446
|
-
|
497
|
+
Thread.new {
|
498
|
+
assert_raises Timeout::Error do
|
499
|
+
wrapper.with { flunk "connection checked out :(" }
|
500
|
+
end
|
501
|
+
}.join
|
447
502
|
end
|
448
503
|
|
449
|
-
assert Thread.new { wrapper.with {
|
504
|
+
assert Thread.new { wrapper.with {} }.join
|
450
505
|
end
|
451
506
|
|
452
507
|
class ConnWithEval
|
@@ -461,4 +516,38 @@ class TestConnectionPool < Minitest::Test
|
|
461
516
|
assert_equal "eval'ed 1", wrapper.eval(1)
|
462
517
|
end
|
463
518
|
|
519
|
+
def test_wrapper_with_connection_pool
|
520
|
+
recorder = Recorder.new
|
521
|
+
pool = ConnectionPool.new(size: 1) { recorder }
|
522
|
+
wrapper = ConnectionPool::Wrapper.new(pool: pool)
|
523
|
+
|
524
|
+
pool.with { |r| r.do_work("with") }
|
525
|
+
wrapper.do_work("wrapped")
|
526
|
+
|
527
|
+
assert_equal ["with", "wrapped"], recorder.calls
|
528
|
+
end
|
529
|
+
|
530
|
+
def test_stats_without_active_connection
|
531
|
+
pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
|
532
|
+
|
533
|
+
assert_equal(2, pool.size)
|
534
|
+
assert_equal(2, pool.available)
|
535
|
+
end
|
536
|
+
|
537
|
+
def test_stats_with_active_connection
|
538
|
+
pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
|
539
|
+
|
540
|
+
pool.with do
|
541
|
+
assert_equal(1, pool.available)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
def test_stats_with_string_size
|
546
|
+
pool = ConnectionPool.new(size: "2") { NetworkConnection.new }
|
547
|
+
|
548
|
+
pool.with do
|
549
|
+
assert_equal(2, pool.size)
|
550
|
+
assert_equal(1, pool.available)
|
551
|
+
end
|
552
|
+
end
|
464
553
|
end
|
@@ -1,7 +1,6 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "helper"
|
2
2
|
|
3
3
|
class TestConnectionPoolTimedStack < Minitest::Test
|
4
|
-
|
5
4
|
def setup
|
6
5
|
@stack = ConnectionPool::TimedStack.new { Object.new }
|
7
6
|
end
|
@@ -34,6 +33,25 @@ class TestConnectionPoolTimedStack < Minitest::Test
|
|
34
33
|
assert_equal 1, stack.length
|
35
34
|
end
|
36
35
|
|
36
|
+
def test_object_creation_fails
|
37
|
+
stack = ConnectionPool::TimedStack.new(2) { raise "failure" }
|
38
|
+
|
39
|
+
begin
|
40
|
+
stack.pop
|
41
|
+
rescue => error
|
42
|
+
assert_equal "failure", error.message
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
stack.pop
|
47
|
+
rescue => error
|
48
|
+
assert_equal "failure", error.message
|
49
|
+
end
|
50
|
+
|
51
|
+
refute_empty stack
|
52
|
+
assert_equal 2, stack.length
|
53
|
+
end
|
54
|
+
|
37
55
|
def test_pop
|
38
56
|
object = Object.new
|
39
57
|
@stack.push object
|
@@ -44,19 +62,13 @@ class TestConnectionPoolTimedStack < Minitest::Test
|
|
44
62
|
end
|
45
63
|
|
46
64
|
def test_pop_empty
|
47
|
-
e = assert_raises
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
assert_equal 'Waited 0 sec', e.message
|
65
|
+
e = assert_raises(ConnectionPool::TimeoutError) { @stack.pop timeout: 0 }
|
66
|
+
assert_equal "Waited 0 sec", e.message
|
52
67
|
end
|
53
68
|
|
54
69
|
def test_pop_empty_2_0_compatibility
|
55
|
-
e = assert_raises
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
assert_equal 'Waited 0 sec', e.message
|
70
|
+
e = assert_raises(Timeout::Error) { @stack.pop 0 }
|
71
|
+
assert_equal "Waited 0 sec", e.message
|
60
72
|
end
|
61
73
|
|
62
74
|
def test_pop_full
|
@@ -69,11 +81,11 @@ class TestConnectionPoolTimedStack < Minitest::Test
|
|
69
81
|
end
|
70
82
|
|
71
83
|
def test_pop_wait
|
72
|
-
thread = Thread.start
|
84
|
+
thread = Thread.start {
|
73
85
|
@stack.pop
|
74
|
-
|
86
|
+
}
|
75
87
|
|
76
|
-
Thread.pass while thread.status ==
|
88
|
+
Thread.pass while thread.status == "run"
|
77
89
|
|
78
90
|
object = Object.new
|
79
91
|
|
@@ -83,7 +95,7 @@ class TestConnectionPoolTimedStack < Minitest::Test
|
|
83
95
|
end
|
84
96
|
|
85
97
|
def test_pop_shutdown
|
86
|
-
@stack.shutdown {
|
98
|
+
@stack.shutdown {}
|
87
99
|
|
88
100
|
assert_raises ConnectionPool::PoolShuttingDownError do
|
89
101
|
@stack.pop
|
@@ -125,6 +137,4 @@ class TestConnectionPoolTimedStack < Minitest::Test
|
|
125
137
|
refute_empty called
|
126
138
|
assert_empty @stack
|
127
139
|
end
|
128
|
-
|
129
140
|
end
|
130
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: connection_pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-06-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- lib/connection_pool.rb
|
73
73
|
- lib/connection_pool/timed_stack.rb
|
74
74
|
- lib/connection_pool/version.rb
|
75
|
+
- lib/connection_pool/wrapper.rb
|
75
76
|
- test/helper.rb
|
76
77
|
- test/test_connection_pool.rb
|
77
78
|
- test/test_connection_pool_timed_stack.rb
|
@@ -94,8 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
95
|
- !ruby/object:Gem::Version
|
95
96
|
version: '0'
|
96
97
|
requirements: []
|
97
|
-
|
98
|
-
rubygems_version: 2.4.5
|
98
|
+
rubygems_version: 3.1.2
|
99
99
|
signing_key:
|
100
100
|
specification_version: 4
|
101
101
|
summary: Generic connection pool for Ruby
|