Open-source project
mperham/connection_pool avatar
mperham/connection_pool

connection_pool for Ruby: usage, lifecycle, and limits

Generic connection pooling for Ruby

1,691 stars146 forksRubyMIT

At a glance

What is it?
connection_pool is a generic Ruby library for sharing a bounded set of client connections among threads or fibers. Its main design choice is explicit checkout with `with`, while `Wrapper` offers a slower migration path for code built around a single global client.
Who is it for?
Adopt connection_pool when a Ruby application shares clients such as Redis or Dalli across concurrent fibers or threads and can put resource-sensitive operations inside `with`. Do not choose it merely to wrap a single client permanently, because the README says `ConnectionPool::Wrapper` is not high performance.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • mperham/connection_pool1,691 ★ · -1
Star history
Repository2026-10-162026-11-06
mperham/connection_pool1.7k1.7k

-1 Stars in the last 21 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

connection_pool solves bounded client sharing in concurrent Ruby

connection_pool addresses applications in which several threads or fibers need access to the same type of resource, such as Redis, Dalli, or another Ruby network client, but opening an unlimited number of clients would be undesirable. It creates a pool whose capacity is set with `size` and hands out a connection for the duration of a block. The library is deliberately generic. It does not require Redis, MongoDB, or ActiveRecord as its object model.

The README contrasts this library with connection pools built into MongoDB and ActiveRecord. That distinction matters when deciding which layer should own pooling. A database-specific pool may already express the relevant client constraints, while this project is aimed at clients that do not provide equivalent pooling or at applications that want a shared abstraction across clients.

Its central tradeoff is bounded concurrency with blocking. If every pooled object is checked out, `with` waits until an object becomes available. If none is available before the configured `:timeout`, it raises `ConnectionPool::TimeoutError`, which the README identifies as a subclass of `Timeout::Error`. This gives callers a defined failure point, but it also means pool sizing and timeout selection are part of application behavior rather than an invisible implementation detail.

Explicit `with` blocks define the connection lifecycle

A pool is created with a block that constructs the client. Connections are lazily created as needed, so construction does not require the pool to instantiate every possible object immediately. The ordinary usage pattern checks out a connection, performs one or more operations, and returns it automatically when the block ends.

ruby
$memcached = ConnectionPool.new(size: 5, timeout: 5) { Dalli::Client.new }

This creates a pool of five `Dalli::Client` objects with a five-second checkout timeout. The client construction remains application-specific. connection_pool controls access to those objects, but the README does not document automatic retry, health checks, reconnection, or protocol-specific failure handling.

The primary API exposes the resource through `with`:

ruby
$memcached.with do |conn|
  conn.get('some-count')
end

The block scopes the checkout. That makes accidental retention beyond the intended operation less likely than an interface that returns a connection and leaves the caller responsible for checking it back in. It also gives discarded-connection handling a defined location, since `discard_current_connection` must be called inside a `with` block.

The pool does not need to be used exclusively through this block form. The README documents `ConnectionPool#then`, which lets code support both a pooled client and a raw client. That is useful while an application is migrating, although it does not remove the need to choose correct pool capacity.

Per-call timeouts separate critical waits from noncritical waits

The pool-wide timeout establishes its normal checkout policy, but callers can override it for one invocation. This is a meaningful operational control because a background or optional operation may need to fail quickly, while another operation may reasonably wait longer for a connection.

ruby
$memcached.with(timeout: 2.0) do |conn|
  conn.get('some-count')
end

Here the checkout attempt waits at most two seconds instead of using the timeout configured on the pool. The README says this changes only that invocation. It does not mutate the pool's default or affect later callers.

The resulting exception is part of the interface callers must handle. A service can let the timeout propagate, rescue `ConnectionPool::TimeoutError`, or use a different timeout for different classes of work. The README does not prescribe a retry policy, and it does not claim that retrying is safe for a particular client operation. Retries therefore need application-level judgment.

The library also warns against using Ruby's `Timeout.timeout` in application code because it can cause silent corruption and mysterious errors. The warning is broader than connection checkout. It recommends socket timeout options exposed by libraries such as `Net::HTTP`, Redis, and Dalli instead. This distinction matters for production design: pool checkout timeout limits waiting for a resource, while client or socket timeout limits behavior after a connection has been obtained.

Wrapper supports migration but adds a slower compatibility layer

Existing applications often have code written around a global connection rather than an explicit block. `ConnectionPool::Wrapper` is provided to ease migration. It presents the wrapped client's methods through `method_missing`; each request checks out a pooled connection, runs the method, and immediately returns the connection.

ruby
$redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.new }
$redis.sadd('foo', 1)
$redis.smembers('foo')

This can reduce the initial amount of call-site rewriting. The documented direction is to move performance-sensitive code to `with` and remove `Wrapper` after the system has been migrated:

ruby
$redis.with do |conn|
  conn.sadd('foo', 1)
  conn.smembers('foo')
end

The tradeoff is explicit in the README, which says the wrapper is not high performance. It should therefore be treated as a transition mechanism rather than the preferred long-term interface. The project does not document a way to configure or measure that overhead, so teams should inspect their own call paths before keeping the wrapper in a latency-sensitive request.

The real alternative is the client's own single-client integration or another architecture that avoids shared connection management, such as keeping one client per execution context. connection_pool is more relevant when concurrency genuinely exceeds the useful number of shared connections and the application needs a bounded checkout policy.

Shutdown, reload, and reaping address different lifecycle needs

The library separates permanent shutdown from operational reload. `shutdown` marks a pool as no longer usable for new checkouts, but existing checkouts continue to work. It blocks until checked-in connections have been closed by the supplied cleanup block.

ruby
cp = ConnectionPool.new { Redis.new }
cp.shutdown { |c| c.close }

Shutdown is optional according to the README because Ruby's garbage collector normally reclaims unreferenced pools. That does not mean an application can ignore orderly client closure in every deployment. The cleanup block is the documented place for client-specific cleanup such as `close`.

`reload` serves a different case. It closes existing connections and allows continued use of the pool. The README also states that connection_pool automatically reloads after a process fork, unless `auto_reload_after_fork: false` is supplied. Manual reload blocks until connections are checked in and closed:

ruby
cp = ConnectionPool.new(auto_reload_after_fork: false) { Redis.new }
cp.reload { |conn| conn.quit }
cp.with { |conn| conn.get('some-count') }

For expensive clients, `reap` closes connections that have remained idle beyond a specified duration. Its documented default is 60 seconds. The example starts a thread and calls it every 30 seconds:

ruby
Thread.new do
  loop do
    cp.reap(idle_seconds: 300, &:close)
    sleep 30
  end
end

This is application-owned scheduling. The library exposes the operation but does not document a built-in scheduler.

Broken connections require explicit discard decisions

connection_pool does not claim to repair failed connections. Its documented response is to discard a broken connection from inside the active checkout block, optionally passing it to a cleanup block. The pool can then create a replacement on a later need, subject to its normal creation and checkout behavior.

ruby
pool.with do |conn|
  begin
    conn.execute("SELECT 1")
  rescue SomeConnectionError
    pool.discard_current_connection(&:close)
    raise
  end
end

The location restriction is significant. Discarding is tied to the current checkout context, which helps prevent accidental removal of an unrelated connection. The example also re-raises the original error, allowing application error handling to remain visible rather than silently swallowing a database or client failure.

This is a lower-level responsibility than many users may expect from a pool. The README does not specify client-specific reconnection behavior, health checks, or whether a failed operation left a connection safe to reuse. Applications should define which failures justify discarding a connection and how cleanup is performed for their client.

State inspection is available through `size`, `available`, and `idle`. For a new pool of size ten, all ten are available before checkout, one becomes unavailable during a checkout, and one is idle after checkout. These methods help expose saturation, but the README does not document metrics export or alerting integration.

Maintenance, upgrades, and MIT licensing shape adoption cost

The repository is not archived, and its last push was on 2026-08-30. The repository has no GitHub releases, so adoption should not be based on an assumed release cadence. The available upgrade guidance is about the major API change from ConnectionPool 2.

Support for Ruby versions older than 3.2 has been removed. APIs now consistently use keyword arguments, including `checkout` and `reap`:

ruby
pool = ConnectionPool.new(size: 5, timeout: 5)
pool.checkout(timeout: 1)
pool.reap(idle_seconds: 30)

This makes migration checks concrete. Code using positional arguments from version 2 must be changed rather than treated as behaviorally identical. The repository includes a `LICENSE` identified as MIT, which permits use and modification under that license subject to its terms. This is a licensing observation, not legal advice.

There is also a fork-related operational choice. Automatic reload after fork is enabled by default and can be disabled. Teams running multi-process servers should verify that this behavior matches their client's connection state. The README documents the switch but does not enumerate every client library whose fork behavior is safe or unsafe.

Editorial conclusion

Adopt connection_pool when a Ruby application shares clients such as Redis or Dalli across concurrent fibers or threads and can put resource-sensitive operations inside `with`. Do not choose it merely to wrap a single client permanently, because the README says `ConnectionPool::Wrapper` is not high performance. Before adoption, verify client compatibility with explicit checkout, the timeout policy, shutdown and reload behavior, fork handling, and the operational cost of periodically calling `reap`.

Frequently asked questions

How do I use connection_pool with Redis or Dalli in Ruby?

Construct the pool with the client class, then perform operations inside a `with` block. The README uses `Dalli::Client.new` with `ConnectionPool.new(size: 5, timeout: 5)` and calls the checked-out client inside the block.

What happens when all connection_pool connections are in use?

The `with` call blocks until a connection becomes available. If none is available within the configured `:timeout`, it raises `ConnectionPool::TimeoutError`.

Can I shut down or reload a connection_pool?

Yes. `shutdown` closes the pool after outstanding checkouts finish, while `reload` closes current connections and permits continued use. Both operations accept a cleanup block and block until connections are checked in and closed.

Should I use ConnectionPool::Wrapper or `with`?

Use `Wrapper` as a migration aid for code that expects a global client, then move performance-sensitive code to `with`. The README explicitly says that `Wrapper` is not high performance.

How does connection_pool handle broken connections?

It does not document automatic repair. A caller can invoke `discard_current_connection` inside the `with` block, optionally passing a cleanup block, and then handle the original error.

Official sources

  1. Issues
  2. License: MIT
  3. mperham/connection_pool on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mperham-connection-pool.svg)](https://hysenlabs.com/projects/mperham-connection-pool)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/mperham-connection-pool.svg)](https://hysenlabs.com/projects/mperham-connection-pool)

Was this useful?