diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a20768..4135bd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2", "3.3", "3.4", "jruby"] + ruby: ["3.2", "3.3", "3.4", "4.0", "jruby"] experimental: [false] include: - ruby: "truffleruby" diff --git a/.standard.yml b/.standard.yml index c45c691..b442cc2 100644 --- a/.standard.yml +++ b/.standard.yml @@ -1,3 +1,3 @@ -ruby_version: 2.5.0 +ruby_version: 3.2.0 fix: true parallel: true diff --git a/Changes.md b/Changes.md index d8c0e77..9e13adb 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,13 @@ # connection_pool Changelog +3.0.0 +------ + +- **BREAKING CHANGES** `ConnectionPool` and `ConnectionPool::TimedStack` now + use keyword arguments rather than positional arguments everywhere. + See README for upgrade notes. +- Dropped support for Ruby <3.2.0 + 2.5.5 ------ diff --git a/README.md b/README.md index 5917a9c..40d65f6 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,6 @@ This will only modify the resource-get timeout for this particular invocation. This is useful if you want to fail-fast on certain non-critical sections when a resource is not available, or conversely if you are comfortable blocking longer on a particular resource. -This is not implemented in the `ConnectionPool::Wrapper` class. ## Migrating to a Connection Pool @@ -103,27 +102,18 @@ Like `shutdown`, this will block until all connections are checked in and closed ## Reap -You can reap idle connections in the ConnectionPool instance to close connections that were created but have not been used for a certain amount of time. This can be useful to run periodically in a separate thread especially if keeping the connection open is resource intensive. +You can call `reap` periodically on the ConnectionPool instance to close connections that were created but have not been used for a certain amount of time. This can be useful in environments where connections are expensive. -You can specify how many seconds the connections have to be idle for them to be reaped. -Defaults to 60 seconds. +You can specify how many seconds the connections have to be idle for them to be reaped, defaulting to 60 seconds. ```ruby cp = ConnectionPool.new { Redis.new } -cp.reap(300) { |conn| conn.close } # Reaps connections that have been idle for 300 seconds (5 minutes). -``` - -### Reaper Thread -You can start your own reaper thread to reap idle connections in the ConnectionPool instance on a regular interval. - -```ruby -cp = ConnectionPool.new { Redis.new } - -# Start a reaper thread to reap connections that have been idle for 300 seconds (5 minutes). +# Start a reaper thread to reap connections that have been +# idle more than 300 seconds (5 minutes) Thread.new do loop do - cp.reap(300) { |conn| conn.close } + cp.reap(idle_seconds: 300) { |conn| conn.close } sleep 300 end end @@ -140,14 +130,14 @@ It can only be done inside the block passed to `with` or `with_timeout`. Takes an optional block that will be executed with the connection. ```ruby - pool.with do |conn| - begin - conn.execute("SELECT 1") - rescue SomeConnectionError - pool.discard_current_connection # remove the connection from the pool - raise - end - end +pool.with do |conn| + begin + conn.execute("SELECT 1") + rescue SomeConnectionError + pool.discard_current_connection # remove the connection from the pool + raise + end +end ``` ## Current State @@ -169,20 +159,28 @@ end cp.idle # => 1 ``` -Notes ------ +## Upgrading from ConnectionPool 2 + +* Support for Ruby <3.2 has been removed. +* ConnectionPool's APIs now consistently use keyword arguments everywhere. +Positional arguments must be converted to keywords: +```ruby +pool = ConnectionPool.new(size: 5, timeout: 5) +pool.checkout(1) # 2.x +pool.reap(30) # 2.x +pool.checkout(timeout: 1) # 3.x +pool.reap(idle_seconds: 30) # 3.x +``` + +## Notes - Connections are lazily created as needed. -- There is no provision for repairing or checking the health of a connection; - connections should be self-repairing. This is true of the Dalli and Redis - clients. -- **WARNING**: Don't ever use `Timeout.timeout` in your Ruby code or you will see +- **WARNING**: Avoid `Timeout.timeout` in your Ruby code or you can see occasional silent corruption and mysterious errors. The Timeout API is unsafe - and cannot be used correctly, ever. Use proper socket timeout options as - exposed by Net::HTTP, Redis, Dalli, etc. + and dangerous to use. Use proper socket timeout options as exposed by + Net::HTTP, Redis, Dalli, etc. -Author ------- +## Author -Mike Perham, [@getajobmike](https://twitter.com/getajobmike), +Mike Perham, [@getajobmike](https://ruby.social/@getajobmike), diff --git a/Rakefile b/Rakefile index 4d53139..5d0e09b 100644 --- a/Rakefile +++ b/Rakefile @@ -6,5 +6,5 @@ Rake::TestTask.new task default: [:"standard:fix", :test] task :bench do - require_relative "./test/benchmarks.rb" + require_relative "test/benchmarks" end diff --git a/connection_pool.gemspec b/connection_pool.gemspec index 32289f2..afeb9b3 100644 --- a/connection_pool.gemspec +++ b/connection_pool.gemspec @@ -15,10 +15,18 @@ Gem::Specification.new do |s| s.executables = [] s.require_paths = ["lib"] s.license = "MIT" + + s.required_ruby_version = ">= 3.2.0" s.add_development_dependency "bundler" - s.add_development_dependency "minitest", ">= 5.0.0" + s.add_development_dependency "maxitest" s.add_development_dependency "rake" - s.required_ruby_version = ">= 2.5.0" - s.metadata = {"changelog_uri" => "https://github.com/mperham/connection_pool/blob/main/Changes.md", "rubygems_mfa_required" => "true"} + s.metadata = { + "bug_tracker_uri" => "https://github.com/mperham/connection_pool/issues", + "documentation_uri" => "https://github.com/mperham/connection_pool/wiki", + "changelog_uri" => "https://github.com/mperham/connection_pool/blob/main/Changes.md", + "source_code_uri" => "https://github.com/mperham/connection_pool", + "homepage_uri" => "https://github.com/mperham/connection_pool", + "rubygems_mfa_required" => "true" + } end diff --git a/lib/connection_pool.rb b/lib/connection_pool.rb index b384f01..a219acd 100644 --- a/lib/connection_pool.rb +++ b/lib/connection_pool.rb @@ -39,10 +39,8 @@ class TimeoutError < ::Timeout::Error; end # - :auto_reload_after_fork - automatically drop all connections after fork, defaults to true # class ConnectionPool - DEFAULTS = {size: 5, timeout: 5, auto_reload_after_fork: true}.freeze - - def self.wrap(options, &block) - Wrapper.new(options, &block) + def self.wrap(**, &) + Wrapper.new(**, &) end if Process.respond_to?(:fork) @@ -66,19 +64,18 @@ def self.after_fork nil end - if ::Process.respond_to?(:_fork) # MRI 3.1+ - module ForkTracker - def _fork - pid = super - if pid == 0 - ConnectionPool.after_fork - end - pid + module ForkTracker + def _fork + pid = super + if pid == 0 + ConnectionPool.after_fork end + pid end - Process.singleton_class.prepend(ForkTracker) end + Process.singleton_class.prepend(ForkTracker) else + # JRuby, et al INSTANCES = nil private_constant :INSTANCES @@ -87,28 +84,26 @@ def self.after_fork end end - def initialize(options = {}, &block) - raise ArgumentError, "Connection pool requires a block" unless block - - options = DEFAULTS.merge(options) + def initialize(timeout: 5, size: 5, auto_reload_after_fork: true, &) + raise ArgumentError, "Connection pool requires a block" unless block_given? - @size = Integer(options.fetch(:size)) - @timeout = options.fetch(:timeout) - @auto_reload_after_fork = options.fetch(:auto_reload_after_fork) + @size = Integer(size) + @timeout = Float(timeout) + @auto_reload_after_fork = auto_reload_after_fork - @available = TimedStack.new(@size, &block) + @available = TimedStack.new(size: @size, &) @key = :"pool-#{@available.object_id}" @key_count = :"pool-#{@available.object_id}-count" @discard_key = :"pool-#{@available.object_id}-discard" INSTANCES[self] = self if @auto_reload_after_fork && INSTANCES end - def with(options = {}) + def with(**) # We need to manage exception handling manually here in order # to work correctly with `Timeout.timeout` and `Thread#raise`. # Otherwise an interrupted Thread can leak connections. Thread.handle_interrupt(Exception => :never) do - conn = checkout(options) + conn = checkout(**) begin Thread.handle_interrupt(Exception => :immediate) do yield conn @@ -154,13 +149,15 @@ def discard_current_connection(&block) ::Thread.current[@discard_key] = block || proc { |conn| conn } end - def checkout(options = {}) + def checkout(**options) if ::Thread.current[@key] ::Thread.current[@key_count] += 1 ::Thread.current[@key] else + conn = @available.pop(timeout: options.fetch(:timeout, @timeout), **options) + ::Thread.current[@key] = conn ::Thread.current[@key_count] = 1 - ::Thread.current[@key] = @available.pop(options[:timeout] || @timeout, options) + conn end end @@ -209,8 +206,8 @@ def reload(&block) ## Reaps idle connections that have been idle for over +idle_seconds+. # +idle_seconds+ defaults to 60. - def reap(idle_seconds = 60, &block) - @available.reap(idle_seconds, &block) + def reap(idle_seconds: 60, &block) + @available.reap(idle_seconds:, &block) end # Size of this connection pool diff --git a/lib/connection_pool/timed_stack.rb b/lib/connection_pool/timed_stack.rb index f02fba3..883f074 100644 --- a/lib/connection_pool/timed_stack.rb +++ b/lib/connection_pool/timed_stack.rb @@ -5,7 +5,7 @@ # # Examples: # -# ts = TimedStack.new(1) { MyConnection.new } +# ts = TimedStack.new(size: 1) { MyConnection.new } # # # fetch a connection # conn = ts.pop @@ -22,7 +22,7 @@ class ConnectionPool::TimedStack ## # Creates a new pool with +size+ connections that are created from the given # +block+. - def initialize(size = 0, &block) + def initialize(size: 0, &block) @create_block = block @created = 0 @que = [] @@ -33,15 +33,15 @@ def initialize(size = 0, &block) end ## - # Returns +obj+ to the stack. +options+ is ignored in TimedStack but may be + # Returns +obj+ to the stack. Additional kwargs are ignored in TimedStack but may be # used by subclasses that extend TimedStack. - def push(obj, options = {}) + def push(obj, **) @mutex.synchronize do if @shutdown_block @created -= 1 unless @created == 0 @shutdown_block.call(obj) else - store_connection obj, options + store_connection obj, ** end @resource.broadcast @@ -60,26 +60,22 @@ def push(obj, options = {}) # # The +timeout+ argument will be removed in 3.0. # Other options may be used by subclasses that extend TimedStack. - def pop(timeout = 0.5, options = {}) - options, timeout = timeout, 0.5 if Hash === timeout - timeout = options.fetch :timeout, timeout - + def pop(timeout: 0.5, exception: ConnectionPool::TimeoutError, **) deadline = current_time + timeout @mutex.synchronize do loop do raise ConnectionPool::PoolShuttingDownError if @shutdown_block - if (conn = try_fetch_connection(options)) + if (conn = try_fetch_connection(**)) return conn end - connection = try_create(options) + connection = try_create(**) return connection if connection to_wait = deadline - current_time if to_wait <= 0 - exc = options.fetch(:exception, ConnectionPool::TimeoutError) - if exc - raise ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" + if exception + raise exception, "Waited #{timeout} sec, #{length}/#{@max} available" else return nil end @@ -108,21 +104,19 @@ def shutdown(reload: false, &block) ## # Reaps connections that were checked in more than +idle_seconds+ ago. - def reap(idle_seconds, &block) - raise ArgumentError, "reap must receive a block" unless block + def reap(idle_seconds:) + raise ArgumentError, "reap must receive a block" unless block_given? raise ArgumentError, "idle_seconds must be a number" unless idle_seconds.is_a?(Numeric) raise ConnectionPool::PoolShuttingDownError if @shutdown_block idle.times do - conn = - @mutex.synchronize do - raise ConnectionPool::PoolShuttingDownError if @shutdown_block - - reserve_idle_connection(idle_seconds) - end + conn = @mutex.synchronize do + raise ConnectionPool::PoolShuttingDownError if @shutdown_block + reserve_idle_connection(idle_seconds) + end break unless conn - block.call(conn) + yield conn end end @@ -162,15 +156,15 @@ def current_time # This method must returns a connection from the stack if one exists. Allows # subclasses with expensive match/search algorithms to avoid double-handling # their stack. - def try_fetch_connection(options = nil) - connection_stored?(options) && fetch_connection(options) + def try_fetch_connection(**) + connection_stored?(**) && fetch_connection(**) end ## # This is an extension point for TimedStack and is called with a mutex. # # This method must returns true if a connection is available on the stack. - def connection_stored?(options = nil) + def connection_stored?(**) !@que.empty? end @@ -178,7 +172,7 @@ def connection_stored?(options = nil) # This is an extension point for TimedStack and is called with a mutex. # # This method must return a connection from the stack. - def fetch_connection(options = nil) + def fetch_connection(**) @que.pop&.first end @@ -186,8 +180,8 @@ def fetch_connection(options = nil) # This is an extension point for TimedStack and is called with a mutex. # # This method must shut down all connections on the stack. - def shutdown_connections(options = nil) - while (conn = try_fetch_connection(options)) + def shutdown_connections(**) + while (conn = try_fetch_connection(**)) @created -= 1 unless @created == 0 @shutdown_block.call(conn) end @@ -218,7 +212,7 @@ def idle_connections?(idle_seconds) # This is an extension point for TimedStack and is called with a mutex. # # This method must return +obj+ to the stack. - def store_connection(obj, options = nil) + def store_connection(obj, **) @que.push [obj, current_time] end @@ -227,7 +221,7 @@ def store_connection(obj, options = nil) # # This method must create a connection if and only if the total number of # connections allowed has not been met. - def try_create(options = nil) + def try_create(**) unless @created == @max object = @create_block.call @created += 1 diff --git a/lib/connection_pool/version.rb b/lib/connection_pool/version.rb index eb28348..9836c21 100644 --- a/lib/connection_pool/version.rb +++ b/lib/connection_pool/version.rb @@ -1,3 +1,3 @@ class ConnectionPool - VERSION = "2.5.5" + VERSION = "3.0.0" end diff --git a/lib/connection_pool/wrapper.rb b/lib/connection_pool/wrapper.rb index 8630bee..b817433 100644 --- a/lib/connection_pool/wrapper.rb +++ b/lib/connection_pool/wrapper.rb @@ -2,20 +2,20 @@ class ConnectionPool class Wrapper < ::BasicObject METHODS = [:with, :pool_shutdown, :wrapped_pool] - def initialize(options = {}, &block) - @pool = options.fetch(:pool) { ::ConnectionPool.new(options, &block) } + def initialize(**options, &) + @pool = options.fetch(:pool) { ::ConnectionPool.new(**options, &) } end def wrapped_pool @pool end - def with(&block) - @pool.with(&block) + def with(**, &) + @pool.with(**, &) end - def pool_shutdown(&block) - @pool.shutdown(&block) + def pool_shutdown(&) + @pool.shutdown(&) end def pool_size @@ -26,31 +26,18 @@ def pool_available @pool.available end - def respond_to?(id, *args) - METHODS.include?(id) || with { |c| c.respond_to?(id, *args) } + def respond_to?(id, *, **) + METHODS.include?(id) || with { |c| c.respond_to?(id, *, **) } end - # rubocop:disable Style/MissingRespondToMissing - if ::RUBY_VERSION >= "3.0.0" - def method_missing(name, *args, **kwargs, &block) - with do |connection| - connection.send(name, *args, **kwargs, &block) - end - end - elsif ::RUBY_VERSION >= "2.7.0" - ruby2_keywords def method_missing(name, *args, &block) - with do |connection| - connection.send(name, *args, &block) - end - end - else - def method_missing(name, *args, &block) - with do |connection| - connection.send(name, *args, &block) - end + def respond_to_missing?(id, *, **) + with { |c| c.respond_to?(id, *, **) } + end + + def method_missing(name, *, **, &) + with do |connection| + connection.send(name, *, **, &) end end - # rubocop:enable Style/MethodMissingSuper - # rubocop:enable Style/MissingRespondToMissing end end diff --git a/test/benchmarks.rb b/test/benchmarks.rb index b645483..e701ce0 100644 --- a/test/benchmarks.rb +++ b/test/benchmarks.rb @@ -7,6 +7,6 @@ Benchmark.ips do |x| x.report("ConnectionPool#with") do - CP.with {|x| } + CP.with { |x| } end -end \ No newline at end of file +end diff --git a/test/helper.rb b/test/helper.rb index b7ee3ca..6e0bc33 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -1,9 +1,28 @@ -gem "minitest" +require "bundler/setup" +Bundler.require(:default, :test) require "minitest/pride" -require "minitest/autorun" +require "maxitest/autorun" +require "maxitest/threads" +# require "maxitest/timeout" +# Maxitest.timeout = 0.5 -$VERBOSE = 1 +# $VERBOSE = 1 +# $TESTING = true +# disable minitest/parallel threads +# ENV["MT_CPU"] = "0" +# ENV["N"] = "0" +# Disable any stupid backtrace cleansers +# ENV["BACKTRACE"] = "1" + +if ENV["COVERAGE"] + require "simplecov" + SimpleCov.start do + enable_coverage :branch + add_filter "/test/" + minimum_coverage 90 + end +end require_relative "../lib/connection_pool" diff --git a/test/test_connection_pool.rb b/test/test_connection_pool.rb index 42ee243..9ad60a2 100644 --- a/test/test_connection_pool.rb +++ b/test/test_connection_pool.rb @@ -7,21 +7,25 @@ def teardown end class NetworkConnection - SLEEP_TIME = 0.1 + SLEEP_TIME = 0.02 def initialize @x = 0 end + def pass + Thread.pass + end + def do_something(*_args, increment: 1) @x += increment - sleep SLEEP_TIME + sleep 0.02 @x end def do_something_with_positional_hash(options) @x += options[:increment] || 1 - sleep SLEEP_TIME + pass @x end @@ -31,7 +35,7 @@ def fast def do_something_with_block @x += yield - sleep SLEEP_TIME + pass @x end @@ -63,18 +67,13 @@ def use_pool(pool, size) end def kill_threads(threads) - threads.each do |thread| - thread.kill - thread.join - end + threads.each(&:kill) + threads.each(&:join) end def test_basic_multithreaded_usage pool_size = 5 pool = ConnectionPool.new(size: pool_size) { NetworkConnection.new } - - start = Time.new - generations = 3 result = Array.new(pool_size * generations) { @@ -85,11 +84,7 @@ def test_basic_multithreaded_usage end }.map(&:value) - finish = Time.new - assert_equal((1..generations).cycle(pool_size).sort, result.sort) - - assert_operator(finish - start, :>, generations * NetworkConnection::SLEEP_TIME) end def test_timeout @@ -146,6 +141,14 @@ def test_with_timeout end end assert_equal 1, pool.available + # Timeout.timeout creates a watcher thread and does not provide a way to + # shut it down so we have to disable maxitest's extra thread paranoia or + # else it will trigger a test failure. + skip_maxitest_extra_threads + end + + def skip_maxitest_extra_threads + @maxitest_threads_before = Thread.list end def test_invalid_size @@ -159,7 +162,7 @@ def test_invalid_size def test_handle_interrupt_ensures_checkin pool = ConnectionPool.new(timeout: 0, size: 1) { Object.new } - def pool.checkout(options) + def pool.checkout(**options) sleep 0.015 super end @@ -226,7 +229,7 @@ def stack.connection_stored?(opts) options = {foo: 123} e = assert_raises do - pool.with(options) {} + pool.with(**options) {} end assert_equal e.message, options.to_s @@ -388,7 +391,7 @@ def test_checkout_timeout_override pool.checkout end - assert pool.checkout timeout: 2 * NetworkConnection::SLEEP_TIME + assert pool.checkout(timeout: 2 * NetworkConnection::SLEEP_TIME) end def test_passthru @@ -467,7 +470,7 @@ def test_nested_checkout def test_nested_discard recorder = Recorder.new - pool = ConnectionPool.new(size: 1) { {recorder: recorder} } + pool = ConnectionPool.new(size: 1, timeout: 0.01) { {recorder: recorder} } pool.with do |r_outer| @other = Thread.new { |t| pool.with do |r_other| @@ -591,7 +594,7 @@ def test_reap_removes_idle_connections assert_equal 1, pool.idle - pool.reap(0) { |recorder| recorder.do_work("reap") } + pool.reap(idle_seconds: 0) { |recorder| recorder.do_work("reap") } assert_equal 0, pool.idle assert_equal [["reap"]], recorders.map(&:calls) @@ -607,7 +610,7 @@ def test_reap_removes_all_idle_connections assert_equal 3, pool.idle - pool.reap(0) { |recorder| recorder.do_work("reap") } + pool.reap(idle_seconds: 0) { |recorder| recorder.do_work("reap") } assert_equal 0, pool.idle assert_equal [["reap"]] * 3, recorders.map(&:calls) @@ -618,7 +621,7 @@ def test_reap_does_not_remove_connections_if_outside_idle_time pool.with { |conn| conn } - pool.reap(1000) { |conn| flunk "should not reap active connection" } + pool.reap(idle_seconds: 1000) { |conn| flunk "should not reap active connection" } end def test_idle_returns_number_of_idle_connections @@ -655,7 +658,7 @@ def test_reap_raises_error_after_shutting_down pool.shutdown {} assert_raises ConnectionPool::PoolShuttingDownError do - pool.reap(0) {} + pool.reap(idle_seconds: 0) {} end end @@ -687,6 +690,9 @@ def test_wrapper_with assert_raises Timeout::Error do wrapper.with { flunk "connection checked out :(" } end + assert_raises Timeout::Error do + wrapper.with(timeout: 0.1) { flunk "connection checked out :(" } + end }.join end @@ -805,7 +811,8 @@ def test_ractors_pool_usage pool.with { |y| checkedout = y } checkedout end - result = r.take + + result = (RUBY_VERSION < "4") ? r.take : r.value assert_equal obj, result # same string but different String instance refute_equal obj.object_id, result.object_id # was copied across Ractor boundary end diff --git a/test/test_connection_pool_timed_stack.rb b/test/test_connection_pool_timed_stack.rb index 503177c..daab7ef 100644 --- a/test/test_connection_pool_timed_stack.rb +++ b/test/test_connection_pool_timed_stack.rb @@ -6,7 +6,7 @@ def setup end def test_empty_eh - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } refute_empty stack @@ -20,7 +20,7 @@ def test_empty_eh end def test_length - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } assert_equal 1, stack.length @@ -46,7 +46,7 @@ def test_length_after_shutdown_reload_for_no_create_stack end def test_length_after_shutdown_reload_with_checked_out_conn - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } conn = stack.pop stack.shutdown(reload: true) {} assert_equal 0, stack.length @@ -55,7 +55,7 @@ def test_length_after_shutdown_reload_with_checked_out_conn end def test_idle - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } assert_equal 0, stack.idle @@ -69,7 +69,7 @@ def test_idle end def test_object_creation_fails - stack = ConnectionPool::TimedStack.new(2) { raise "failure" } + stack = ConnectionPool::TimedStack.new(size: 2) { raise "failure" } begin stack.pop @@ -103,20 +103,13 @@ def test_pop_empty assert_nil @stack.pop(timeout: 0, exception: false) end - def test_pop_empty_2_0_compatibility - e = assert_raises(Timeout::Error) { @stack.pop 0 } - assert_equal "Waited 0 sec, 0/0 available", e.message - - assert_nil @stack.pop(0, exception: false) - end - def test_pop_empty_custom_exception e = assert_raises(RuntimeError) { @stack.pop(timeout: 0, exception: RuntimeError) } assert_equal "Waited 0 sec, 0/0 available", e.message end def test_pop_full - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } popped = stack.pop @@ -125,7 +118,7 @@ def test_pop_full end def test_pop_full_with_extra_conn_pushed - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } popped = stack.pop stack.push(Object.new) @@ -137,7 +130,7 @@ def test_pop_full_with_extra_conn_pushed assert_equal 1, stack.length stack.pop - assert_raises(ConnectionPool::TimeoutError) { stack.pop(0) } + assert_raises(ConnectionPool::TimeoutError) { stack.pop(timeout: 0) } end def test_pop_wait @@ -163,7 +156,7 @@ def test_pop_shutdown end def test_pop_shutdown_reload - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } object = stack.pop stack.push(object) @@ -173,16 +166,16 @@ def test_pop_shutdown_reload end def test_pop_raises_error_if_shutdown_reload_is_run_and_connection_is_still_in_use - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } stack.pop stack.shutdown(reload: true) {} assert_raises ConnectionPool::TimeoutError do - stack.pop(0) + stack.pop(timeout: 0) end end def test_push - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } conn = stack.pop @@ -256,12 +249,12 @@ def test_reap_can_be_called_after_error end assert_raises(closing_error) do - @stack.reap(0, &reap_proc) + @stack.reap(idle_seconds: 0, &reap_proc) end assert_equal 1, called.size - @stack.reap(0, &reap_proc) + @stack.reap(idle_seconds: 0, &reap_proc) assert_equal 3, called.size end @@ -270,7 +263,7 @@ def test_reap called = [] - @stack.reap(0) do |object| + @stack.reap(idle_seconds: 0) do |object| called << object end @@ -279,10 +272,10 @@ def test_reap end def test_reap_full_stack - stack = ConnectionPool::TimedStack.new(1) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 1) { Object.new } stack.push stack.pop - stack.reap(0) do |object| + stack.reap(idle_seconds: 0) do |object| nil end @@ -295,7 +288,7 @@ def test_reap_large_idle_seconds called = [] - @stack.reap(100) do |object| + @stack.reap(idle_seconds: 100) do |object| called << object end @@ -305,18 +298,18 @@ def test_reap_large_idle_seconds def test_reap_no_block assert_raises(ArgumentError) do - @stack.reap(0) + @stack.reap(idle_seconds: 0) end end def test_reap_non_numeric_idle_seconds assert_raises(ArgumentError) do - @stack.reap("0") { |object| object } + @stack.reap(idle_seconds: "0") { |object| object } end end def test_reap_with_multiple_connections - stack = ConnectionPool::TimedStack.new(2) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 2) { Object.new } stubbed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) conn1 = stack.pop conn2 = stack.pop @@ -332,7 +325,7 @@ def test_reap_with_multiple_connections called = [] stack.stub :current_time, stubbed_time + 2 do - stack.reap(1.5) do |object| + stack.reap(idle_seconds: 1.5) do |object| called << object end end @@ -343,7 +336,7 @@ def test_reap_with_multiple_connections end def test_reap_with_multiple_connections_and_zero_idle_seconds - stack = ConnectionPool::TimedStack.new(2) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 2) { Object.new } stubbed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) conn1 = stack.pop conn2 = stack.pop @@ -359,7 +352,7 @@ def test_reap_with_multiple_connections_and_zero_idle_seconds called = [] stack.stub :current_time, stubbed_time + 2 do - stack.reap(0) do |object| + stack.reap(idle_seconds: 0) do |object| called << object end end @@ -369,7 +362,7 @@ def test_reap_with_multiple_connections_and_zero_idle_seconds end def test_reap_with_multiple_connections_and_idle_seconds_outside_range - stack = ConnectionPool::TimedStack.new(2) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 2) { Object.new } stubbed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) conn1 = stack.pop conn2 = stack.pop @@ -385,7 +378,7 @@ def test_reap_with_multiple_connections_and_idle_seconds_outside_range called = [] stack.stub :current_time, stubbed_time + 2 do - stack.reap(3) do |object| + stack.reap(idle_seconds: 3) do |object| called << object end end @@ -395,12 +388,12 @@ def test_reap_with_multiple_connections_and_idle_seconds_outside_range end def test_reap_does_not_loop_continuously - stack = ConnectionPool::TimedStack.new(2) { Object.new } + stack = ConnectionPool::TimedStack.new(size: 2) { Object.new } stack.push(Object.new) stack.push(Object.new) close_attempts = 0 - stack.reap(0) do |conn| + stack.reap(idle_seconds: 0) do |conn| if close_attempts >= 2 flunk "Reap is stuck in a loop" end diff --git a/test/test_timed_stack_subclassing.rb b/test/test_timed_stack_subclassing.rb index 50fb8d4..57314f2 100644 --- a/test/test_timed_stack_subclassing.rb +++ b/test/test_timed_stack_subclassing.rb @@ -9,7 +9,7 @@ def setup def test_try_fetch_connection obj = Object.new - stack = @klass.new(1) { obj } + stack = @klass.new(size: 1) { obj } assert_equal false, stack.send(:try_fetch_connection) assert_equal obj, stack.pop stack.push obj @@ -19,7 +19,7 @@ def test_try_fetch_connection def test_override_try_fetch_connection obj = Object.new - stack = @klass.new(1) { obj } + stack = @klass.new(size: 1) { obj } stack.push stack.pop connection_stored_called = "cs_called"