connection_pool 2.1.3 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
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