connection_pool 2.1.3 → 2.2.2
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 +4 -8
- data/Changes.md +22 -0
- data/README.md +12 -11
- data/lib/connection_pool.rb +58 -34
- data/lib/connection_pool/monotonic_time.rb +66 -0
- data/lib/connection_pool/timed_stack.rb +4 -24
- data/lib/connection_pool/version.rb +1 -1
- data/test/test_connection_pool.rb +109 -57
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf8177bf07f6c8e68b13f28416f6ccc0c64803af
|
4
|
+
data.tar.gz: ff9c836ce5576d4d0cb1edf8d858a761f982e437
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73bda4d8d0b3cc9daef5c93bb98b7649d811bd5c8f467e25c3aaf7d3353541225e7b30e1efd481a14e45f5c56b5fc846d49dabdd9a167702a71c4a9631a9b6a2
|
7
|
+
data.tar.gz: a331275f67b3635e09792e646113e32bdc74254a2cdff032e972c6f2ebedd6391a1e508c36be7c4e33b40e2a07efe029a2ad0267c3ba4dc20b22813df86427bb
|
data/.travis.yml
CHANGED
data/Changes.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
connection\_pool changelog
|
2
|
+
---------------------------
|
3
|
+
|
4
|
+
2.2.2
|
5
|
+
------
|
6
|
+
|
7
|
+
- Add pool `size` and `available` accessors for metrics and monitoring
|
8
|
+
purposes [#97, robholland]
|
9
|
+
|
10
|
+
2.2.1
|
11
|
+
------
|
12
|
+
|
13
|
+
- Allow CP::Wrapper to use an existing pool [#87, etiennebarrie]
|
14
|
+
- Use monotonic time for more accurate timeouts [#84, jdantonio]
|
15
|
+
|
16
|
+
2.2.0
|
17
|
+
------
|
18
|
+
|
19
|
+
- Rollback `Timeout` handling introduced in 2.1.1 and 2.1.2. It seems
|
20
|
+
impossible to safely work around the issue. Please never, ever use
|
21
|
+
`Timeout.timeout` in your code or you will see rare but mysterious bugs. [#75]
|
22
|
+
|
1
23
|
2.1.3
|
2
24
|
------
|
3
25
|
|
data/README.md
CHANGED
@@ -45,8 +45,10 @@ 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
54
|
$redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.connect }
|
@@ -69,17 +71,20 @@ 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
|
|
85
90
|
|
@@ -90,14 +95,10 @@ Notes
|
|
90
95
|
- There is no provision for repairing or checking the health of a connection;
|
91
96
|
connections should be self-repairing. This is true of the Dalli and Redis
|
92
97
|
clients.
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
```
|
99
|
-
$ gem install connection_pool
|
100
|
-
```
|
98
|
+
- **WARNING**: Don't ever use `Timeout.timeout` in your Ruby code or you will see
|
99
|
+
occasional silent corruption and mysterious errors. The Timeout API is unsafe
|
100
|
+
and cannot be used correctly, ever. Use proper socket timeout options as
|
101
|
+
exposed by Net::HTTP, Redis, Dalli, etc.
|
101
102
|
|
102
103
|
|
103
104
|
Author
|
data/lib/connection_pool.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative 'connection_pool/version'
|
2
2
|
require_relative 'connection_pool/timed_stack'
|
3
3
|
|
4
|
+
|
4
5
|
# Generic connection pool class for e.g. sharing a limited number of network connections
|
5
6
|
# among many threads. Note: Connections are lazily created.
|
6
7
|
#
|
@@ -14,7 +15,7 @@ require_relative 'connection_pool/timed_stack'
|
|
14
15
|
#
|
15
16
|
# Using optional timeout override (for that single invocation)
|
16
17
|
#
|
17
|
-
# @pool.with(:
|
18
|
+
# @pool.with(timeout: 2.0) do |redis|
|
18
19
|
# redis.lpop('my-list') if redis.llen('my-list') > 0
|
19
20
|
# end
|
20
21
|
#
|
@@ -50,43 +51,60 @@ class ConnectionPool
|
|
50
51
|
|
51
52
|
@available = TimedStack.new(@size, &block)
|
52
53
|
@key = :"current-#{@available.object_id}"
|
54
|
+
@key_count = :"current-#{@available.object_id}-count"
|
55
|
+
end
|
56
|
+
|
57
|
+
if Thread.respond_to?(:handle_interrupt)
|
58
|
+
|
59
|
+
# MRI
|
60
|
+
def with(options = {})
|
61
|
+
Thread.handle_interrupt(Exception => :never) do
|
62
|
+
conn = checkout(options)
|
63
|
+
begin
|
64
|
+
Thread.handle_interrupt(Exception => :immediate) do
|
65
|
+
yield conn
|
66
|
+
end
|
67
|
+
ensure
|
68
|
+
checkin
|
69
|
+
end
|
70
|
+
end
|
53
71
|
end
|
54
72
|
|
73
|
+
else
|
74
|
+
|
75
|
+
# jruby 1.7.x
|
55
76
|
def with(options = {})
|
56
|
-
# Connections can become corrupted via Timeout::Error. Discard
|
57
|
-
# any connection whose usage after checkout does not finish as expected.
|
58
|
-
# See #67
|
59
|
-
success = false
|
60
77
|
conn = checkout(options)
|
61
78
|
begin
|
62
|
-
|
63
|
-
success = true # means the connection wasn't interrupted
|
64
|
-
result
|
79
|
+
yield conn
|
65
80
|
ensure
|
66
|
-
|
67
|
-
# everything is roses, we can safely check the connection back in
|
68
|
-
checkin
|
69
|
-
else
|
70
|
-
@available.discard!(pop_connection)
|
71
|
-
end
|
81
|
+
checkin
|
72
82
|
end
|
73
83
|
end
|
74
84
|
|
85
|
+
end
|
86
|
+
|
75
87
|
def checkout(options = {})
|
76
|
-
|
77
|
-
|
78
|
-
@
|
88
|
+
if ::Thread.current[@key]
|
89
|
+
::Thread.current[@key_count]+= 1
|
90
|
+
::Thread.current[@key]
|
79
91
|
else
|
80
|
-
|
92
|
+
::Thread.current[@key_count]= 1
|
93
|
+
::Thread.current[@key]= @available.pop(options[:timeout] || @timeout)
|
81
94
|
end
|
82
|
-
|
83
|
-
stack.push conn
|
84
|
-
conn
|
85
95
|
end
|
86
96
|
|
87
97
|
def checkin
|
88
|
-
|
89
|
-
|
98
|
+
if ::Thread.current[@key]
|
99
|
+
if ::Thread.current[@key_count] == 1
|
100
|
+
@available.push(::Thread.current[@key])
|
101
|
+
::Thread.current[@key]= nil
|
102
|
+
else
|
103
|
+
::Thread.current[@key_count]-= 1
|
104
|
+
end
|
105
|
+
else
|
106
|
+
raise ConnectionPool::Error, 'no connections are checked out'
|
107
|
+
end
|
90
108
|
|
91
109
|
nil
|
92
110
|
end
|
@@ -95,25 +113,23 @@ class ConnectionPool
|
|
95
113
|
@available.shutdown(&block)
|
96
114
|
end
|
97
115
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
if stack.empty?
|
102
|
-
raise ConnectionPool::Error, 'no connections are checked out'
|
103
|
-
else
|
104
|
-
stack.pop
|
105
|
-
end
|
116
|
+
# Size of this connection pool
|
117
|
+
def size
|
118
|
+
@size
|
106
119
|
end
|
107
120
|
|
108
|
-
|
109
|
-
|
121
|
+
# Number of pool entries available for checkout at this instant.
|
122
|
+
def available
|
123
|
+
@available.length
|
110
124
|
end
|
111
125
|
|
126
|
+
private
|
127
|
+
|
112
128
|
class Wrapper < ::BasicObject
|
113
129
|
METHODS = [:with, :pool_shutdown]
|
114
130
|
|
115
131
|
def initialize(options = {}, &block)
|
116
|
-
@pool = ::ConnectionPool.new(options, &block)
|
132
|
+
@pool = options.fetch(:pool) { ::ConnectionPool.new(options, &block) }
|
117
133
|
end
|
118
134
|
|
119
135
|
def with(&block)
|
@@ -124,6 +140,14 @@ class ConnectionPool
|
|
124
140
|
@pool.shutdown(&block)
|
125
141
|
end
|
126
142
|
|
143
|
+
def pool_size
|
144
|
+
@pool.size
|
145
|
+
end
|
146
|
+
|
147
|
+
def pool_available
|
148
|
+
@pool.available
|
149
|
+
end
|
150
|
+
|
127
151
|
def respond_to?(id, *args)
|
128
152
|
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
|
129
153
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Global monotonic clock from Concurrent Ruby 1.0.
|
2
|
+
# Copyright (c) Jerry D'Antonio -- released under the MIT license.
|
3
|
+
# Slightly modified; used with permission.
|
4
|
+
# https://github.com/ruby-concurrency/concurrent-ruby
|
5
|
+
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
class ConnectionPool
|
9
|
+
|
10
|
+
class_definition = Class.new do
|
11
|
+
|
12
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
13
|
+
|
14
|
+
# @!visibility private
|
15
|
+
def get_time
|
16
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
17
|
+
end
|
18
|
+
|
19
|
+
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
20
|
+
|
21
|
+
# @!visibility private
|
22
|
+
def get_time
|
23
|
+
java.lang.System.nanoTime() / 1_000_000_000.0
|
24
|
+
end
|
25
|
+
|
26
|
+
else
|
27
|
+
|
28
|
+
# @!visibility private
|
29
|
+
def initialize
|
30
|
+
@mutex = Mutex.new
|
31
|
+
@last_time = Time.now.to_f
|
32
|
+
end
|
33
|
+
|
34
|
+
# @!visibility private
|
35
|
+
def get_time
|
36
|
+
@mutex.synchronize do
|
37
|
+
now = Time.now.to_f
|
38
|
+
if @last_time < now
|
39
|
+
@last_time = now
|
40
|
+
else # clock has moved back in time
|
41
|
+
@last_time += 0.000_001
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Clock that cannot be set and represents monotonic time since
|
50
|
+
# some unspecified starting point.
|
51
|
+
#
|
52
|
+
# @!visibility private
|
53
|
+
GLOBAL_MONOTONIC_CLOCK = class_definition.new
|
54
|
+
private_constant :GLOBAL_MONOTONIC_CLOCK
|
55
|
+
|
56
|
+
class << self
|
57
|
+
##
|
58
|
+
# Returns the current time a tracked by the application monotonic clock.
|
59
|
+
#
|
60
|
+
# @return [Float] The current monotonic time when `since` not given else
|
61
|
+
# the elapsed monotonic time between `since` and the current time
|
62
|
+
def monotonic_time
|
63
|
+
GLOBAL_MONOTONIC_CLOCK.get_time
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'timeout'
|
3
|
+
require_relative 'monotonic_time'
|
3
4
|
|
4
5
|
##
|
5
6
|
# Raised when you attempt to retrieve a connection from a pool that has been
|
@@ -27,6 +28,7 @@ class ConnectionPool::PoolShuttingDownError < RuntimeError; end
|
|
27
28
|
# #=> raises Timeout::Error after 5 seconds
|
28
29
|
|
29
30
|
class ConnectionPool::TimedStack
|
31
|
+
attr_reader :max
|
30
32
|
|
31
33
|
##
|
32
34
|
# Creates a new pool with +size+ connections that are created from the given
|
@@ -72,7 +74,7 @@ class ConnectionPool::TimedStack
|
|
72
74
|
options, timeout = timeout, 0.5 if Hash === timeout
|
73
75
|
timeout = options.fetch :timeout, timeout
|
74
76
|
|
75
|
-
deadline =
|
77
|
+
deadline = ConnectionPool.monotonic_time + timeout
|
76
78
|
@mutex.synchronize do
|
77
79
|
loop do
|
78
80
|
raise ConnectionPool::PoolShuttingDownError if @shutdown_block
|
@@ -81,7 +83,7 @@ class ConnectionPool::TimedStack
|
|
81
83
|
connection = try_create(options)
|
82
84
|
return connection if connection
|
83
85
|
|
84
|
-
to_wait = deadline -
|
86
|
+
to_wait = deadline - ConnectionPool.monotonic_time
|
85
87
|
raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
|
86
88
|
@resource.wait(@mutex, to_wait)
|
87
89
|
end
|
@@ -117,28 +119,6 @@ class ConnectionPool::TimedStack
|
|
117
119
|
@max - @created + @que.length
|
118
120
|
end
|
119
121
|
|
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
|
137
|
-
|
138
|
-
@resource.broadcast
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
122
|
private
|
143
123
|
|
144
124
|
##
|
@@ -3,13 +3,15 @@ require_relative 'helper'
|
|
3
3
|
class TestConnectionPool < Minitest::Test
|
4
4
|
|
5
5
|
class NetworkConnection
|
6
|
+
SLEEP_TIME = 0.1
|
7
|
+
|
6
8
|
def initialize
|
7
9
|
@x = 0
|
8
10
|
end
|
9
11
|
|
10
12
|
def do_something
|
11
13
|
@x += 1
|
12
|
-
sleep
|
14
|
+
sleep SLEEP_TIME
|
13
15
|
@x
|
14
16
|
end
|
15
17
|
|
@@ -19,7 +21,7 @@ class TestConnectionPool < Minitest::Test
|
|
19
21
|
|
20
22
|
def do_something_with_block
|
21
23
|
@x += yield
|
22
|
-
sleep
|
24
|
+
sleep SLEEP_TIME
|
23
25
|
@x
|
24
26
|
end
|
25
27
|
|
@@ -58,25 +60,30 @@ class TestConnectionPool < Minitest::Test
|
|
58
60
|
end
|
59
61
|
|
60
62
|
def test_basic_multithreaded_usage
|
61
|
-
|
63
|
+
pool_size = 5
|
64
|
+
pool = ConnectionPool.new(size: pool_size) { NetworkConnection.new }
|
65
|
+
|
66
|
+
start = Time.new
|
62
67
|
|
63
|
-
|
68
|
+
generations = 3
|
69
|
+
|
70
|
+
result = Array.new(pool_size * generations) do
|
64
71
|
Thread.new do
|
65
72
|
pool.with do |net|
|
66
73
|
net.do_something
|
67
74
|
end
|
68
75
|
end
|
69
|
-
end
|
76
|
+
end.map(&:value)
|
70
77
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
78
|
+
finish = Time.new
|
79
|
+
|
80
|
+
assert_equal((1..generations).cycle(pool_size).sort, result.sort)
|
81
|
+
|
82
|
+
assert_operator(finish - start, :>, generations * NetworkConnection::SLEEP_TIME)
|
76
83
|
end
|
77
84
|
|
78
85
|
def test_timeout
|
79
|
-
pool = ConnectionPool.new(:
|
86
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
80
87
|
thread = Thread.new do
|
81
88
|
pool.with do |net|
|
82
89
|
net.do_something
|
@@ -98,7 +105,7 @@ class TestConnectionPool < Minitest::Test
|
|
98
105
|
end
|
99
106
|
|
100
107
|
def test_with
|
101
|
-
pool = ConnectionPool.new(:
|
108
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
102
109
|
|
103
110
|
pool.with do
|
104
111
|
assert_raises Timeout::Error do
|
@@ -109,42 +116,62 @@ class TestConnectionPool < Minitest::Test
|
|
109
116
|
assert Thread.new { pool.checkout }.join
|
110
117
|
end
|
111
118
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
+
def test_with_timeout
|
120
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
121
|
+
|
122
|
+
assert_raises Timeout::Error do
|
123
|
+
Timeout.timeout(0.01) do
|
124
|
+
pool.with do |obj|
|
125
|
+
assert_equal 0, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
|
126
|
+
sleep 0.015
|
127
|
+
end
|
119
128
|
end
|
120
129
|
end
|
130
|
+
assert_equal 1, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
|
131
|
+
end
|
121
132
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
# no "connections" allocated yet
|
126
|
-
assert_equal [], ObjectSpace.each_object(marker_class).to_a
|
133
|
+
def test_checkout_ignores_timeout
|
134
|
+
skip("Thread.handle_interrupt not available") unless Thread.respond_to?(:handle_interrupt)
|
127
135
|
|
128
|
-
|
136
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
137
|
+
def pool.checkout(options)
|
138
|
+
sleep 0.015
|
139
|
+
super
|
140
|
+
end
|
129
141
|
|
142
|
+
did_something = false
|
130
143
|
assert_raises Timeout::Error do
|
131
|
-
Timeout.timeout(
|
132
|
-
pool.with do
|
133
|
-
|
134
|
-
|
135
|
-
|
144
|
+
Timeout.timeout(0.01) do
|
145
|
+
pool.with do |obj|
|
146
|
+
did_something = true
|
147
|
+
# Timeout::Error will be triggered by any non-trivial Ruby code
|
148
|
+
# executed here since it couldn't be raised during checkout.
|
149
|
+
# It looks like setting the local variable above does not trigger
|
150
|
+
# the Timeout check in MRI 2.2.1.
|
151
|
+
obj.tap { obj.hash }
|
136
152
|
end
|
137
153
|
end
|
138
154
|
end
|
155
|
+
assert did_something
|
156
|
+
assert_equal 1, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
|
157
|
+
end
|
139
158
|
|
140
|
-
|
159
|
+
def test_explicit_return
|
160
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) do
|
161
|
+
mock = Minitest::Mock.new
|
162
|
+
def mock.disconnect!
|
163
|
+
raise "should not disconnect upon explicit return"
|
164
|
+
end
|
165
|
+
mock
|
166
|
+
end
|
141
167
|
|
142
|
-
|
143
|
-
|
168
|
+
pool.with do |conn|
|
169
|
+
return true
|
170
|
+
end
|
144
171
|
end
|
145
172
|
|
146
173
|
def test_with_timeout_override
|
147
|
-
pool = ConnectionPool.new(:
|
174
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
148
175
|
|
149
176
|
t = Thread.new do
|
150
177
|
pool.with do |net|
|
@@ -159,13 +186,13 @@ class TestConnectionPool < Minitest::Test
|
|
159
186
|
pool.with { |net| net.do_something }
|
160
187
|
end
|
161
188
|
|
162
|
-
pool.with(:
|
189
|
+
pool.with(timeout: 2 * NetworkConnection::SLEEP_TIME) do |conn|
|
163
190
|
refute_nil conn
|
164
191
|
end
|
165
192
|
end
|
166
193
|
|
167
194
|
def test_checkin
|
168
|
-
pool = ConnectionPool.new(:
|
195
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
169
196
|
conn = pool.checkout
|
170
197
|
|
171
198
|
assert_raises Timeout::Error do
|
@@ -178,12 +205,12 @@ class TestConnectionPool < Minitest::Test
|
|
178
205
|
end
|
179
206
|
|
180
207
|
def test_returns_value
|
181
|
-
pool = ConnectionPool.new(:
|
208
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
182
209
|
assert_equal 1, pool.with {|o| 1 }
|
183
210
|
end
|
184
211
|
|
185
212
|
def test_checkin_never_checkout
|
186
|
-
pool = ConnectionPool.new(:
|
213
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
187
214
|
|
188
215
|
e = assert_raises ConnectionPool::Error do
|
189
216
|
pool.checkin
|
@@ -193,7 +220,7 @@ class TestConnectionPool < Minitest::Test
|
|
193
220
|
end
|
194
221
|
|
195
222
|
def test_checkin_no_current_checkout
|
196
|
-
pool = ConnectionPool.new(:
|
223
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
197
224
|
|
198
225
|
pool.checkout
|
199
226
|
pool.checkin
|
@@ -204,7 +231,7 @@ class TestConnectionPool < Minitest::Test
|
|
204
231
|
end
|
205
232
|
|
206
233
|
def test_checkin_twice
|
207
|
-
pool = ConnectionPool.new(:
|
234
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
|
208
235
|
|
209
236
|
pool.checkout
|
210
237
|
pool.checkout
|
@@ -223,7 +250,7 @@ class TestConnectionPool < Minitest::Test
|
|
223
250
|
end
|
224
251
|
|
225
252
|
def test_checkout
|
226
|
-
pool = ConnectionPool.new(:
|
253
|
+
pool = ConnectionPool.new(size: 1) { NetworkConnection.new }
|
227
254
|
|
228
255
|
conn = pool.checkout
|
229
256
|
|
@@ -233,7 +260,7 @@ class TestConnectionPool < Minitest::Test
|
|
233
260
|
end
|
234
261
|
|
235
262
|
def test_checkout_multithread
|
236
|
-
pool = ConnectionPool.new(:
|
263
|
+
pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
|
237
264
|
conn = pool.checkout
|
238
265
|
|
239
266
|
t = Thread.new do
|
@@ -244,7 +271,7 @@ class TestConnectionPool < Minitest::Test
|
|
244
271
|
end
|
245
272
|
|
246
273
|
def test_checkout_timeout
|
247
|
-
pool = ConnectionPool.new(:
|
274
|
+
pool = ConnectionPool.new(timeout: 0, size: 0) { Object.new }
|
248
275
|
|
249
276
|
assert_raises Timeout::Error do
|
250
277
|
pool.checkout
|
@@ -252,7 +279,7 @@ class TestConnectionPool < Minitest::Test
|
|
252
279
|
end
|
253
280
|
|
254
281
|
def test_checkout_timeout_override
|
255
|
-
pool = ConnectionPool.new(:
|
282
|
+
pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
|
256
283
|
|
257
284
|
thread = Thread.new do
|
258
285
|
pool.with do |net|
|
@@ -267,11 +294,11 @@ class TestConnectionPool < Minitest::Test
|
|
267
294
|
pool.checkout
|
268
295
|
end
|
269
296
|
|
270
|
-
assert pool.checkout :
|
297
|
+
assert pool.checkout timeout: 2 * NetworkConnection::SLEEP_TIME
|
271
298
|
end
|
272
299
|
|
273
300
|
def test_passthru
|
274
|
-
pool = ConnectionPool.wrap(:
|
301
|
+
pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
|
275
302
|
assert_equal 1, pool.do_something
|
276
303
|
assert_equal 2, pool.do_something
|
277
304
|
assert_equal 5, pool.do_something_with_block { 3 }
|
@@ -279,7 +306,7 @@ class TestConnectionPool < Minitest::Test
|
|
279
306
|
end
|
280
307
|
|
281
308
|
def test_passthru_respond_to
|
282
|
-
pool = ConnectionPool.wrap(:
|
309
|
+
pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
|
283
310
|
assert pool.respond_to?(:with)
|
284
311
|
assert pool.respond_to?(:do_something)
|
285
312
|
assert pool.respond_to?(:do_magic)
|
@@ -287,7 +314,7 @@ class TestConnectionPool < Minitest::Test
|
|
287
314
|
end
|
288
315
|
|
289
316
|
def test_return_value
|
290
|
-
pool = ConnectionPool.new(:
|
317
|
+
pool = ConnectionPool.new(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
|
291
318
|
result = pool.with do |net|
|
292
319
|
net.fast
|
293
320
|
end
|
@@ -295,7 +322,7 @@ class TestConnectionPool < Minitest::Test
|
|
295
322
|
end
|
296
323
|
|
297
324
|
def test_heavy_threading
|
298
|
-
pool = ConnectionPool.new(:
|
325
|
+
pool = ConnectionPool.new(timeout: 0.5, size: 3) { NetworkConnection.new }
|
299
326
|
|
300
327
|
threads = Array.new(20) do
|
301
328
|
Thread.new do
|
@@ -309,7 +336,7 @@ class TestConnectionPool < Minitest::Test
|
|
309
336
|
end
|
310
337
|
|
311
338
|
def test_reuses_objects_when_pool_not_saturated
|
312
|
-
pool = ConnectionPool.new(:
|
339
|
+
pool = ConnectionPool.new(size: 5) { NetworkConnection.new }
|
313
340
|
|
314
341
|
ids = 10.times.map do
|
315
342
|
pool.with { |c| c.object_id }
|
@@ -320,7 +347,7 @@ class TestConnectionPool < Minitest::Test
|
|
320
347
|
|
321
348
|
def test_nested_checkout
|
322
349
|
recorder = Recorder.new
|
323
|
-
pool = ConnectionPool.new(:
|
350
|
+
pool = ConnectionPool.new(size: 1) { recorder }
|
324
351
|
pool.with do |r_outer|
|
325
352
|
@other = Thread.new do |t|
|
326
353
|
pool.with do |r_other|
|
@@ -345,7 +372,7 @@ class TestConnectionPool < Minitest::Test
|
|
345
372
|
def test_shutdown_is_executed_for_all_connections
|
346
373
|
recorders = []
|
347
374
|
|
348
|
-
pool = ConnectionPool.new(:
|
375
|
+
pool = ConnectionPool.new(size: 3) do
|
349
376
|
Recorder.new.tap { |r| recorders << r }
|
350
377
|
end
|
351
378
|
|
@@ -361,7 +388,7 @@ class TestConnectionPool < Minitest::Test
|
|
361
388
|
end
|
362
389
|
|
363
390
|
def test_raises_error_after_shutting_down
|
364
|
-
pool = ConnectionPool.new(:
|
391
|
+
pool = ConnectionPool.new(size: 1) { true }
|
365
392
|
|
366
393
|
pool.shutdown { }
|
367
394
|
|
@@ -373,7 +400,7 @@ class TestConnectionPool < Minitest::Test
|
|
373
400
|
def test_runs_shutdown_block_asynchronously_if_connection_was_in_use
|
374
401
|
recorders = []
|
375
402
|
|
376
|
-
pool = ConnectionPool.new(:
|
403
|
+
pool = ConnectionPool.new(size: 3) do
|
377
404
|
Recorder.new.tap { |r| recorders << r }
|
378
405
|
end
|
379
406
|
|
@@ -395,7 +422,7 @@ class TestConnectionPool < Minitest::Test
|
|
395
422
|
end
|
396
423
|
|
397
424
|
def test_raises_an_error_if_shutdown_is_called_without_a_block
|
398
|
-
pool = ConnectionPool.new(:
|
425
|
+
pool = ConnectionPool.new(size: 1) { }
|
399
426
|
|
400
427
|
assert_raises ArgumentError do
|
401
428
|
pool.shutdown
|
@@ -405,7 +432,7 @@ class TestConnectionPool < Minitest::Test
|
|
405
432
|
def test_shutdown_is_executed_for_all_connections_in_wrapped_pool
|
406
433
|
recorders = []
|
407
434
|
|
408
|
-
wrapper = ConnectionPool::Wrapper.new(:
|
435
|
+
wrapper = ConnectionPool::Wrapper.new(size: 3) do
|
409
436
|
Recorder.new.tap { |r| recorders << r }
|
410
437
|
end
|
411
438
|
|
@@ -436,7 +463,7 @@ class TestConnectionPool < Minitest::Test
|
|
436
463
|
end
|
437
464
|
|
438
465
|
def test_wrapper_with
|
439
|
-
wrapper = ConnectionPool::Wrapper.new(:
|
466
|
+
wrapper = ConnectionPool::Wrapper.new(timeout: 0, size: 1) { Object.new }
|
440
467
|
|
441
468
|
wrapper.with do
|
442
469
|
assert_raises Timeout::Error do
|
@@ -461,4 +488,29 @@ class TestConnectionPool < Minitest::Test
|
|
461
488
|
assert_equal "eval'ed 1", wrapper.eval(1)
|
462
489
|
end
|
463
490
|
|
491
|
+
def test_wrapper_with_connection_pool
|
492
|
+
recorder = Recorder.new
|
493
|
+
pool = ConnectionPool.new(size: 1) { recorder }
|
494
|
+
wrapper = ConnectionPool::Wrapper.new(pool: pool)
|
495
|
+
|
496
|
+
pool.with { |r| r.do_work('with') }
|
497
|
+
wrapper.do_work('wrapped')
|
498
|
+
|
499
|
+
assert_equal ['with', 'wrapped'], recorder.calls
|
500
|
+
end
|
501
|
+
|
502
|
+
def test_stats_without_active_connection
|
503
|
+
pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
|
504
|
+
|
505
|
+
assert_equal(2, pool.size)
|
506
|
+
assert_equal(2, pool.available)
|
507
|
+
end
|
508
|
+
|
509
|
+
def test_stats_with_active_connection
|
510
|
+
pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
|
511
|
+
|
512
|
+
pool.with do
|
513
|
+
assert_equal(1, pool.available)
|
514
|
+
end
|
515
|
+
end
|
464
516
|
end
|
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.2
|
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: 2018-05-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- Rakefile
|
71
71
|
- connection_pool.gemspec
|
72
72
|
- lib/connection_pool.rb
|
73
|
+
- lib/connection_pool/monotonic_time.rb
|
73
74
|
- lib/connection_pool/timed_stack.rb
|
74
75
|
- lib/connection_pool/version.rb
|
75
76
|
- test/helper.rb
|
@@ -95,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
96
|
version: '0'
|
96
97
|
requirements: []
|
97
98
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.
|
99
|
+
rubygems_version: 2.6.13
|
99
100
|
signing_key:
|
100
101
|
specification_version: 4
|
101
102
|
summary: Generic connection pool for Ruby
|