connection_pool 2.1.3 → 2.2.0

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: 57159a2ea25da28e141dc80936a642134fbc9680
4
+ data.tar.gz: 7f534538909e40c117b84090a89b6f4fcd5ca8ee
5
5
  SHA512:
6
- metadata.gz: 53d34f8ca6a7d51ce535a83e2821dd33eb15a623df3022b89775c232876c8fec28a2b33adda24248136014d99f0ebe9f09c1f49e1f0ced76435db26538e907a7
7
- data.tar.gz: 660f22d0f828c789942da1da3d7c4131e008dd383a83c74e59ea03f306a59b966e879d42f9704ffa3b7de6accb6c114cfff982d3343bbe34c5e60ecc8f705ac5
6
+ metadata.gz: bb15e188af8037b01d59ab3728ee010033ab6c71cdd82ed6a8f4f59609c23791d9afdb8a9e31c755079cd1ce7c6e0ca20a7b43d2bd4b142b7ddacbd9b9ad94c3
7
+ data.tar.gz: 69e22e8e329f525fb875c2abe1090a35a63d035bb9337d9bd79604f46f1f1c3dbb92a83d6bf8d899388e8873cf04060c288bfef0e760e14b23a2fa12b1ffdf32
data/Changes.md CHANGED
@@ -1,3 +1,13 @@
1
+ connection\_pool changelog
2
+ ---------------------------
3
+
4
+ 2.2.0
5
+ ------
6
+
7
+ - Rollback `Timeout` handling introduced in 2.1.1 and 2.1.2. It seems
8
+ impossible to safely work around the issue. Please never, ever use
9
+ `Timeout.timeout` in your code or you will see rare but mysterious bugs. [#75]
10
+
1
11
  2.1.3
2
12
  ------
3
13
 
data/README.md CHANGED
@@ -8,6 +8,11 @@ MongoDB has its own connection pool. ActiveRecord has its own connection pool.
8
8
  This is a generic connection pool that can be used with anything, e.g. Redis,
9
9
  Dalli and other Ruby network clients.
10
10
 
11
+ **WARNING**: Don't ever use `Timeout.timeout` in your Ruby code or you will see
12
+ occasional silent corruption and mysterious errors. The Timeout API is unsafe
13
+ and cannot be used correctly, ever. Use proper socket timeout options as
14
+ exposed by Net::HTTP, Redis, Dalli, etc.
15
+
11
16
 
12
17
  Usage
13
18
  -----
@@ -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
  #
@@ -52,26 +53,36 @@ class ConnectionPool
52
53
  @key = :"current-#{@available.object_id}"
53
54
  end
54
55
 
56
+ if Thread.respond_to?(:handle_interrupt)
57
+
58
+ # MRI
59
+ def with(options = {})
60
+ Thread.handle_interrupt(Exception => :never) do
61
+ conn = checkout(options)
62
+ begin
63
+ Thread.handle_interrupt(Exception => :immediate) do
64
+ yield conn
65
+ end
66
+ ensure
67
+ checkin
68
+ end
69
+ end
70
+ end
71
+
72
+ else
73
+
74
+ # jruby 1.7.x
55
75
  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
76
  conn = checkout(options)
61
77
  begin
62
- result = yield conn
63
- success = true # means the connection wasn't interrupted
64
- result
78
+ yield conn
65
79
  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
80
+ checkin
72
81
  end
73
82
  end
74
83
 
84
+ end
85
+
75
86
  def checkout(options = {})
76
87
  conn = if stack.empty?
77
88
  timeout = options[:timeout] || @timeout
@@ -117,28 +117,6 @@ class ConnectionPool::TimedStack
117
117
  @max - @created + @que.length
118
118
  end
119
119
 
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
120
  private
143
121
 
144
122
  ##
@@ -1,3 +1,3 @@
1
1
  class ConnectionPool
2
- VERSION = "2.1.3"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -109,38 +109,58 @@ class TestConnectionPool < Minitest::Test
109
109
  assert Thread.new { pool.checkout }.join
110
110
  end
111
111
 
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")
112
+ def test_with_timeout
113
+ pool = ConnectionPool.new(:timeout => 0, :size => 1) { Object.new }
114
+
115
+ assert_raises Timeout::Error do
116
+ Timeout.timeout(0.01) do
117
+ pool.with do |obj|
118
+ assert_equal 0, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
119
+ sleep 0.015
120
+ end
119
121
  end
120
122
  end
123
+ assert_equal 1, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
124
+ end
121
125
 
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
126
+ def test_checkout_ignores_timeout
127
+ skip("Thread.handle_interrupt not available") unless Thread.respond_to?(:handle_interrupt)
127
128
 
128
- checkin_time = 0.05
129
+ pool = ConnectionPool.new(:timeout => 0, :size => 1) { Object.new }
130
+ def pool.checkout(options)
131
+ sleep 0.015
132
+ super
133
+ end
129
134
 
135
+ did_something = false
130
136
  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
137
+ Timeout.timeout(0.01) do
138
+ pool.with do |obj|
139
+ did_something = true
140
+ # Timeout::Error will be triggered by any non-trivial Ruby code
141
+ # executed here since it couldn't be raised during checkout.
142
+ # It looks like setting the local variable above does not trigger
143
+ # the Timeout check in MRI 2.2.1.
144
+ obj.tap { obj.hash }
136
145
  end
137
146
  end
138
147
  end
148
+ assert did_something
149
+ assert_equal 1, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
150
+ end
139
151
 
140
- GC.start
152
+ def test_explicit_return
153
+ pool = ConnectionPool.new(:timeout => 0, :size => 1) do
154
+ mock = Minitest::Mock.new
155
+ def mock.disconnect!
156
+ raise "should not disconnect upon explicit return"
157
+ end
158
+ mock
159
+ end
141
160
 
142
- # no dangling references to this "connection" remain
143
- assert_equal [], ObjectSpace.each_object(marker_class).to_a
161
+ pool.with do |conn|
162
+ return true
163
+ end
144
164
  end
145
165
 
146
166
  def test_with_timeout_override
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.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: 2015-03-19 00:00:00.000000000 Z
12
+ date: 2015-04-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler