Pond

Pond is a gem that offers thread-safe object pooling. It can wrap anything that is costly to instantiate, but is usually used for connections. It is intentionally very similar to the connection_pool gem, but is intended to be more efficient and flexible. It instantiates objects lazily by default, which is important for things with high overhead like Postgres connections. It can also be dynamically resized, and does not block on object instantiation.

Also, it was pretty fun to write.

Installation

Add this line to your application's Gemfile:

gem 'pond'

And then execute:

$ bundle

Or install it yourself as:

$ gem install pond

Usage

require 'pond'
require 'redis'

$redis_pond = Pond.new(:maximum_size => 5, :timeout => 0.5) { Redis.new }

# No connections are established until we need one:
$redis_pond.checkout do |redis|
  redis.incr 'my_counter'
  redis.lpush 'my_list', 'item'
end

# Alternatively, wrap it:
$redis = Pond.wrap(:maximum_size => 5, :timeout => 0.5) { Redis.new }

# You can now use $redis as you normally would.
$redis.incr 'my_counter'
$redis.lpush 'my_list', 'item'

$redis.pipelined do
  # All these commands go to the same Redis connection, and so are pipelined correctly.
  $redis.incr 'my_counter'
  $redis.lpush 'my_list', 'item'
end

Options:

  • :maximum_size - The maximum number of objects you want the pool to contain. The default is 10.
  • :timeout - When attempting to check out an object but none are available, how many seconds to wait before raising a Pond::Timeout error. The default is 1. Integers or floats are both accepted.
  • :collection - How to manage the objects in the pool. The default is :queue, meaning that pond.checkout will yield the object that hasn't been used in the longest period of time. This is to prevent connections from becoming 'stale'. The alternative is :stack, so checkout will yield the object that has most recently been returned to the pool. This would be preferable if you're using connections that have their own logic for becoming idle in periods of low activity.
  • :eager - Set this to true to fill the pool with instantiated objects (up to the maximum size) when it is created, similar to how the connection_pool gem works.
  • :detach_if - Set this to a callable object that can determine whether objects should be returned to the pool or not. See the following example for more information.

Detaching Objects

Sometimes objects in the pool outlive their usefulness (connections may fail) and it becomes necessary to remove them. Pond's detach_if option is useful for this - you can pass it any callable object, and Pond will pass it objects from the pool that have been checked out before they are checked back in. For example, when using Pond with PostgreSQL connections:

require 'pond'
require 'pg'

$pg_pond = Pond.new(:detach_if => lambda {|c| c.finished?}) do
  PG.connect(:dbname => "pond_test")
end

Now, after a PostgreSQL connection has been used, but before it is returned to the pool, it will be passed to that lambda to see if it should be detached or not. If the lambda returns truthy, the connection will be detached (and made available for garbage collection), and a new one will be instantiated to replace it as necessary (until the pool returns to its maximum size).

Contributing

I don't plan on adding too many more features to Pond, since I want to keep its design simple. If there's something you'd like to see it do, open an issue so we can discuss it before going to the trouble of creating a pull request.