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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec91685df50fe18b79ef2cd2ed2fb038e322ff81
4
- data.tar.gz: 99b88e1aa01811e18c2737e8e062c4726ce54666
3
+ metadata.gz: cf8177bf07f6c8e68b13f28416f6ccc0c64803af
4
+ data.tar.gz: ff9c836ce5576d4d0cb1edf8d858a761f982e437
5
5
  SHA512:
6
- metadata.gz: 53d34f8ca6a7d51ce535a83e2821dd33eb15a623df3022b89775c232876c8fec28a2b33adda24248136014d99f0ebe9f09c1f49e1f0ced76435db26538e907a7
7
- data.tar.gz: 660f22d0f828c789942da1da3d7c4131e008dd383a83c74e59ea03f306a59b966e879d42f9704ffa3b7de6accb6c114cfff982d3343bbe34c5e60ecc8f705ac5
6
+ metadata.gz: 73bda4d8d0b3cc9daef5c93bb98b7649d811bd5c8f467e25c3aaf7d3353541225e7b30e1efd481a14e45f5c56b5fc846d49dabdd9a167702a71c4a9631a9b6a2
7
+ data.tar.gz: a331275f67b3635e09792e646113e32bdc74254a2cdff032e972c6f2ebedd6391a1e508c36be7c4e33b40e2a07efe029a2ad0267c3ba4dc20b22813df86427bb
@@ -3,12 +3,8 @@ sudo: false
3
3
  cache: bundler
4
4
  language: ruby
5
5
  rvm:
6
- - 1.9.3
7
- - 2.0.0
8
- - 2.1.0
9
- - 2.2.0
6
+ - 2.2.9
7
+ - 2.3.6
8
+ - 2.4.3
9
+ - 2.5.0
10
10
  - jruby
11
- - rbx-2
12
- notifications:
13
- email:
14
- - drbrain@segment7.net
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 port your connection code over time:
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.close }
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; Ruby's garbage collector will reclaim
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
- Install
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
@@ -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(:timeout => 2.0) do |redis|
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
- result = yield conn
63
- success = true # means the connection wasn't interrupted
64
- result
79
+ yield conn
65
80
  ensure
66
- if success
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
- conn = if stack.empty?
77
- timeout = options[:timeout] || @timeout
78
- @available.pop(timeout: timeout)
88
+ if ::Thread.current[@key]
89
+ ::Thread.current[@key_count]+= 1
90
+ ::Thread.current[@key]
79
91
  else
80
- stack.last
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
- conn = pop_connection # mutates stack, must be on its own line
89
- @available.push(conn) if stack.empty?
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
- private
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
116
+ # Size of this connection pool
117
+ def size
118
+ @size
106
119
  end
107
120
 
108
- def stack
109
- ::Thread.current[@key] ||= []
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 = Time.now + timeout
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 - Time.now
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
  ##
@@ -1,3 +1,3 @@
1
1
  class ConnectionPool
2
- VERSION = "2.1.3"
2
+ VERSION = "2.2.2"
3
3
  end
@@ -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 0.05
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 0.05
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
- pool = ConnectionPool.new(:size => 5) { NetworkConnection.new }
63
+ pool_size = 5
64
+ pool = ConnectionPool.new(size: pool_size) { NetworkConnection.new }
65
+
66
+ start = Time.new
62
67
 
63
- threads = Array.new(15) do
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
- a = Time.now
72
- result = threads.map(&:value)
73
- b = Time.now
74
- assert_operator((b - a), :>, 0.125)
75
- assert_equal([1,2,3].cycle(5).sort, result.sort)
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(:timeout => 0, :size => 1) { NetworkConnection.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(:timeout => 0, :size => 1) { Object.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 test_with_with_dangerous_timeouts
113
- case RUBY_ENGINE.to_sym
114
- when :jruby
115
- skip('JRuby GC dislikes this test')
116
- when :ruby
117
- if RUBY_VERSION == '2.0.0' && RUBY_PATCHLEVEL == 598
118
- skip("#{RUBY_VERSION}p#{RUBY_PATCHLEVEL} GC dislikes this test")
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
- marker_class = Class.new
123
- pool = ConnectionPool.new(:timeout => 0, :size => 1) { marker_class.new }
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
- checkin_time = 0.05
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(checkin_time) do
132
- pool.with do
133
- # a "connection" has been allocated
134
- refute_equal [], ObjectSpace.each_object(marker_class).to_a
135
- sleep 2 * checkin_time
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
- GC.start
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
- # no dangling references to this "connection" remain
143
- assert_equal [], ObjectSpace.each_object(marker_class).to_a
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(:timeout => 0, :size => 1) { NetworkConnection.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(:timeout => 0.1) do |conn|
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(:timeout => 0, :size => 1) { NetworkConnection.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(:timeout => 0, :size => 1) { Object.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(:timeout => 0, :size => 1) { Object.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(:timeout => 0, :size => 1) { Object.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(:timeout => 0, :size => 1) { Object.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(:size => 1) { NetworkConnection.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(:size => 2) { NetworkConnection.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(:timeout => 0, :size => 0) { Object.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(:timeout => 0, :size => 1) { NetworkConnection.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 :timeout => 0.1
297
+ assert pool.checkout timeout: 2 * NetworkConnection::SLEEP_TIME
271
298
  end
272
299
 
273
300
  def test_passthru
274
- pool = ConnectionPool.wrap(:timeout => 0.1, :size => 1) { NetworkConnection.new }
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(:timeout => 0.1, :size => 1) { NetworkConnection.new }
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(:timeout => 0.1, :size => 1) { NetworkConnection.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(:timeout => 0.5, :size => 3) { NetworkConnection.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(:size => 5) { NetworkConnection.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(:size => 1) { recorder }
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(:size => 3) do
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(:size => 1) { true }
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(:size => 3) do
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(:size => 1) { }
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(:size => 3) do
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(:timeout => 0, :size => 1) { Object.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.1.3
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: 2015-03-19 00:00:00.000000000 Z
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.4.5
99
+ rubygems_version: 2.6.13
99
100
  signing_key:
100
101
  specification_version: 4
101
102
  summary: Generic connection pool for Ruby