Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

fibered connection pool spec

  • Loading branch information...
commit dfcf4775e227cb24a3a1d529689e374edcf858e8 1 parent 7b90d4c
@humanzz authored
View
40 lib/never_block/pool/fibered_connection_pool.rb
@@ -25,19 +25,28 @@ module Pool
# end
# 32.times do
# Fiber.new do
- # conn = pool.hold # hold will pause the fiber until a connection is available
- # conn.execute('something') # you can use the connection normally now
+ # # acquire a connection from the pool
+ # pool.hold do |conn|
+ # conn.execute('something') # you can use the connection normally now
+ # end
# end.resume
# end
#
- # The pool has support for transactions, just pass true to the pool#hold method
- # and the connection will not be released after the block is finished
+ # The pool has support for transactions, just pass true to the
+ # pool#hold method and the connection will not be released after the block
+ # is finished
# It is the responsibility of client code to release the connection
class FiberedConnectionPool
-
- # initialize the connection pool
- # using the supplied proc to create the connections
- # you can choose to start them eagerly or lazily (lazy by default)
+
+ attr_reader :size
+
+ # initialize the connection pool using the supplied proc to create
+ # the connections
+ # You can choose to start them eagerly or lazily (lazy by default)
+ # Available options are
+ # :size => the maximum number of connections to be created in the pool
+ # :eager => (true|false) indicates whether connections should be
+ # created initially or when need
def initialize(options = {}, &block)
@connections, @busy_connections, @queue = [], {},[]
@connection_proc = block
@@ -45,16 +54,13 @@ def initialize(options = {}, &block)
if options[:eager]
@size.times do
@connections << @connection_proc.call
- end
+ end
end
end
- # If a connection is available,
- # pass it to the block, otherwise
- # pass the fiber to the queue
- # till a connection is available
- # when done with a connection
- # try to porcess other fibers in the queue
+ # If a connection is available, pass it to the block, otherwise pass
+ # the fiber to the queue till a connection is available
+ # when done with a connection try to porcess other fibers in the queue
# before releasing the connection
# if inside a transaction, don't release the fiber
def hold(transactional = false)
@@ -67,7 +73,7 @@ def hold(transactional = false)
yield conn
ensure
release(fiber, conn) unless transactional
- process_queue
+ process_queue
end
end
@@ -106,7 +112,7 @@ def process_queue
fiber = @queue.shift
# What is really happening here?
# we are resuming a fiber from within
- # another, should we call transfer insted?
+ # another, should we call transfer instead?
fiber.resume @busy_connections[fiber] = @connections.shift
end
end
View
106 spec/fibered_connection_pool_spec.rb
@@ -0,0 +1,106 @@
+$:.unshift File.expand_path('..')
+require 'lib/neverblock'
+
+class MockConnection; end
+
+describe NB::Pool::FiberedConnectionPool do
+ before(:each) do
+ @pool = NB::Pool::FiberedConnectionPool.new(:size => 10) do
+ MockConnection.new
+ end
+ end
+
+ it "should create all connections lazily by default" do
+ @pool.instance_variable_get(:@connections).length.should == 0
+ @pool.instance_variable_get(:@busy_connections).length.should == 0
+ end
+
+ it "should create all connections eagerly if specified" do
+ @pool = NB::Pool::FiberedConnectionPool.new(:size => 10, :eager => true) do
+ MockConnection.new
+ end
+ @pool.instance_variable_get(:@connections).length.should == 10
+ @pool.instance_variable_get(:@busy_connections).length.should == 0
+ end
+
+ it "should create and yield a connection if :size not reached" do
+ @pool.instance_variable_get(:@connections).length.should == 0
+ @pool.instance_variable_get(:@busy_connections).length.should == 0
+
+ @pool.hold {|conn| conn.should be_instance_of(MockConnection)}
+
+ @pool.instance_variable_get(:@connections).length.should == 1
+ @pool.instance_variable_get(:@busy_connections).length.should == 0
+ end
+
+ it "should create connections up to :size and queue other requests" do
+ # prepate the fiber pool
+ fpool = NB::Pool::FiberPool.new(15)
+ fibers = []; fpool.fibers.each {|f| fibers << f}
+ progress = Array.new(15, false)
+
+ # send 15 requests to the connection pool (of size 10)
+ 10.times do |i|
+ fpool.spawn do
+ @pool.hold do |conn|
+ Fiber.yield
+ progress[i] = Fiber.current #mark task finished
+ end
+ end
+ end
+ (10..14).each do |i|
+ fpool.spawn do
+ @pool.hold do |conn|
+ progress[i] = Fiber.current #mark task finished
+ end
+ end
+ end
+
+ # 10 requests should be in progress and 5 should be queued
+ @pool.instance_variable_get(:@connections).length.should == 0
+ @pool.instance_variable_get(:@busy_connections).length.should == 10
+ @pool.instance_variable_get(:@queue).length.should == 5
+
+ #resume first request which will finish it and will also handle the
+ #queued requests
+ fibers[0].resume
+ [0,*10..14].each {|i| fibers[i].should == progress[i]}
+ [*1..9].each do |i|
+ progress[i].should == false
+ fibers[i].resume
+ progress[i].should == fibers[i]
+ end
+ end
+
+ it "should use the same connection in a transaction" do
+ #make sure there are more than one connection in the pool
+ @pool = NB::Pool::FiberedConnectionPool.new(:size => 10, :eager => true) do
+ MockConnection.new
+ end
+ fpool = NB::Pool::FiberPool.new(12)
+ fibers = []; fpool.fibers.each {|f| fibers << f}
+ t_conn = nil
+ fpool.spawn do
+ #announce the beginning of a transaction
+ @pool.hold(true) {|conn| t_conn = conn}
+
+ #another call to hold should get the same transaction's connection
+ @pool.hold {|conn| t_conn.should == conn}
+
+ #release the transaction connection
+ @pool.hold do |conn|
+ t_conn.should == conn
+ @pool.release(Fiber.current, conn)
+ end
+
+ #will now get a connection other than the transation's one (since there
+ #are many connections. If there was only one then it would have been
+ #returned anyways)
+ @pool.hold {|conn| t_conn.should_not == conn}
+ end
+ end
+
+ after(:each) do
+ @pool = nil
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.