connection_pool 2.2.2 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: connection_pool
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Perham
8
8
  - Damian Janowski
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-05-24 00:00:00.000000000 Z
12
+ date: 2023-03-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -61,26 +61,19 @@ executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
- - ".gitignore"
65
- - ".travis.yml"
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
- - lib/connection_pool/monotonic_time.rb
74
69
  - lib/connection_pool/timed_stack.rb
75
70
  - lib/connection_pool/version.rb
76
- - test/helper.rb
77
- - test/test_connection_pool.rb
78
- - test/test_connection_pool_timed_stack.rb
71
+ - lib/connection_pool/wrapper.rb
79
72
  homepage: https://github.com/mperham/connection_pool
80
73
  licenses:
81
74
  - MIT
82
75
  metadata: {}
83
- post_install_message:
76
+ post_install_message:
84
77
  rdoc_options: []
85
78
  require_paths:
86
79
  - lib
@@ -88,19 +81,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
81
  requirements:
89
82
  - - ">="
90
83
  - !ruby/object:Gem::Version
91
- version: '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
- rubyforge_project:
99
- rubygems_version: 2.6.13
100
- signing_key:
91
+ rubygems_version: 3.4.7
92
+ signing_key:
101
93
  specification_version: 4
102
94
  summary: Generic connection pool for Ruby
103
- test_files:
104
- - test/helper.rb
105
- - test/test_connection_pool.rb
106
- - test/test_connection_pool_timed_stack.rb
95
+ test_files: []
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- *.gem
2
- .bundle
3
- Gemfile.lock
4
- pkg/*
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- ---
2
- sudo: false
3
- cache: bundler
4
- language: ruby
5
- rvm:
6
- - 2.2.9
7
- - 2.3.6
8
- - 2.4.3
9
- - 2.5.0
10
- - jruby
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
@@ -1,66 +0,0 @@
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
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,516 +0,0 @@
1
- require_relative 'helper'
2
-
3
- class TestConnectionPool < Minitest::Test
4
-
5
- class NetworkConnection
6
- SLEEP_TIME = 0.1
7
-
8
- def initialize
9
- @x = 0
10
- end
11
-
12
- def do_something
13
- @x += 1
14
- sleep SLEEP_TIME
15
- @x
16
- end
17
-
18
- def fast
19
- @x += 1
20
- end
21
-
22
- def do_something_with_block
23
- @x += yield
24
- sleep SLEEP_TIME
25
- @x
26
- end
27
-
28
- def respond_to?(method_id, *args)
29
- method_id == :do_magic || super(method_id, *args)
30
- end
31
- end
32
-
33
- class Recorder
34
- def initialize
35
- @calls = []
36
- end
37
-
38
- attr_reader :calls
39
-
40
- def do_work(label)
41
- @calls << label
42
- end
43
- end
44
-
45
- def use_pool(pool, size)
46
- Array.new(size) do
47
- Thread.new do
48
- pool.with do sleep end
49
- end
50
- end.each do |thread|
51
- Thread.pass until thread.status == 'sleep'
52
- end
53
- end
54
-
55
- def kill_threads(threads)
56
- threads.each do |thread|
57
- thread.kill
58
- thread.join
59
- end
60
- end
61
-
62
- def test_basic_multithreaded_usage
63
- pool_size = 5
64
- pool = ConnectionPool.new(size: pool_size) { NetworkConnection.new }
65
-
66
- start = Time.new
67
-
68
- generations = 3
69
-
70
- result = Array.new(pool_size * generations) do
71
- Thread.new do
72
- pool.with do |net|
73
- net.do_something
74
- end
75
- end
76
- end.map(&:value)
77
-
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)
83
- end
84
-
85
- def test_timeout
86
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
87
- thread = Thread.new do
88
- pool.with do |net|
89
- net.do_something
90
- sleep 0.01
91
- end
92
- end
93
-
94
- Thread.pass while thread.status == 'run'
95
-
96
- assert_raises Timeout::Error do
97
- pool.with { |net| net.do_something }
98
- end
99
-
100
- thread.join
101
-
102
- pool.with do |conn|
103
- refute_nil conn
104
- end
105
- end
106
-
107
- def test_with
108
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
109
-
110
- pool.with do
111
- assert_raises Timeout::Error do
112
- Thread.new { pool.checkout }.join
113
- end
114
- end
115
-
116
- assert Thread.new { pool.checkout }.join
117
- end
118
-
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
128
- end
129
- end
130
- assert_equal 1, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
131
- end
132
-
133
- def test_checkout_ignores_timeout
134
- skip("Thread.handle_interrupt not available") unless Thread.respond_to?(:handle_interrupt)
135
-
136
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
137
- def pool.checkout(options)
138
- sleep 0.015
139
- super
140
- end
141
-
142
- did_something = false
143
- assert_raises Timeout::Error do
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 }
152
- end
153
- end
154
- end
155
- assert did_something
156
- assert_equal 1, pool.instance_variable_get(:@available).instance_variable_get(:@que).size
157
- end
158
-
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
167
-
168
- pool.with do |conn|
169
- return true
170
- end
171
- end
172
-
173
- def test_with_timeout_override
174
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
175
-
176
- t = Thread.new do
177
- pool.with do |net|
178
- net.do_something
179
- sleep 0.01
180
- end
181
- end
182
-
183
- Thread.pass while t.status == 'run'
184
-
185
- assert_raises Timeout::Error do
186
- pool.with { |net| net.do_something }
187
- end
188
-
189
- pool.with(timeout: 2 * NetworkConnection::SLEEP_TIME) do |conn|
190
- refute_nil conn
191
- end
192
- end
193
-
194
- def test_checkin
195
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
196
- conn = pool.checkout
197
-
198
- assert_raises Timeout::Error do
199
- Thread.new { pool.checkout }.join
200
- end
201
-
202
- pool.checkin
203
-
204
- assert_same conn, Thread.new { pool.checkout }.value
205
- end
206
-
207
- def test_returns_value
208
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
209
- assert_equal 1, pool.with {|o| 1 }
210
- end
211
-
212
- def test_checkin_never_checkout
213
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
214
-
215
- e = assert_raises ConnectionPool::Error do
216
- pool.checkin
217
- end
218
-
219
- assert_equal 'no connections are checked out', e.message
220
- end
221
-
222
- def test_checkin_no_current_checkout
223
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
224
-
225
- pool.checkout
226
- pool.checkin
227
-
228
- assert_raises ConnectionPool::Error do
229
- pool.checkin
230
- end
231
- end
232
-
233
- def test_checkin_twice
234
- pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new }
235
-
236
- pool.checkout
237
- pool.checkout
238
-
239
- pool.checkin
240
-
241
- assert_raises Timeout::Error do
242
- Thread.new do
243
- pool.checkout
244
- end.join
245
- end
246
-
247
- pool.checkin
248
-
249
- assert Thread.new { pool.checkout }.join
250
- end
251
-
252
- def test_checkout
253
- pool = ConnectionPool.new(size: 1) { NetworkConnection.new }
254
-
255
- conn = pool.checkout
256
-
257
- assert_kind_of NetworkConnection, conn
258
-
259
- assert_same conn, pool.checkout
260
- end
261
-
262
- def test_checkout_multithread
263
- pool = ConnectionPool.new(size: 2) { NetworkConnection.new }
264
- conn = pool.checkout
265
-
266
- t = Thread.new do
267
- pool.checkout
268
- end
269
-
270
- refute_same conn, t.value
271
- end
272
-
273
- def test_checkout_timeout
274
- pool = ConnectionPool.new(timeout: 0, size: 0) { Object.new }
275
-
276
- assert_raises Timeout::Error do
277
- pool.checkout
278
- end
279
- end
280
-
281
- def test_checkout_timeout_override
282
- pool = ConnectionPool.new(timeout: 0, size: 1) { NetworkConnection.new }
283
-
284
- thread = Thread.new do
285
- pool.with do |net|
286
- net.do_something
287
- sleep 0.01
288
- end
289
- end
290
-
291
- Thread.pass while thread.status == 'run'
292
-
293
- assert_raises Timeout::Error do
294
- pool.checkout
295
- end
296
-
297
- assert pool.checkout timeout: 2 * NetworkConnection::SLEEP_TIME
298
- end
299
-
300
- def test_passthru
301
- pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
302
- assert_equal 1, pool.do_something
303
- assert_equal 2, pool.do_something
304
- assert_equal 5, pool.do_something_with_block { 3 }
305
- assert_equal 6, pool.with { |net| net.fast }
306
- end
307
-
308
- def test_passthru_respond_to
309
- pool = ConnectionPool.wrap(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
310
- assert pool.respond_to?(:with)
311
- assert pool.respond_to?(:do_something)
312
- assert pool.respond_to?(:do_magic)
313
- refute pool.respond_to?(:do_lots_of_magic)
314
- end
315
-
316
- def test_return_value
317
- pool = ConnectionPool.new(timeout: 2 * NetworkConnection::SLEEP_TIME, size: 1) { NetworkConnection.new }
318
- result = pool.with do |net|
319
- net.fast
320
- end
321
- assert_equal 1, result
322
- end
323
-
324
- def test_heavy_threading
325
- pool = ConnectionPool.new(timeout: 0.5, size: 3) { NetworkConnection.new }
326
-
327
- threads = Array.new(20) do
328
- Thread.new do
329
- pool.with do |net|
330
- sleep 0.01
331
- end
332
- end
333
- end
334
-
335
- threads.map { |thread| thread.join }
336
- end
337
-
338
- def test_reuses_objects_when_pool_not_saturated
339
- pool = ConnectionPool.new(size: 5) { NetworkConnection.new }
340
-
341
- ids = 10.times.map do
342
- pool.with { |c| c.object_id }
343
- end
344
-
345
- assert_equal 1, ids.uniq.size
346
- end
347
-
348
- def test_nested_checkout
349
- recorder = Recorder.new
350
- pool = ConnectionPool.new(size: 1) { recorder }
351
- pool.with do |r_outer|
352
- @other = Thread.new do |t|
353
- pool.with do |r_other|
354
- r_other.do_work('other')
355
- end
356
- end
357
-
358
- pool.with do |r_inner|
359
- r_inner.do_work('inner')
360
- end
361
-
362
- Thread.pass
363
-
364
- r_outer.do_work('outer')
365
- end
366
-
367
- @other.join
368
-
369
- assert_equal ['inner', 'outer', 'other'], recorder.calls
370
- end
371
-
372
- def test_shutdown_is_executed_for_all_connections
373
- recorders = []
374
-
375
- pool = ConnectionPool.new(size: 3) do
376
- Recorder.new.tap { |r| recorders << r }
377
- end
378
-
379
- threads = use_pool pool, 3
380
-
381
- pool.shutdown do |recorder|
382
- recorder.do_work("shutdown")
383
- end
384
-
385
- kill_threads(threads)
386
-
387
- assert_equal [["shutdown"]] * 3, recorders.map { |r| r.calls }
388
- end
389
-
390
- def test_raises_error_after_shutting_down
391
- pool = ConnectionPool.new(size: 1) { true }
392
-
393
- pool.shutdown { }
394
-
395
- assert_raises ConnectionPool::PoolShuttingDownError do
396
- pool.checkout
397
- end
398
- end
399
-
400
- def test_runs_shutdown_block_asynchronously_if_connection_was_in_use
401
- recorders = []
402
-
403
- pool = ConnectionPool.new(size: 3) do
404
- Recorder.new.tap { |r| recorders << r }
405
- end
406
-
407
- threads = use_pool pool, 2
408
-
409
- pool.checkout
410
-
411
- pool.shutdown do |recorder|
412
- recorder.do_work("shutdown")
413
- end
414
-
415
- kill_threads(threads)
416
-
417
- assert_equal [["shutdown"], ["shutdown"], []], recorders.map { |r| r.calls }
418
-
419
- pool.checkin
420
-
421
- assert_equal [["shutdown"], ["shutdown"], ["shutdown"]], recorders.map { |r| r.calls }
422
- end
423
-
424
- def test_raises_an_error_if_shutdown_is_called_without_a_block
425
- pool = ConnectionPool.new(size: 1) { }
426
-
427
- assert_raises ArgumentError do
428
- pool.shutdown
429
- end
430
- end
431
-
432
- def test_shutdown_is_executed_for_all_connections_in_wrapped_pool
433
- recorders = []
434
-
435
- wrapper = ConnectionPool::Wrapper.new(size: 3) do
436
- Recorder.new.tap { |r| recorders << r }
437
- end
438
-
439
- threads = use_pool wrapper, 3
440
-
441
- wrapper.pool_shutdown do |recorder|
442
- recorder.do_work("shutdown")
443
- end
444
-
445
- kill_threads(threads)
446
-
447
- assert_equal [["shutdown"]] * 3, recorders.map { |r| r.calls }
448
- end
449
-
450
- def test_wrapper_method_missing
451
- wrapper = ConnectionPool::Wrapper.new { NetworkConnection.new }
452
-
453
- assert_equal 1, wrapper.fast
454
- end
455
-
456
- def test_wrapper_respond_to_eh
457
- wrapper = ConnectionPool::Wrapper.new { NetworkConnection.new }
458
-
459
- assert_respond_to wrapper, :with
460
-
461
- assert_respond_to wrapper, :fast
462
- refute_respond_to wrapper, :"nonexistent method"
463
- end
464
-
465
- def test_wrapper_with
466
- wrapper = ConnectionPool::Wrapper.new(timeout: 0, size: 1) { Object.new }
467
-
468
- wrapper.with do
469
- assert_raises Timeout::Error do
470
- Thread.new do
471
- wrapper.with { flunk 'connection checked out :(' }
472
- end.join
473
- end
474
- end
475
-
476
- assert Thread.new { wrapper.with { } }.join
477
- end
478
-
479
- class ConnWithEval
480
- def eval(arg)
481
- "eval'ed #{arg}"
482
- end
483
- end
484
-
485
- def test_wrapper_kernel_methods
486
- wrapper = ConnectionPool::Wrapper.new(timeout: 0, size: 1) { ConnWithEval.new }
487
-
488
- assert_equal "eval'ed 1", wrapper.eval(1)
489
- end
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
516
- end