fiber_connection_pool 0.2.0 → 0.2.1

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: 3805c4817fd7ddab4eaafcdf30bacdd99dd79646
4
- data.tar.gz: 2af6d4c85fd8d672b7d8c0b9eaea6a02a76e3658
3
+ metadata.gz: 9fdb88ab21985a6014912366f1a917da8d0df76c
4
+ data.tar.gz: cc131028a049c18efc0029e63e83966b24cc10b0
5
5
  SHA512:
6
- metadata.gz: 65a29c9021d57a6f7de8390aa28cf56a5d330ed7b1259dd760b6b4dd5e5be67d8a7e5b1d0c127fb62d7b7c0d252746866269e4fd2a134cf2ab92e8dcba5a9b8c
7
- data.tar.gz: ae6b1518828313527b91be4005e6cdc510f364158ed7626df6dff083feee43166006267baba0aae70f519df54bc20ee93bdf0b94e0fd97072bacf3fd6b4e823b
6
+ metadata.gz: 84abe69a34155bdf1307987d93a4b3c55cd0483be1d55ca0b50fadd8faf42f2582c46a1cada282beb9d245138c71829b00f3e86b8721a553ac75f6c8d1a9e414
7
+ data.tar.gz: d7f20e3e765f1e334405b0c333c86d63302804318856d2928823f9e10a00dc3015abfb826ccc773e39ab089621d958601301596564d062cfb7162cab7cc9bd3e
data/README.md CHANGED
@@ -3,6 +3,7 @@ fiber_connection_pool
3
3
 
4
4
  [![Build Status](https://secure.travis-ci.org/rubencaro/fiber_connection_pool.png?branch=master)](http://travis-ci.org/rubencaro/fiber_connection_pool)
5
5
  [![Gem Version](https://badge.fury.io/rb/fiber_connection_pool.png)](http://rubygems.org/gems/fiber_connection_pool)
6
+ [![Code Climate](https://codeclimate.com/github/rubencaro/fiber_connection_pool.png)](https://codeclimate.com/github/rubencaro/fiber_connection_pool)
6
7
 
7
8
  Fiber-based generic connection pool
8
9
 
@@ -2,12 +2,12 @@ require 'fiber'
2
2
  require_relative 'fiber_connection_pool/exceptions'
3
3
 
4
4
  class FiberConnectionPool
5
- VERSION = '0.2.0'
5
+ VERSION = '0.2.1'
6
6
 
7
- RESERVED_BACKUP_TTL_SECS = 30 # reserved backup cleanup trigger
7
+ RESERVED_TTL_SECS = 30 # reserved cleanup trigger
8
8
  SAVED_DATA_TTL_SECS = 30 # saved_data cleanup trigger
9
9
 
10
- attr_accessor :saved_data, :reserved_backup
10
+ attr_accessor :saved_data, :treated_exceptions
11
11
 
12
12
  # Initializes the pool with 'size' instances
13
13
  # running the given block to get each one. Ex:
@@ -19,8 +19,8 @@ class FiberConnectionPool
19
19
 
20
20
  @saved_data = {} # placeholder for requested save data
21
21
  @reserved = {} # map of in-progress connections
22
- @reserved_backup = {} # backup map of in-progress connections, to catch failures
23
- @last_backup_cleanup = Time.now # reserved backup cleanup trigger
22
+ @treated_exceptions = [ PlaceholderException ] # list of Exception classes that need further connection treatment
23
+ @last_reserved_cleanup = Time.now # reserved cleanup trigger
24
24
  @available = [] # pool of free connections
25
25
  @pending = [] # pending reservations (FIFO)
26
26
  @save_data_requests = {} # blocks to be yielded to save data
@@ -119,14 +119,12 @@ class FiberConnectionPool
119
119
  # Identify the connection that just failed for current fiber.
120
120
  # Pass it to the given block, which must return a valid instance of connection.
121
121
  # After that, put the new connection into the pool in failed connection's place.
122
- # Raises NoBackupConnection if cannot find the failed connection instance.
122
+ # Raises NoReservedConnection if cannot find the failed connection instance.
123
123
  #
124
124
  def with_failed_connection
125
- f = Fiber.current
126
- bad_conn = @reserved_backup[f]
127
- raise NoBackupConnection.new if bad_conn.nil?
125
+ bad_conn = @reserved[Fiber.current]
126
+ raise NoReservedConnection.new if bad_conn.nil?
128
127
  new_conn = yield bad_conn
129
- release_backup f
130
128
  @available.reject!{ |v| v == bad_conn }
131
129
  @reserved.reject!{ |k,v| v == bad_conn }
132
130
  @available.push new_conn
@@ -137,13 +135,13 @@ class FiberConnectionPool
137
135
  end
138
136
  end
139
137
 
140
- # Delete any backups held for dead fibers
138
+ # Delete any reserved held for dead fibers
141
139
  #
142
- def backup_cleanup
143
- @reserved_backup.dup.each do |k,v|
144
- @reserved_backup.delete(k) if not k.alive?
140
+ def reserved_cleanup
141
+ @last_reserved_cleanup = Time.now
142
+ @reserved.dup.each do |k,v|
143
+ release(k) if not k.alive?
145
144
  end
146
- @last_backup_cleanup = Time.now
147
145
  end
148
146
 
149
147
  private
@@ -164,12 +162,18 @@ class FiberConnectionPool
164
162
  # save anything requested
165
163
  process_save_data(f, conn, method)
166
164
 
167
- # successful run, release_backup
168
- release_backup(f)
165
+ # successful run, release
166
+ release(f)
169
167
 
170
168
  retval
171
- ensure
169
+ rescue *treated_exceptions => ex
170
+ # do not release connection for these
171
+ # maybe prepare something here to be used on connection repair
172
+ raise ex
173
+ rescue Exception => ex
174
+ # not successful run, but not meant to be treated
172
175
  release(f)
176
+ raise ex
173
177
  end
174
178
  end
175
179
 
@@ -191,8 +195,7 @@ class FiberConnectionPool
191
195
  #
192
196
  def acquire(fiber)
193
197
  if conn = @available.pop
194
- @reserved[fiber.object_id] = conn
195
- @reserved_backup[fiber] = conn
198
+ @reserved[fiber] = conn
196
199
  conn
197
200
  else
198
201
  Fiber.yield @pending.push fiber
@@ -200,21 +203,16 @@ class FiberConnectionPool
200
203
  end
201
204
  end
202
205
 
203
- # Release connection from the backup hash
204
- # Also perform cleanup if TTL is past
205
- #
206
- def release_backup(fiber)
207
- @reserved_backup.delete(fiber)
208
- # try cleanup
209
- backup_cleanup if (Time.now - @last_backup_cleanup) >= RESERVED_BACKUP_TTL_SECS
210
- end
211
-
212
206
  # Release connection assigned to the supplied fiber and
213
207
  # resume any other pending connections (which will
214
208
  # immediately try to run acquire on the pool)
209
+ # Also perform cleanup if TTL is past
215
210
  #
216
211
  def release(fiber)
217
- @available.push(@reserved.delete(fiber.object_id)).compact!
212
+ @available.push @reserved.delete(fiber)
213
+
214
+ # try cleanup
215
+ reserved_cleanup if (Time.now - @last_reserved_cleanup) >= RESERVED_TTL_SECS
218
216
 
219
217
  if pending = @pending.shift
220
218
  pending.resume
@@ -1,6 +1,8 @@
1
1
 
2
- class NoBackupConnection < Exception
2
+ class NoReservedConnection < StandardError
3
3
  def initialize
4
- super "No backup connection for this fiber!"
4
+ super "No reserved connection for this fiber!"
5
5
  end
6
6
  end
7
+
8
+ class PlaceholderException < StandardError; end
@@ -1,11 +1,13 @@
1
1
  require 'helper'
2
+ require 'set'
3
+ require 'yaml'
2
4
 
3
5
  class TestFiberConnectionPool < Minitest::Test
4
6
 
5
7
  def test_blocking_behaviour
6
8
  # get pool and fibers
7
9
  pool = FiberConnectionPool.new(:size => 5) { ::BlockingConnection.new(:delay => 0.05) }
8
- info = { :threads => [], :fibers => [], :instances => []}
10
+ info = { :threads => [].to_set, :fibers => [].to_set, :instances => [].to_set}
9
11
 
10
12
  fibers = Array.new(15){ Fiber.new { pool.do_something(info) } }
11
13
 
@@ -21,14 +23,13 @@ class TestFiberConnectionPool < Minitest::Test
21
23
  # because as we are -blocking- it's always available
22
24
  # again for the next request
23
25
  # we should have visited 1 thread, 15 fibers and 1 instances
24
- info.dup.each{ |k,v| info[k] = v.uniq }
25
26
  assert_equal 1, info[:threads].count
26
27
  assert_equal 15, info[:fibers].count
27
28
  assert_equal 1, info[:instances].count
28
29
  end
29
30
 
30
31
  def test_em_synchrony_behaviour
31
- info = { :threads => [], :fibers => [], :instances => []}
32
+ info = { :threads => [].to_set, :fibers => [].to_set, :instances => [].to_set}
32
33
 
33
34
  # get pool and fibers
34
35
  pool = FiberConnectionPool.new(:size => 5) { ::EMSynchronyConnection.new(:delay => 0.05) }
@@ -44,7 +45,6 @@ class TestFiberConnectionPool < Minitest::Test
44
45
  assert_operator(lapse, :<, 0.20)
45
46
 
46
47
  # we should have visited 1 thread, 15 fibers and 5 instances
47
- info.dup.each{ |k,v| info[k] = v.uniq }
48
48
  assert_equal 1, info[:threads].count
49
49
  assert_equal 15, info[:fibers].count
50
50
  assert_equal 5, info[:instances].count
@@ -68,19 +68,22 @@ class TestFiberConnectionPool < Minitest::Test
68
68
  end
69
69
 
70
70
  def test_failure_reaction
71
- info = { :instances => [] }
71
+ info = { :instances => [].to_set, :repaired_connections => [].to_set, :failing_connections => [].to_set }
72
72
 
73
73
  # get pool and fibers
74
74
  pool = FiberConnectionPool.new(:size => 5) { ::EMSynchronyConnection.new(:delay => 0.05) }
75
75
 
76
76
  fibers = Array.new(14){ Fiber.new { pool.do_something(info) } }
77
77
 
78
+ # state which exceptions should be treated
79
+ pool.treated_exceptions = [ FakeException ]
80
+
78
81
  failing_fiber = Fiber.new do
79
82
  begin
80
83
  pool.fail(info)
81
84
  rescue
82
85
  pool.with_failed_connection do |connection|
83
- info[:repaired_connection] = connection
86
+ info[:repaired_connections] << connection
84
87
  # replace it in the pool
85
88
  ::EMSynchronyConnection.new(:delay => 0.05)
86
89
  end
@@ -88,62 +91,78 @@ class TestFiberConnectionPool < Minitest::Test
88
91
  end
89
92
  # put it among others, not the first or the last
90
93
  # so we see it does not mistake the failing connection
91
- fibers.insert 7,failing_fiber
94
+ fibers.insert 2,failing_fiber
95
+
96
+ failing_fiber_not_treated = Fiber.new do
97
+ begin
98
+ pool.fail_not_treated(info)
99
+ rescue
100
+ # not meant to be treated
101
+ assert_raises NoReservedConnection do
102
+ pool.with_failed_connection{ |c| 'boo' }
103
+ end
104
+ end
105
+ end
106
+ # put it among others, not the first or the last
107
+ # so we see it does not mistake the failing connection
108
+ fibers.insert 7,failing_fiber_not_treated
92
109
 
93
110
  run_em_reactor fibers
94
111
 
95
- # we should have visited 1 thread, 15 fibers and 6 instances (including failed)
96
- info.dup.each{ |k,v| info[k] = v.uniq if v.is_a?(Array) }
112
+
113
+ # we should have visited 1 thread, 15 fibers and 6 instances (including repaired)
97
114
  assert_equal 6, info[:instances].count
98
115
 
99
- # assert we do not lose track of failing connection
100
- assert_equal info[:repaired_connection], info[:failing_connection]
116
+ # assert we do not lose track of failing connections, but only repair those that needed it
117
+ assert_equal 2, info[:failing_connections].count
118
+ assert_equal 1, info[:repaired_connections].count
119
+ assert info[:failing_connections].include?(info[:repaired_connections].first)
101
120
 
102
121
  # assert we replaced it
103
- refute pool.has_connection?(info[:failing_connection])
122
+ refute pool.has_connection?(info[:repaired_connections].first)
104
123
 
105
124
  # nothing left
106
- assert_equal(0, pool.reserved_backup.count)
125
+ assert_equal(0, pool.instance_variable_get(:@reserved).count)
107
126
 
108
127
  # if dealing with failed connection where you shouldn't...
109
- assert_raises NoBackupConnection do
128
+ assert_raises NoReservedConnection do
110
129
  pool.with_failed_connection{ |c| 'boo' }
111
130
  end
112
131
  end
113
132
 
114
- def test_reserved_backups
133
+ def test_reserved
115
134
  # create pool, run fibers and gather info
116
- pool, info = run_reserved_backups
135
+ pool, info = run_reserved
117
136
 
118
137
  # one left
119
- assert_equal(1, pool.reserved_backup.count)
138
+ assert_equal(1, pool.instance_variable_get(:@reserved).count)
120
139
 
121
140
  # fire cleanup
122
- pool.backup_cleanup
141
+ pool.reserved_cleanup
123
142
 
124
143
  # nothing left
125
- assert_equal(0, pool.reserved_backup.count)
144
+ assert_equal(0, pool.instance_variable_get(:@reserved).count)
126
145
 
127
146
  # assert we did not replace it
128
- assert pool.has_connection?(info[:failing_connection])
147
+ assert info[:failing_connections].all?{ |c| pool.has_connection?(c) }
129
148
  end
130
149
 
131
- def test_auto_cleanup_reserved_backups
150
+ def test_auto_cleanup_reserved
132
151
  # lower ttl to force auto cleanup
133
- prev_ttl = force_constant FiberConnectionPool, :RESERVED_BACKUP_TTL_SECS, 0
152
+ prev_ttl = force_constant FiberConnectionPool, :RESERVED_TTL_SECS, 0
134
153
 
135
154
  # create pool, run fibers and gather info
136
- pool, info = run_reserved_backups
155
+ pool, info = run_reserved
137
156
 
138
157
  # nothing left, because failing fiber was not the last to run
139
158
  # the following fiber made the cleanup
140
- assert_equal(0, pool.reserved_backup.count)
159
+ assert_equal(0, pool.instance_variable_get(:@reserved).count)
141
160
 
142
161
  # assert we did not replace it
143
- assert pool.has_connection?(info[:failing_connection])
162
+ assert info[:failing_connections].all?{ |c| pool.has_connection?(c) }
144
163
  ensure
145
164
  # restore
146
- force_constant FiberConnectionPool, :RESERVED_BACKUP_TTL_SECS, prev_ttl
165
+ force_constant FiberConnectionPool, :RESERVED_TTL_SECS, prev_ttl
147
166
  end
148
167
 
149
168
  def test_save_data
@@ -183,32 +202,41 @@ class TestFiberConnectionPool < Minitest::Test
183
202
 
184
203
  private
185
204
 
186
- def run_reserved_backups
187
- info = { :instances => [] }
205
+ def run_reserved
206
+ info = { :instances => [].to_set, :failing_connections => [].to_set }
188
207
 
189
208
  # get pool and fibers
190
209
  pool = FiberConnectionPool.new(:size => 2) { ::EMSynchronyConnection.new(:delay => 0.05) }
191
210
 
192
211
  fibers = Array.new(4){ Fiber.new { pool.do_something(info) } }
193
212
 
194
- # we do not repair it, backup associated with this Fiber stays in the pool
213
+ # state which exceptions should be treated
214
+ pool.treated_exceptions = [ FakeException ]
215
+
216
+ # we do not repair it, stays reserved
195
217
  failing_fiber = Fiber.new { pool.fail(info) rescue nil }
196
218
 
197
219
  # put it among others, not the first or the last
198
220
  # so we see it does not mistake the failing connection
199
221
  fibers.insert 2,failing_fiber
200
222
 
223
+ # we do not repair it, but we did not mean to, it's released again
224
+ failing_fiber_not_treated = Fiber.new { pool.fail_not_treated(info) rescue nil }
225
+
226
+ # put it among others, not the first or the last
227
+ # so we see it does not mistake the failing connection
228
+ fibers.insert 1,failing_fiber_not_treated
229
+
201
230
  run_em_reactor fibers
202
231
 
203
- # we should have visited only 2 instances (no instance added by repairing broken one)
204
- info.dup.each{ |k,v| info[k] = v.uniq if v.is_a?(Array) }
232
+ # we should have visited only 2 instances (nothing was repaired this time)
205
233
  assert_equal 2, info[:instances].count
206
234
 
207
235
  [ pool, info ]
208
236
  end
209
237
 
210
238
  def run_saved_data
211
- info = { :instances => [] }
239
+ info = { :instances => [].to_set }
212
240
 
213
241
  # get pool and fibers
214
242
  pool = FiberConnectionPool.new(:size => 2) { ::EMSynchronyConnection.new(:delay => 0.05) }
@@ -227,7 +255,6 @@ class TestFiberConnectionPool < Minitest::Test
227
255
  run_em_reactor fibers
228
256
 
229
257
  # we should have visited 2 instances
230
- info.dup.each{ |k,v| info[k] = v.uniq if v.is_a?(Array) }
231
258
  assert_equal 2, info[:instances].count
232
259
 
233
260
  [ pool, fibers, info ]
data/test/helper.rb CHANGED
@@ -21,6 +21,8 @@ class BlockingConnection
21
21
  end
22
22
  end
23
23
 
24
+ class FakeException < StandardError; end
25
+
24
26
  class EMSynchronyConnection < BlockingConnection
25
27
  def do_something(info = {})
26
28
  fill_info info
@@ -29,7 +31,13 @@ class EMSynchronyConnection < BlockingConnection
29
31
 
30
32
  def fail(info)
31
33
  fill_info info
32
- info[:failing_connection] = self
34
+ info[:failing_connections] << self
35
+ raise FakeException.new "Fakingly failing here..."
36
+ end
37
+
38
+ def fail_not_treated(info)
39
+ fill_info info
40
+ info[:failing_connections] << self
33
41
  raise "Sadly failing here..."
34
42
  end
35
43
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fiber_connection_pool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruben Caro
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-20 00:00:00.000000000 Z
12
+ date: 2013-09-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest