Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package rubygem-connection_pool for
openSUSE:Factory checked in at 2026-07-15 16:33:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-connection_pool (Old)
and /work/SRC/openSUSE:Factory/.rubygem-connection_pool.new.1991 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-connection_pool"
Wed Jul 15 16:33:47 2026 rev:3 rq:1365320 version:3.0.2
Changes:
--------
---
/work/SRC/openSUSE:Factory/rubygem-connection_pool/rubygem-connection_pool.changes
2025-02-06 22:10:53.059613688 +0100
+++
/work/SRC/openSUSE:Factory/.rubygem-connection_pool.new.1991/rubygem-connection_pool.changes
2026-07-15 16:48:48.945173046 +0200
@@ -1,0 +2,6 @@
+Mon Jul 13 08:57:58 UTC 2026 - Aleksei Burlakov <[email protected]>
+
+- Update to version 3.0.2:
+ * Support :name keyword for backwards compatibility
+
+-------------------------------------------------------------------
Old:
----
connection_pool-2.5.0.gem
New:
----
connection_pool-3.0.2.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-connection_pool.spec ++++++
--- /var/tmp/diff_new_pack.PVBGpq/_old 2026-07-15 16:48:56.553431555 +0200
+++ /var/tmp/diff_new_pack.PVBGpq/_new 2026-07-15 16:48:56.569432099 +0200
@@ -1,7 +1,7 @@
#
# spec file for package rubygem-connection_pool
#
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -24,7 +24,7 @@
#
Name: rubygem-connection_pool
-Version: 2.5.0
+Version: 3.0.2
Release: 0
%define mod_name connection_pool
%define mod_full_name %{mod_name}-%{version}
++++++ connection_pool-2.5.0.gem -> connection_pool-3.0.2.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Changes.md new/Changes.md
--- old/Changes.md 2025-01-07 21:26:26.000000000 +0100
+++ new/Changes.md 1980-01-02 01:00:00.000000000 +0100
@@ -1,5 +1,57 @@
# connection_pool Changelog
+3.0.2
+------
+
+- Support :name keyword for backwards compatibility [#210]
+
+3.0.1
+------
+
+- Add missing `fork.rb` to gemspec.
+
+3.0.0
+------
+
+- **BREAKING CHANGES** `ConnectionPool` and `ConnectionPool::TimedStack` now
+ use keyword arguments rather than positional arguments everywhere. Expected
impact is minimal as most people use the `with` API, which is unchanged.
+```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
+```
+- Dropped support for Ruby <3.2.0
+
+2.5.5
+------
+
+- Support `ConnectionPool::TimedStack#pop(exception: false)` [#207]
+ to avoid using exceptions as control flow.
+
+2.5.4
+------
+
+- Add ability to remove a broken connection from the pool [#204, womblep]
+
+2.5.3
+------
+
+- Fix TruffleRuby/JRuby crash [#201]
+
+2.5.2
+------
+
+- Rollback inadvertant change to `auto_reload_after_fork` default. [#200]
+
+2.5.1
+------
+
+- Pass options to TimedStack in `checkout` [#195]
+- Optimize connection lookup [#196]
+- Fixes for use with Ractors
+
2.5.0
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/README.md new/README.md
--- old/README.md 2025-01-07 21:26:26.000000000 +0100
+++ new/README.md 1980-01-02 01:00:00.000000000 +0100
@@ -38,7 +38,7 @@
$redis.then { |r| r.set 'foo' 'bar' }
```
-Optionally, you can specify a timeout override using the with-block semantics:
+Optionally, you can specify a timeout override:
``` ruby
$memcached.with(timeout: 2.0) do |conn|
@@ -46,11 +46,9 @@
end
```
-This will only modify the resource-get timeout for this particular
-invocation.
+This will only modify the 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
@@ -72,7 +70,7 @@
end
```
-Once you've ported your entire system to use `with`, you can simply remove
`Wrapper` and use the simpler and faster `ConnectionPool`.
+Once you've ported your entire system to use `with`, you can remove
`::Wrapper` and use `ConnectionPool` directly.
## Shutdown
@@ -90,41 +88,50 @@
## Reload
-You can reload a ConnectionPool instance in the case it is desired to close
all connections to the pool and, unlike `shutdown`, afterwards recreate
connections so the pool may continue to be used.
-Reloading may be useful after forking the process.
+You can reload a ConnectionPool instance if it is necessary to close all
existing connections and continue to use the pool.
+ConnectionPool will automatically reload if the process is forked.
+Use `auto_reload_after_fork: false` if you don't want this behavior.
```ruby
-cp = ConnectionPool.new { Redis.new }
-cp.reload { |conn| conn.quit }
+cp = ConnectionPool.new(auto_reload_after_fork: false) { Redis.new }
+cp.reload { |conn| conn.quit } # reload manually
cp.with { |conn| conn.get('some-count') }
```
-Like `shutdown`, this will block until all connections are checked in and
closed.
+Like `shutdown`, `reload` 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).
+
+# Start a reaper thread to reap connections that have been
+# idle more than 300 seconds (5 minutes)
+Thread.new do
+ loop do
+ cp.reap(idle_seconds: 300, &:close)
+ sleep 30
+ end
+end
```
-### Reaper Thread
+## Discarding Connections
-You can start your own reaper thread to reap idle connections in the
ConnectionPool instance on a regular interval.
+You can discard connections in the ConnectionPool instance to remove
connections that are broken and can't be repaired.
+It can only be done inside the block passed to `with`.
+Takes an optional block that will be executed with the connection.
```ruby
-cp = ConnectionPool.new { Redis.new }
-
-# Start a reaper thread to reap connections that have been idle for 300
seconds (5 minutes).
-Thread.new do
- loop do
- cp.reap(300) { |conn| conn.close }
- sleep 300
+pool.with do |conn|
+ begin
+ conn.execute("SELECT 1")
+ rescue SomeConnectionError
+ pool.discard_current_connection(&:close) # remove the connection from the
pool
+ raise
end
end
```
@@ -148,20 +155,28 @@
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),
<https://www.mikeperham.com>
+Mike Perham, [@getajobmike](https://ruby.social/@getajobmike),
<https://www.mikeperham.com>
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/connection_pool.gemspec new/connection_pool.gemspec
--- old/connection_pool.gemspec 2025-01-07 21:26:26.000000000 +0100
+++ new/connection_pool.gemspec 1980-01-02 01:00:00.000000000 +0100
@@ -10,15 +10,26 @@
s.description = s.summary = "Generic connection pool for Ruby"
s.files = ["Changes.md", "LICENSE", "README.md", "connection_pool.gemspec",
- "lib/connection_pool.rb", "lib/connection_pool/timed_stack.rb",
- "lib/connection_pool/version.rb", "lib/connection_pool/wrapper.rb"]
+ "lib/connection_pool.rb",
+ "lib/connection_pool/timed_stack.rb",
+ "lib/connection_pool/version.rb",
+ "lib/connection_pool/fork.rb",
+ "lib/connection_pool/wrapper.rb"]
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/connection_pool/fork.rb
new/lib/connection_pool/fork.rb
--- old/lib/connection_pool/fork.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/lib/connection_pool/fork.rb 1980-01-02 01:00:00.000000000 +0100
@@ -0,0 +1,40 @@
+class ConnectionPool
+ if Process.respond_to?(:fork)
+ INSTANCES = ObjectSpace::WeakMap.new
+ private_constant :INSTANCES
+
+ def self.after_fork
+ INSTANCES.each_value do |pool|
+ # We're in after_fork, so we know all other threads are dead.
+ # All we need to do is ensure the main thread doesn't have a
+ # checked out connection
+ pool.checkin(force: true)
+ pool.reload do |connection|
+ # Unfortunately we don't know what method to call to close the
connection,
+ # so we try the most common one.
+ connection.close if connection.respond_to?(:close)
+ end
+ end
+ nil
+ end
+
+ module ForkTracker
+ def _fork
+ pid = super
+ if pid == 0
+ ConnectionPool.after_fork
+ end
+ pid
+ end
+ end
+ Process.singleton_class.prepend(ForkTracker)
+ else
+ # JRuby, et al
+ INSTANCES = nil
+ private_constant :INSTANCES
+
+ def self.after_fork
+ # noop
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/connection_pool/timed_stack.rb
new/lib/connection_pool/timed_stack.rb
--- old/lib/connection_pool/timed_stack.rb 2025-01-07 21:26:26.000000000
+0100
+++ new/lib/connection_pool/timed_stack.rb 1980-01-02 01:00:00.000000000
+0100
@@ -1,11 +1,11 @@
##
# The TimedStack manages a pool of homogeneous connections (or any resource
-# you wish to manage). Connections are created lazily up to a given maximum
+# you wish to manage). Connections are created lazily up to a given maximum
# number.
-
+#
# Examples:
#
-# ts = TimedStack.new(1) { MyConnection.new }
+# ts = TimedStack.new(size: 1) { MyConnection.new }
#
# # fetch a connection
# conn = ts.pop
@@ -16,15 +16,13 @@
# conn = ts.pop
# ts.pop timeout: 5
# #=> raises ConnectionPool::TimeoutError after 5 seconds
-
class ConnectionPool::TimedStack
attr_reader :max
##
# 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 = []
@@ -35,16 +33,15 @@
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
@@ -53,29 +50,35 @@
alias_method :<<, :push
##
- # Retrieves a connection from the stack. If a connection is available it is
- # immediately returned. If no connection is available within the given
+ # Retrieves a connection from the stack. If a connection is available it is
+ # immediately returned. If no connection is available within the given
# timeout a ConnectionPool::TimeoutError is raised.
#
- # +:timeout+ is the only checked entry in +options+ and is preferred over
- # the +timeout+ argument (which will be removed in a future release). 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
-
+ # @option options [Float] :timeout (0.5) Wait this many seconds for an
available entry
+ # @option options [Class] :exception (ConnectionPool::TimeoutError)
Exception class to raise
+ # if an entry was not available within the timeout period. Use `exception:
false` to return nil.
+ #
+ # Other options may be used by subclasses that extend TimedStack.
+ def pop(timeout: 0.5, exception: ConnectionPool::TimeoutError, **)
deadline = current_time + timeout
@mutex.synchronize do
loop do
raise ConnectionPool::PoolShuttingDownError if @shutdown_block
- return fetch_connection(options) if connection_stored?(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
- raise ConnectionPool::TimeoutError, "Waited #{timeout} sec,
#{length}/#{@max} available" if to_wait <= 0
+ if to_wait <= 0
+ if exception
+ raise exception, "Waited #{timeout} sec, #{length}/#{@max}
available"
+ else
+ return nil
+ end
+ end
@resource.wait(@mutex, to_wait)
end
end
@@ -86,7 +89,6 @@
# removing it from the pool. Attempting to checkout a connection after
# shutdown will raise +ConnectionPool::PoolShuttingDownError+ unless
# +:reload+ is +true+.
-
def shutdown(reload: false, &block)
raise ArgumentError, "shutdown must receive a block" unless block
@@ -101,34 +103,31 @@
##
# 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
+ count = idle
+ count.times do
+ 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
##
# Returns +true+ if there are no available connections.
-
def empty?
(@created - @que.length) >= @max
end
##
# The number of connections available on the stack.
-
def length
@max - @created + @que.length
end
@@ -139,6 +138,12 @@
@que.length
end
+ ##
+ # Reduce the created count
+ def decrement_created
+ @created -= 1 unless @created == 0
+ end
+
private
def current_time
@@ -148,9 +153,18 @@
##
# 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.
+ # 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(**)
+ connection_stored?(**) && fetch_connection(**)
+ end
- def connection_stored?(options = nil)
+ ##
+ # 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?(**)
[email protected]?
end
@@ -158,8 +172,7 @@
# 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
@@ -167,10 +180,8 @@
# 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 connection_stored?(options)
- conn = fetch_connection(options)
+ def shutdown_connections(**)
+ while (conn = try_fetch_connection(**))
@created -= 1 unless @created == 0
@shutdown_block.call(conn)
end
@@ -181,12 +192,13 @@
#
# This method returns the oldest idle connection if it has been idle for
more than idle_seconds.
# This requires that the stack is kept in order of checked in time (oldest
first).
-
def reserve_idle_connection(idle_seconds)
return unless idle_connections?(idle_seconds)
@created -= 1 unless @created == 0
+ # Most active elements are at the tail of the array.
+ # Most idle will be at the head so `shift` rather than `pop`.
@que.shift.first
end
@@ -194,17 +206,18 @@
# This is an extension point for TimedStack and is called with a mutex.
#
# Returns true if the first connection in the stack has been idle for more
than idle_seconds
-
def idle_connections?(idle_seconds)
- connection_stored? && (current_time - @que.first.last > idle_seconds)
+ return unless connection_stored?
+ # Most idle will be at the head so `first`
+ age = (current_time - @que.first.last)
+ age > idle_seconds
end
##
# 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
@@ -213,8 +226,7 @@
#
# 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/connection_pool/version.rb
new/lib/connection_pool/version.rb
--- old/lib/connection_pool/version.rb 2025-01-07 21:26:26.000000000 +0100
+++ new/lib/connection_pool/version.rb 1980-01-02 01:00:00.000000000 +0100
@@ -1,3 +1,3 @@
class ConnectionPool
- VERSION = "2.5.0"
+ VERSION = "3.0.2"
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/connection_pool/wrapper.rb
new/lib/connection_pool/wrapper.rb
--- old/lib/connection_pool/wrapper.rb 2025-01-07 21:26:26.000000000 +0100
+++ new/lib/connection_pool/wrapper.rb 1980-01-02 01:00:00.000000000 +0100
@@ -2,20 +2,20 @@
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 @@
@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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/connection_pool.rb new/lib/connection_pool.rb
--- old/lib/connection_pool.rb 2025-01-07 21:26:26.000000000 +0100
+++ new/lib/connection_pool.rb 1980-01-02 01:00:00.000000000 +0100
@@ -39,72 +39,30 @@
# - :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}
-
- def self.wrap(options, &block)
- Wrapper.new(options, &block)
- end
-
- if Process.respond_to?(:fork)
- INSTANCES = ObjectSpace::WeakMap.new
- private_constant :INSTANCES
-
- def self.after_fork
- INSTANCES.values.each do |pool|
- next unless pool.auto_reload_after_fork
-
- # We're on after fork, so we know all other threads are dead.
- # All we need to do is to ensure the main thread doesn't have a
- # checked out connection
- pool.checkin(force: true)
- pool.reload do |connection|
- # Unfortunately we don't know what method to call to close the
connection,
- # so we try the most common one.
- connection.close if connection.respond_to?(:close)
- end
- end
- nil
- end
-
- if ::Process.respond_to?(:_fork) # MRI 3.1+
- module ForkTracker
- def _fork
- pid = super
- if pid == 0
- ConnectionPool.after_fork
- end
- pid
- end
- end
- Process.singleton_class.prepend(ForkTracker)
- end
- else
- INSTANCES = nil
- private_constant :INSTANCES
-
- def self.after_fork
- # noop
- end
+ def self.wrap(**, &)
+ Wrapper.new(**, &)
end
- def initialize(options = {}, &block)
- raise ArgumentError, "Connection pool requires a block" unless block
-
- options = DEFAULTS.merge(options)
+ attr_reader :size
- @size = Integer(options.fetch(:size))
- @timeout = options.fetch(:timeout)
- @auto_reload_after_fork = options.fetch(:auto_reload_after_fork)
+ def initialize(timeout: 5, size: 5, auto_reload_after_fork: true, name: nil,
&)
+ raise ArgumentError, "Connection pool requires a block" unless block_given?
- @available = TimedStack.new(@size, &block)
+ @size = Integer(size)
+ @timeout = Float(timeout)
+ @available = TimedStack.new(size: @size, &)
@key = :"pool-#{@available.object_id}"
@key_count = :"pool-#{@available.object_id}-count"
- INSTANCES[self] = self if INSTANCES
+ @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
@@ -116,20 +74,67 @@
end
alias_method :then, :with
- def checkout(options = {})
+ ##
+ # Marks the current thread's checked-out connection for discard.
+ #
+ # When a connection is marked for discard, it will not be returned to the
pool
+ # when checked in. Instead, the connection will be discarded.
+ # This is useful when a connection has become invalid or corrupted
+ # and should not be reused.
+ #
+ # Takes an optional block that will be called with the connection to be
discarded.
+ # The block should perform any necessary clean-up on the connection.
+ #
+ # @yield [conn]
+ # @yieldparam conn [Object] The connection to be discarded.
+ # @yieldreturn [void]
+ #
+ #
+ # Note: This only affects the connection currently checked out by the
calling thread.
+ # The connection will be discarded when +checkin+ is called.
+ #
+ # @return [void]
+ #
+ # @example
+ # pool.with do |conn|
+ # begin
+ # conn.execute("SELECT 1")
+ # rescue SomeConnectionError
+ # pool.discard_current_connection # Mark connection as bad
+ # raise
+ # end
+ # end
+ def discard_current_connection(&block)
+ ::Thread.current[@discard_key] = block || proc { |conn| conn }
+ end
+
+ def checkout(timeout: @timeout, **)
if ::Thread.current[@key]
::Thread.current[@key_count] += 1
::Thread.current[@key]
else
+ conn = @available.pop(timeout:, **)
+ ::Thread.current[@key] = conn
::Thread.current[@key_count] = 1
- ::Thread.current[@key] = @available.pop(options[:timeout] || @timeout)
+ conn
end
end
def checkin(force: false)
if ::Thread.current[@key]
if ::Thread.current[@key_count] == 1 || force
- @available.push(::Thread.current[@key])
+ if ::Thread.current[@discard_key]
+ begin
+ @available.decrement_created
+ ::Thread.current[@discard_key].call(::Thread.current[@key])
+ rescue
+ nil
+ ensure
+ ::Thread.current[@discard_key] = nil
+ end
+ else
+ @available.push(::Thread.current[@key])
+ end
::Thread.current[@key] = nil
::Thread.current[@key_count] = nil
else
@@ -146,31 +151,24 @@
# Shuts down the ConnectionPool by passing each connection to +block+ and
# then removing it from the pool. Attempting to checkout a connection after
# shutdown will raise +ConnectionPool::PoolShuttingDownError+.
-
- def shutdown(&block)
- @available.shutdown(&block)
+ def shutdown(&)
+ @available.shutdown(&)
end
##
# Reloads the ConnectionPool by passing each connection to +block+ and then
# removing it the pool. Subsequent checkouts will create new connections as
# needed.
-
- def reload(&block)
- @available.shutdown(reload: true, &block)
+ def reload(&)
+ @available.shutdown(reload: true, &)
end
## 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, &)
+ @available.reap(idle_seconds:, &)
end
- # Size of this connection pool
- attr_reader :size
- # Automatically drop all connections after fork
- attr_reader :auto_reload_after_fork
-
# Number of pool entries available for checkout at this instant.
def available
@available.length
@@ -184,3 +182,4 @@
require_relative "connection_pool/timed_stack"
require_relative "connection_pool/wrapper"
+require_relative "connection_pool/fork"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2025-01-07 21:26:26.000000000 +0100
+++ new/metadata 1980-01-02 01:00:00.000000000 +0100
@@ -1,15 +1,14 @@
--- !ruby/object:Gem::Specification
name: connection_pool
version: !ruby/object:Gem::Version
- version: 2.5.0
+ version: 3.0.2
platform: ruby
authors:
- Mike Perham
- Damian Janowski
-autorequire:
bindir: bin
cert_chain: []
-date: 2025-01-07 00:00:00.000000000 Z
+date: 1980-01-02 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: bundler
@@ -26,19 +25,19 @@
- !ruby/object:Gem::Version
version: '0'
- !ruby/object:Gem::Dependency
- name: minitest
+ name: maxitest
requirement: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
- version: 5.0.0
+ version: '0'
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
- version: 5.0.0
+ version: '0'
- !ruby/object:Gem::Dependency
name: rake
requirement: !ruby/object:Gem::Requirement
@@ -66,6 +65,7 @@
- README.md
- connection_pool.gemspec
- lib/connection_pool.rb
+- lib/connection_pool/fork.rb
- lib/connection_pool/timed_stack.rb
- lib/connection_pool/version.rb
- lib/connection_pool/wrapper.rb
@@ -73,9 +73,12 @@
licenses:
- MIT
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'
-post_install_message:
rdoc_options: []
require_paths:
- lib
@@ -83,15 +86,14 @@
requirements:
- - ">="
- !ruby/object:Gem::Version
- version: 2.5.0
+ version: 3.2.0
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
requirements: []
-rubygems_version: 3.5.22
-signing_key:
+rubygems_version: 3.6.9
specification_version: 4
summary: Generic connection pool for Ruby
test_files: []