fiber_connection_pool 0.2.0 → 0.2.1

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: 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