connection_pool 2.2.4 → 2.3.0

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
  SHA256:
3
- metadata.gz: 0af12e8d2f551b932ee863a1385ddc9a3f3140fff886cacaca25298a4c21d218
4
- data.tar.gz: 16bd7b78d7b35337d35befe063ceeb2cb749dfc9e68cd373e7f76bd856f6cba3
3
+ metadata.gz: f55833e24bd0a92578d10d974f63342be9abd52ed1736d0821feb0f48f05cabe
4
+ data.tar.gz: cb5ad1fa9d0e79ec9eef583e6a8771f1e0208d5c0daa52d31a75523e3ccf3da1
5
5
  SHA512:
6
- metadata.gz: edb345021997307fe736408ad3e3cbebd2ce86fcbdc636fe2d4f03d61258ecea22285a4f91e854d43631533513d6500c9f6cf2094f52d6756a2d61ee2daba256
7
- data.tar.gz: d58d7519b9d4be9bcf7b0628e2aa9d937df2d61d0d1a9423b43618685adf242810256b9decc22bca90cb3ee3eec88e8eabffb409aa9e49f28d56815f26056b81
6
+ metadata.gz: 37b6ee51a8563cd84846ac6647507103ff5a689a28e0a4daf203fdbfac5d7bb09a1416e7eaa71f674522ca9062a05557146182008af016130cbf7eda02eee21d
7
+ data.tar.gz: bba4be5b638f77942e9978328b7a247f5638c4860c263dc58277d39a4239bf3520bfebf14547e3cc8436e85c1be1b88a24246f113bc4038f699229ce41890e04
data/Changes.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # connection_pool Changelog
2
2
 
3
+ 2.3.0
4
+ ------
5
+
6
+ - Minimum Ruby version is now 2.5.0
7
+ - Add pool size to TimeoutError message
8
+
9
+ 2.2.5
10
+ ------
11
+
12
+ - Fix argument forwarding on Ruby 2.7 [#149]
13
+
3
14
  2.2.4
4
15
  ------
5
16
 
data/README.md CHANGED
@@ -1,19 +1,17 @@
1
1
  connection\_pool
2
2
  =================
3
- [![Build Status](https://travis-ci.org/mperham/connection_pool.svg)](https://travis-ci.org/mperham/connection_pool)
3
+ [![Build Status](https://github.com/mperham/connection_pool/actions/workflows/ci.yml/badge.svg)](https://github.com/mperham/connection_pool/actions/workflows/ci.yml)
4
4
 
5
5
  Generic connection pooling for Ruby.
6
6
 
7
- MongoDB has its own connection pool. ActiveRecord has its own connection pool.
8
- This is a generic connection pool that can be used with anything, e.g. Redis,
9
- Dalli and other Ruby network clients.
10
-
7
+ MongoDB has its own connection pool.
8
+ ActiveRecord has its own connection pool.
9
+ This is a generic connection pool that can be used with anything, e.g. Redis, Dalli and other Ruby network clients.
11
10
 
12
11
  Usage
13
12
  -----
14
13
 
15
- Create a pool of objects to share amongst the fibers or threads in your Ruby
16
- application:
14
+ Create a pool of objects to share amongst the fibers or threads in your Ruby application:
17
15
 
18
16
  ``` ruby
19
17
  $memcached = ConnectionPool.new(size: 5, timeout: 5) { Dalli::Client.new }
@@ -28,11 +26,12 @@ end
28
26
  ```
29
27
 
30
28
  If all the objects in the connection pool are in use, `with` will block
31
- until one becomes available. If no object is available within `:timeout` seconds,
32
- `with` will raise a `Timeout::Error`.
29
+ until one becomes available.
30
+ If no object is available within `:timeout` seconds,
31
+ `with` will raise a `ConnectionPool::TimeoutError` (a subclass of `Timeout::Error`).
33
32
 
34
33
  You can also use `ConnectionPool#then` to support _both_ a
35
- connection pool and a raw client (requires Ruby 2.5+).
34
+ connection pool and a raw client.
36
35
 
37
36
  ```ruby
38
37
  # Compatible with a raw Redis::Client, and ConnectionPool Redis
@@ -48,15 +47,14 @@ end
48
47
  ```
49
48
 
50
49
  This will only modify the resource-get timeout for this particular
51
- invocation. This is useful if you want to fail-fast on certain non critical
52
- sections when a resource is not available, or conversely if you are comfortable
53
- blocking longer on a particular resource. This is not implemented in the below
54
- `ConnectionPool::Wrapper` class.
50
+ invocation.
51
+ This is useful if you want to fail-fast on certain non critical
52
+ sections when a resource is not available, or conversely if you are comfortable blocking longer on a particular resource.
53
+ This is not implemented in the `ConnectionPool::Wrapper` class.
55
54
 
56
55
  ## Migrating to a Connection Pool
57
56
 
58
- You can use `ConnectionPool::Wrapper` to wrap a single global connection,
59
- making it easier to migrate existing connection code over time:
57
+ You can use `ConnectionPool::Wrapper` to wrap a single global connection, making it easier to migrate existing connection code over time:
60
58
 
61
59
  ``` ruby
62
60
  $redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.new }
@@ -64,10 +62,8 @@ $redis.sadd('foo', 1)
64
62
  $redis.smembers('foo')
65
63
  ```
66
64
 
67
- The wrapper uses `method_missing` to checkout a connection, run the requested
68
- method and then immediately check the connection back into the pool. It's
69
- **not** high-performance so you'll want to port your performance sensitive code
70
- to use `with` as soon as possible.
65
+ The wrapper uses `method_missing` to checkout a connection, run the requested method and then immediately check the connection back into the pool.
66
+ It's **not** high-performance so you'll want to port your performance sensitive code to use `with` as soon as possible.
71
67
 
72
68
  ``` ruby
73
69
  $redis.with do |conn|
@@ -76,31 +72,26 @@ $redis.with do |conn|
76
72
  end
77
73
  ```
78
74
 
79
- Once you've ported your entire system to use `with`, you can simply remove
80
- `Wrapper` and use the simpler and faster `ConnectionPool`.
75
+ Once you've ported your entire system to use `with`, you can simply remove `Wrapper` and use the simpler and faster `ConnectionPool`.
81
76
 
82
77
 
83
78
  ## Shutdown
84
79
 
85
80
  You can shut down a ConnectionPool instance once it should no longer be used.
86
- Further checkout attempts will immediately raise an error but existing checkouts
87
- will work.
81
+ Further checkout attempts will immediately raise an error but existing checkouts will work.
88
82
 
89
83
  ```ruby
90
84
  cp = ConnectionPool.new { Redis.new }
91
- cp.shutdown { |conn| conn.quit }
85
+ cp.shutdown { |c| c.close }
92
86
  ```
93
87
 
94
88
  Shutting down a connection pool will block until all connections are checked in and closed.
95
- **Note that shutting down is completely optional**; Ruby's garbage collector will reclaim
96
- unreferenced pools under normal circumstances.
89
+ **Note that shutting down is completely optional**; Ruby's garbage collector will reclaim unreferenced pools under normal circumstances.
97
90
 
98
91
  ## Reload
99
92
 
100
- You can reload a ConnectionPool instance in the case it is desired to close all
101
- connections to the pool and, unlike `shutdown`, afterwards recreate connections
102
- so the pool may continue to be used. Reloading may be useful after forking the
103
- process.
93
+ You can reload a ConnectionPool instance in the case it is desired to close all connections to the pool and, unlike `shutdown`, afterwards recreate connections so the pool may continue to be used.
94
+ Reloading may be useful after forking the process.
104
95
 
105
96
  ```ruby
106
97
  cp = ConnectionPool.new { Redis.new }
@@ -108,8 +99,7 @@ cp.reload { |conn| conn.quit }
108
99
  cp.with { |conn| conn.get('some-count') }
109
100
  ```
110
101
 
111
- Like `shutdown`, this will block until all connections are checked in and
112
- closed.
102
+ Like `shutdown`, this will block until all connections are checked in and closed.
113
103
 
114
104
  ## Current State
115
105
 
@@ -9,13 +9,14 @@ Gem::Specification.new do |s|
9
9
  s.homepage = "https://github.com/mperham/connection_pool"
10
10
  s.description = s.summary = "Generic connection pool for Ruby"
11
11
 
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) }
12
+ s.files = ["Changes.md", "LICENSE", "README.md", "connection_pool.gemspec",
13
+ "lib/connection_pool.rb", "lib/connection_pool/timed_stack.rb",
14
+ "lib/connection_pool/version.rb", "lib/connection_pool/wrapper.rb"]
15
+ s.executables = []
15
16
  s.require_paths = ["lib"]
16
17
  s.license = "MIT"
17
18
  s.add_development_dependency "bundler"
18
19
  s.add_development_dependency "minitest", ">= 5.0.0"
19
20
  s.add_development_dependency "rake"
20
- s.required_ruby_version = ">= 2.2.0"
21
+ s.required_ruby_version = ">= 2.5.0"
21
22
  end
@@ -29,8 +29,8 @@ class ConnectionPool::TimedStack
29
29
  @created = 0
30
30
  @que = []
31
31
  @max = size
32
- @mutex = Mutex.new
33
- @resource = ConditionVariable.new
32
+ @mutex = Thread::Mutex.new
33
+ @resource = Thread::ConditionVariable.new
34
34
  @shutdown_block = nil
35
35
  end
36
36
 
@@ -49,7 +49,7 @@ class ConnectionPool::TimedStack
49
49
  @resource.broadcast
50
50
  end
51
51
  end
52
- alias << push
52
+ alias_method :<<, :push
53
53
 
54
54
  ##
55
55
  # Retrieves a connection from the stack. If a connection is available it is
@@ -74,7 +74,7 @@ class ConnectionPool::TimedStack
74
74
  return connection if connection
75
75
 
76
76
  to_wait = deadline - current_time
77
- raise ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0
77
+ raise ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" if to_wait <= 0
78
78
  @resource.wait(@mutex, to_wait)
79
79
  end
80
80
  end
@@ -87,7 +87,7 @@ class ConnectionPool::TimedStack
87
87
  # +:reload+ is +true+.
88
88
 
89
89
  def shutdown(reload: false, &block)
90
- raise ArgumentError, "shutdown must receive a block" unless block_given?
90
+ raise ArgumentError, "shutdown must receive a block" unless block
91
91
 
92
92
  @mutex.synchronize do
93
93
  @shutdown_block = block
@@ -1,3 +1,3 @@
1
1
  class ConnectionPool
2
- VERSION = "2.2.4"
2
+ VERSION = "2.3.0"
3
3
  end
@@ -30,14 +30,19 @@ class ConnectionPool
30
30
  METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
31
31
  end
32
32
 
33
- # rubocop:disable Style/MethodMissingSuper
34
33
  # rubocop:disable Style/MissingRespondToMissing
35
- if ::RUBY_VERSION >= "2.7.0"
34
+ if ::RUBY_VERSION >= "3.0.0"
36
35
  def method_missing(name, *args, **kwargs, &block)
37
36
  with do |connection|
38
37
  connection.send(name, *args, **kwargs, &block)
39
38
  end
40
39
  end
40
+ elsif ::RUBY_VERSION >= "2.7.0"
41
+ ruby2_keywords def method_missing(name, *args, &block)
42
+ with do |connection|
43
+ connection.send(name, *args, &block)
44
+ end
45
+ end
41
46
  else
42
47
  def method_missing(name, *args, &block)
43
48
  with do |connection|
@@ -1,9 +1,11 @@
1
1
  require "timeout"
2
- require "connection_pool/version"
2
+ require_relative "connection_pool/version"
3
3
 
4
4
  class ConnectionPool
5
5
  class Error < ::RuntimeError; end
6
+
6
7
  class PoolShuttingDownError < ::ConnectionPool::Error; end
8
+
7
9
  class TimeoutError < ::Timeout::Error; end
8
10
  end
9
11
 
@@ -67,7 +69,7 @@ class ConnectionPool
67
69
  end
68
70
  end
69
71
  end
70
- alias then with
72
+ alias_method :then, :with
71
73
 
72
74
  def checkout(options = {})
73
75
  if ::Thread.current[@key]
@@ -122,5 +124,5 @@ class ConnectionPool
122
124
  end
123
125
  end
124
126
 
125
- require "connection_pool/timed_stack"
126
- require "connection_pool/wrapper"
127
+ require_relative "connection_pool/timed_stack"
128
+ require_relative "connection_pool/wrapper"
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.2.4
4
+ version: 2.3.0
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: 2021-04-12 00:00:00.000000000 Z
12
+ date: 2022-09-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -61,21 +61,14 @@ executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
- - ".github/workflows/ci.yml"
65
- - ".gitignore"
66
64
  - Changes.md
67
- - Gemfile
68
65
  - LICENSE
69
66
  - README.md
70
- - Rakefile
71
67
  - connection_pool.gemspec
72
68
  - lib/connection_pool.rb
73
69
  - lib/connection_pool/timed_stack.rb
74
70
  - lib/connection_pool/version.rb
75
71
  - lib/connection_pool/wrapper.rb
76
- - test/helper.rb
77
- - test/test_connection_pool.rb
78
- - test/test_connection_pool_timed_stack.rb
79
72
  homepage: https://github.com/mperham/connection_pool
80
73
  licenses:
81
74
  - MIT
@@ -88,18 +81,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
81
  requirements:
89
82
  - - ">="
90
83
  - !ruby/object:Gem::Version
91
- version: 2.2.0
84
+ version: 2.5.0
92
85
  required_rubygems_version: !ruby/object:Gem::Requirement
93
86
  requirements:
94
87
  - - ">="
95
88
  - !ruby/object:Gem::Version
96
89
  version: '0'
97
90
  requirements: []
98
- rubygems_version: 3.2.3
91
+ rubygems_version: 3.2.32
99
92
  signing_key:
100
93
  specification_version: 4
101
94
  summary: Generic connection pool for Ruby
102
- test_files:
103
- - test/helper.rb
104
- - test/test_connection_pool.rb
105
- - test/test_connection_pool_timed_stack.rb
95
+ test_files: []
@@ -1,21 +0,0 @@
1
- name: CI
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- test:
7
- runs-on: ubuntu-latest
8
- strategy:
9
- fail-fast: false
10
- matrix:
11
- ruby: ["2.4", "2.5", "2.6", "2.7", "3.0", "jruby", "truffleruby"]
12
- steps:
13
- - uses: actions/checkout@v2
14
- - uses: ruby/setup-ruby@v1
15
- with:
16
- ruby-version: ${{matrix.ruby}}
17
- bundler-cache: true
18
-
19
- - name: Run tests
20
- timeout-minutes: 5
21
- run: ${{matrix.env}} bundle exec rake
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- *.gem
2
- .bundle
3
- Gemfile.lock
4
- pkg/*
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec(development_group: :runtime)
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
3
- require "rake/testtask"
4
- Rake::TestTask.new
5
-
6
- task default: :test
data/test/helper.rb DELETED
@@ -1,8 +0,0 @@
1
- gem "minitest"
2
-
3
- require "minitest/pride"
4
- require "minitest/autorun"
5
-
6
- $VERBOSE = 1
7
-
8
- require_relative "../lib/connection_pool"
@@ -1,560 +0,0 @@
1
- require_relative "helper"
2
-
3
- class TestConnectionPool < Minitest::Test
4
- class NetworkConnection
5
- SLEEP_TIME = 0.1
6
-
7
- def initialize
8
- @x = 0
9
- end
10
-
11
- def do_something(*_args, increment: 1)
12
- @x += increment
13
- sleep SLEEP_TIME
14
- @x
15
- end
16
-
17
- def fast
18
- @x += 1
19
- end
20
-
21
- def do_something_with_block
22
- @x += yield
23
- sleep SLEEP_TIME
24
- @x
25
- end
26
-
27
- def respond_to?(method_id, *args)
28
- method_id == :do_magic || super(method_id, *args)
29
- end
30
- end
31
-
32
- class Recorder
33
- def initialize
34
- @calls = []
35
- end
36
-
37
- attr_reader :calls
38
-
39
- def do_work(label)
40
- @calls << label
41
- end
42
- end
43
-
44
- def use_pool(pool, size)
45
- Array.new(size) {
46
- Thread.new do
47
- pool.with { sleep }
48
- end
49
- }.each do |thread|
50
- Thread.pass until thread.status == "sleep"
51
- end
52
- end
53
-
54
- def kill_threads(threads)
55
- threads.each do |thread|
56
- thread.kill
57
- thread.join
58
- end
59
- end
60
-
61
- def test_basic_multithreaded_usage
62
- pool_size = 5
63
- pool = ConnectionPool.new(size: pool_size) { NetworkConnection.new }
64
-
65
- start = Time.new
66
-
67
- generations = 3
68
-
69
- result = Array.new(pool_size * generations) {
70
- Thread.new do
71
- pool.with do |net|
72
- net.do_something
73
- end
74
- end
75
- }.map(&:value)
76
-
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)
82
- end
83
-
84
- def test_timeout
85
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
86
- thread = Thread.new {
87
- pool.with do |net|
88
- net.do_something
89
- sleep 0.01
90
- end
91
- }
92
-
93
- Thread.pass while thread.status == "run"
94
-
95
- assert_raises Timeout::Error do
96
- pool.with { |net| net.do_something }
97
- end
98
-
99
- thread.join
100
-
101
- pool.with do |conn|
102
- refute_nil conn
103
- end
104
- end
105
-
106
- def test_with
107
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
108
-
109
- pool.with do
110
- Thread.new {
111
- assert_raises Timeout::Error do
112
- pool.checkout
113
- end
114
- }.join
115
- end
116
-
117
- assert Thread.new { pool.checkout }.join
118
- end
119
-
120
- def test_then
121
- pool = ConnectionPool.new { Object.new }
122
-
123
- assert_equal pool.method(:then), pool.method(:with)
124
- end
125
-
126
- def test_with_timeout
127
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
128
-
129
- assert_raises Timeout::Error do
130
- Timeout.timeout(0.01) do
131
- pool.with do |obj|
132
- assert_equal 0, pool.available
133
- sleep 0.015
134
- end
135
- end
136
- end
137
- assert_equal 1, pool.available
138
- end
139
-
140
- def test_invalid_size
141
- assert_raises ArgumentError, TypeError do
142
- ConnectionPool.new(timeout: 0, size: nil) { Object.new }
143
- end
144
- assert_raises ArgumentError, TypeError do
145
- ConnectionPool.new(timeout: 0, size: "") { Object.new }
146
- end
147
- end
148
-
149
- def test_handle_interrupt_ensures_checkin
150
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
151
- def pool.checkout(options)
152
- sleep 0.015
153
- super
154
- end
155
-
156
- did_something = false
157
-
158
- action = lambda do
159
- Timeout.timeout(0.01) do
160
- pool.with do |obj|
161
- did_something = true
162
- # Timeout::Error will be triggered by any non-trivial Ruby code
163
- # executed here since it couldn't be raised during checkout.
164
- # It looks like setting the local variable above does not trigger
165
- # the Timeout check in MRI 2.2.1.
166
- obj.tap { obj.hash }
167
- end
168
- end
169
- end
170
-
171
- if RUBY_ENGINE == "ruby"
172
- # These asserts rely on the Ruby implementation reaching `did_something =
173
- # true` before the interrupt is detected by the thread. Interrupt
174
- # detection timing is implementation-specific in practice, with JRuby,
175
- # Rubinius, and TruffleRuby all having different interrupt timings to MRI.
176
- # In fact they generally detect interrupts more quickly than MRI, so they
177
- # may not reach `did_something = true` before detecting the interrupt.
178
-
179
- assert_raises Timeout::Error, &action
180
-
181
- assert did_something
182
- else
183
- action.call
184
- end
185
-
186
- assert_equal 1, pool.available
187
- end
188
-
189
- def test_explicit_return
190
- pool = ConnectionPool.new(timeout: 0, size: 1) {
191
- mock = Minitest::Mock.new
192
- def mock.disconnect!
193
- raise "should not disconnect upon explicit return"
194
- end
195
- mock
196
- }
197
-
198
- pool.with do |conn|
199
- return true
200
- end
201
- end
202
-
203
- def test_with_timeout_override
204
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
205
-
206
- t = Thread.new {
207
- pool.with do |net|
208
- net.do_something
209
- sleep 0.01
210
- end
211
- }
212
-
213
- Thread.pass while t.status == "run"
214
-
215
- assert_raises Timeout::Error do
216
- pool.with { |net| net.do_something }
217
- end
218
-
219
- pool.with(timeout: 2 * NetworkConnection::SLEEP_TIME) do |conn|
220
- refute_nil conn
221
- end
222
- end
223
-
224
- def test_checkin
225
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
226
- conn = pool.checkout
227
-
228
- Thread.new {
229
- assert_raises Timeout::Error do
230
- pool.checkout
231
- end
232
- }.join
233
-
234
- pool.checkin
235
-
236
- assert_same conn, Thread.new { pool.checkout }.value
237
- end
238
-
239
- def test_returns_value
240
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
241
- assert_equal 1, pool.with { |o| 1 }
242
- end
243
-
244
- def test_checkin_never_checkout
245
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
246
-
247
- e = assert_raises(ConnectionPool::Error) { pool.checkin }
248
- assert_equal "no connections are checked out", e.message
249
- end
250
-
251
- def test_checkin_no_current_checkout
252
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
253
-
254
- pool.checkout
255
- pool.checkin
256
-
257
- assert_raises ConnectionPool::Error do
258
- pool.checkin
259
- end
260
- end
261
-
262
- def test_checkin_twice
263
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
264
-
265
- pool.checkout
266
- pool.checkout
267
-
268
- pool.checkin
269
-
270
- Thread.new {
271
- assert_raises Timeout::Error do
272
- pool.checkout
273
- end
274
- }.join
275
-
276
- pool.checkin
277
-
278
- assert Thread.new { pool.checkout }.join
279
- end
280
-
281
- def test_checkout
282
- pool = ConnectionPool.new(size: 1) { NetworkConnection.new }
283
-
284
- conn = pool.checkout
285
-
286
- assert_kind_of NetworkConnection, conn
287
-
288
- assert_same conn, pool.checkout
289
- end
290
-
291
- def test_checkout_multithread
292
- pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
293
- conn = pool.checkout
294
-
295
- t = Thread.new {
296
- pool.checkout
297
- }
298
-
299
- refute_same conn, t.value
300
- end
301
-
302
- def test_checkout_timeout
303
- pool = ConnectionPool.new(timeout: 0, size: 0) { Object.new }
304
-
305
- assert_raises Timeout::Error do
306
- pool.checkout
307
- end
308
- end
309
-
310
- def test_checkout_timeout_override
311
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
312
-
313
- thread = Thread.new {
314
- pool.with do |net|
315
- net.do_something
316
- sleep 0.01
317
- end
318
- }
319
-
320
- Thread.pass while thread.status == "run"
321
-
322
- assert_raises Timeout::Error do
323
- pool.checkout
324
- end
325
-
326
- assert pool.checkout timeout: 2 * NetworkConnection::SLEEP_TIME
327
- end
328
-
329
- def test_passthru
330
- pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
331
- assert_equal 1, pool.do_something
332
- assert_equal 2, pool.do_something
333
- assert_equal 5, pool.do_something_with_block { 3 }
334
- assert_equal 6, pool.with { |net| net.fast }
335
- assert_equal 8, pool.do_something(increment: 2)
336
- end
337
-
338
- def test_passthru_respond_to
339
- pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
340
- assert pool.respond_to?(:with)
341
- assert pool.respond_to?(:do_something)
342
- assert pool.respond_to?(:do_magic)
343
- refute pool.respond_to?(:do_lots_of_magic)
344
- end
345
-
346
- def test_return_value
347
- pool = ConnectionPool.new(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
348
- result = pool.with { |net|
349
- net.fast
350
- }
351
- assert_equal 1, result
352
- end
353
-
354
- def test_heavy_threading
355
- pool = ConnectionPool.new(timeout: 0.5, size: 3) { NetworkConnection.new }
356
-
357
- threads = Array.new(20) {
358
- Thread.new do
359
- pool.with do |net|
360
- sleep 0.01
361
- end
362
- end
363
- }
364
-
365
- threads.map { |thread| thread.join }
366
- end
367
-
368
- def test_reuses_objects_when_pool_not_saturated
369
- pool = ConnectionPool.new(size: 5) { NetworkConnection.new }
370
-
371
- ids = 10.times.map {
372
- pool.with { |c| c.object_id }
373
- }
374
-
375
- assert_equal 1, ids.uniq.size
376
- end
377
-
378
- def test_nested_checkout
379
- recorder = Recorder.new
380
- pool = ConnectionPool.new(size: 1) { recorder }
381
- pool.with do |r_outer|
382
- @other = Thread.new { |t|
383
- pool.with do |r_other|
384
- r_other.do_work("other")
385
- end
386
- }
387
-
388
- pool.with do |r_inner|
389
- r_inner.do_work("inner")
390
- end
391
-
392
- Thread.pass
393
-
394
- r_outer.do_work("outer")
395
- end
396
-
397
- @other.join
398
-
399
- assert_equal ["inner", "outer", "other"], recorder.calls
400
- end
401
-
402
- def test_shutdown_is_executed_for_all_connections
403
- recorders = []
404
-
405
- pool = ConnectionPool.new(size: 3) {
406
- Recorder.new.tap { |r| recorders << r }
407
- }
408
-
409
- threads = use_pool pool, 3
410
-
411
- pool.shutdown do |recorder|
412
- recorder.do_work("shutdown")
413
- end
414
-
415
- kill_threads(threads)
416
-
417
- assert_equal [["shutdown"]] * 3, recorders.map { |r| r.calls }
418
- end
419
-
420
- def test_raises_error_after_shutting_down
421
- pool = ConnectionPool.new(size: 1) { true }
422
-
423
- pool.shutdown {}
424
-
425
- assert_raises ConnectionPool::PoolShuttingDownError do
426
- pool.checkout
427
- end
428
- end
429
-
430
- def test_runs_shutdown_block_asynchronously_if_connection_was_in_use
431
- recorders = []
432
-
433
- pool = ConnectionPool.new(size: 3) {
434
- Recorder.new.tap { |r| recorders << r }
435
- }
436
-
437
- threads = use_pool pool, 2
438
-
439
- pool.checkout
440
-
441
- pool.shutdown do |recorder|
442
- recorder.do_work("shutdown")
443
- end
444
-
445
- kill_threads(threads)
446
-
447
- assert_equal [["shutdown"], ["shutdown"], []], recorders.map { |r| r.calls }
448
-
449
- pool.checkin
450
-
451
- assert_equal [["shutdown"], ["shutdown"], ["shutdown"]], recorders.map { |r| r.calls }
452
- end
453
-
454
- def test_raises_an_error_if_shutdown_is_called_without_a_block
455
- pool = ConnectionPool.new(size: 1) {}
456
-
457
- assert_raises ArgumentError do
458
- pool.shutdown
459
- end
460
- end
461
-
462
- def test_shutdown_is_executed_for_all_connections_in_wrapped_pool
463
- recorders = []
464
-
465
- wrapper = ConnectionPool::Wrapper.new(size: 3) {
466
- Recorder.new.tap { |r| recorders << r }
467
- }
468
-
469
- threads = use_pool wrapper, 3
470
-
471
- wrapper.pool_shutdown do |recorder|
472
- recorder.do_work("shutdown")
473
- end
474
-
475
- kill_threads(threads)
476
-
477
- assert_equal [["shutdown"]] * 3, recorders.map { |r| r.calls }
478
- end
479
-
480
- def test_wrapper_wrapped_pool
481
- wrapper = ConnectionPool::Wrapper.new { NetworkConnection.new }
482
- assert_equal ConnectionPool, wrapper.wrapped_pool.class
483
- end
484
-
485
- def test_wrapper_method_missing
486
- wrapper = ConnectionPool::Wrapper.new { NetworkConnection.new }
487
-
488
- assert_equal 1, wrapper.fast
489
- end
490
-
491
- def test_wrapper_respond_to_eh
492
- wrapper = ConnectionPool::Wrapper.new { NetworkConnection.new }
493
-
494
- assert_respond_to wrapper, :with
495
-
496
- assert_respond_to wrapper, :fast
497
- refute_respond_to wrapper, :"nonexistent method"
498
- end
499
-
500
- def test_wrapper_with
501
- wrapper = ConnectionPool::Wrapper.new(timeout: 0, size: 1) { Object.new }
502
-
503
- wrapper.with do
504
- Thread.new {
505
- assert_raises Timeout::Error do
506
- wrapper.with { flunk "connection checked out :(" }
507
- end
508
- }.join
509
- end
510
-
511
- assert Thread.new { wrapper.with {} }.join
512
- end
513
-
514
- class ConnWithEval
515
- def eval(arg)
516
- "eval'ed #{arg}"
517
- end
518
- end
519
-
520
- def test_wrapper_kernel_methods
521
- wrapper = ConnectionPool::Wrapper.new(timeout: 0, size: 1) { ConnWithEval.new }
522
-
523
- assert_equal "eval'ed 1", wrapper.eval(1)
524
- end
525
-
526
- def test_wrapper_with_connection_pool
527
- recorder = Recorder.new
528
- pool = ConnectionPool.new(size: 1) { recorder }
529
- wrapper = ConnectionPool::Wrapper.new(pool: pool)
530
-
531
- pool.with { |r| r.do_work("with") }
532
- wrapper.do_work("wrapped")
533
-
534
- assert_equal ["with", "wrapped"], recorder.calls
535
- end
536
-
537
- def test_stats_without_active_connection
538
- pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
539
-
540
- assert_equal(2, pool.size)
541
- assert_equal(2, pool.available)
542
- end
543
-
544
- def test_stats_with_active_connection
545
- pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
546
-
547
- pool.with do
548
- assert_equal(1, pool.available)
549
- end
550
- end
551
-
552
- def test_stats_with_string_size
553
- pool = ConnectionPool.new(size: "2") { NetworkConnection.new }
554
-
555
- pool.with do
556
- assert_equal(2, pool.size)
557
- assert_equal(1, pool.available)
558
- end
559
- end
560
- end
@@ -1,150 +0,0 @@
1
- require_relative "helper"
2
-
3
- class TestConnectionPoolTimedStack < Minitest::Test
4
- def setup
5
- @stack = ConnectionPool::TimedStack.new { Object.new }
6
- end
7
-
8
- def test_empty_eh
9
- stack = ConnectionPool::TimedStack.new(1) { Object.new }
10
-
11
- refute_empty stack
12
-
13
- popped = stack.pop
14
-
15
- assert_empty stack
16
-
17
- stack.push popped
18
-
19
- refute_empty stack
20
- end
21
-
22
- def test_length
23
- stack = ConnectionPool::TimedStack.new(1) { Object.new }
24
-
25
- assert_equal 1, stack.length
26
-
27
- popped = stack.pop
28
-
29
- assert_equal 0, stack.length
30
-
31
- stack.push popped
32
-
33
- assert_equal 1, stack.length
34
- end
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
-
55
- def test_pop
56
- object = Object.new
57
- @stack.push object
58
-
59
- popped = @stack.pop
60
-
61
- assert_same object, popped
62
- end
63
-
64
- def test_pop_empty
65
- e = assert_raises(ConnectionPool::TimeoutError) { @stack.pop timeout: 0 }
66
- assert_equal "Waited 0 sec", e.message
67
- end
68
-
69
- def test_pop_empty_2_0_compatibility
70
- e = assert_raises(Timeout::Error) { @stack.pop 0 }
71
- assert_equal "Waited 0 sec", e.message
72
- end
73
-
74
- def test_pop_full
75
- stack = ConnectionPool::TimedStack.new(1) { Object.new }
76
-
77
- popped = stack.pop
78
-
79
- refute_nil popped
80
- assert_empty stack
81
- end
82
-
83
- def test_pop_wait
84
- thread = Thread.start {
85
- @stack.pop
86
- }
87
-
88
- Thread.pass while thread.status == "run"
89
-
90
- object = Object.new
91
-
92
- @stack.push object
93
-
94
- assert_same object, thread.value
95
- end
96
-
97
- def test_pop_shutdown
98
- @stack.shutdown {}
99
-
100
- assert_raises ConnectionPool::PoolShuttingDownError do
101
- @stack.pop
102
- end
103
- end
104
-
105
- def test_pop_shutdown_reload
106
- stack = ConnectionPool::TimedStack.new(1) { Object.new }
107
- object = stack.pop
108
- stack.push(object)
109
-
110
- stack.shutdown(reload: true) {}
111
-
112
- refute_equal object, stack.pop
113
- end
114
-
115
- def test_push
116
- stack = ConnectionPool::TimedStack.new(1) { Object.new }
117
-
118
- conn = stack.pop
119
-
120
- stack.push conn
121
-
122
- refute_empty stack
123
- end
124
-
125
- def test_push_shutdown
126
- called = []
127
-
128
- @stack.shutdown do |object|
129
- called << object
130
- end
131
-
132
- @stack.push Object.new
133
-
134
- refute_empty called
135
- assert_empty @stack
136
- end
137
-
138
- def test_shutdown
139
- @stack.push Object.new
140
-
141
- called = []
142
-
143
- @stack.shutdown do |object|
144
- called << object
145
- end
146
-
147
- refute_empty called
148
- assert_empty @stack
149
- end
150
- end