fiber_connection_pool
Fiber-based generic connection pool
A connection pool meant to be used inside a Fiber-based reactor, such as any EventMachine or Celluloid server.
Widely based on ConnectionPool
from em-synchrony gem, and
some things borrowed also from
threaded connection_pool gem.
Used in production environments
with Goliath
(EventMachine based) servers,
and in promising experiments with
Reel
(Celluloid based) servers.
Install
Add this line to your application's Gemfile:
gem 'fiber_connection_pool'
Or install it yourself as:
$ gem install fiber_connection_pool
Inside of your Ruby program, require FiberConnectionPool with:
require 'fiber_connection_pool'
How It Works
pool = FiberConnectionPool.new(:size => 5){ MyFancyConnection.new }
It just keeps an array (the internal pool) holding the result of running
the given block size times. Inside the reactor loop (either EventMachine's or Celluloid's),
each request is wrapped on a Fiber, and then pool plays its magic.
When a method query_me is called on pool and it's not one of its own methods,
then it:
- reserves one connection from the internal pool and associates it with the current Fiber
- if no connection is available, then that Fiber stays on a pending queue, and is yielded
- when a connection is available, then the pool calls
query_meon thatMyFancyConnectioninstance - when
query_mereturns, the reserved instance is released again, and the next Fiber on the pending queue is resumed - the return value is sent back to the caller
Methods from MyFancyConnection instance should yield the fiber before
perform any blocking IO. That returns control to te underlying reactor,
that spawns another fiber to process the next request, while the previous
one is still waiting for the IO response. That new fiber will get its own
connection from the pool, or else it will yield until there
is one available.
The whole process looks synchronous from the Fiber perspective, because it is. The Fiber will really block ( yield ) until it gets the result.
results = pool.query_me(sql)
puts "I waited for this: #{results}"
The magic resides on the fact that other fibers are being processed while this one is waiting.
Not thread-safe
FiberConnectionPool is not thread-safe right now. You will not be able to use it
from different threads, as eventually it will try to resume a Fiber that resides
on a different Thread. That will raise a FiberError( "calling a fiber across threads" ).
Maybe one day we add that feature too.
We have tested it on Goliath servers having one pool on each server instance, and on Reel servers
having one pool on each Actor thread. Take a look at the examples folder for details.
MySQL specific
By now we have only thought and tested it to be used with MySQL connections.
For EventMachine by using Mysql2::EM::Client from em-synchrony.
And for Celluloid by using a patched version of ruby-mysql.
We plan on removing any MySQL specific code, so it becomes completely generic. Does not seem so hard to achieve.
Reacting to connection failure
When the call to a method raises an Exception it will raise as if there was no pool between your code and the connetion itself. You can rescue the Exception as usual and react as you would do normally.
You have to be aware that the connection instance will remain in the pool, and other fibers
will surely use it. If the Exception you rescued indicates that the connection should be
recreated, you can call recreate_connection passing it a new instance. The instance that
just failed will be replaced inside the pool by the brand new connection.
Supported Platforms
Used in production environments on Ruby 1.9.3 and 2.0.0. Tested against Ruby 1.9.3, 2.0.0, and rbx-19mode (See details..).
TODOS
- no MySQL-specific code
- better testing
- improve reaction to failure
- better in-code docs
- make thread-safe

