From 8ec4b4e7c59c6f040a6e18cb7b52d604943c5e9e Mon Sep 17 00:00:00 2001 From: Damian Janowski Date: Wed, 14 Mar 2012 10:07:43 -0300 Subject: [PATCH 1/5] Do pass-through via a specialized class. --- lib/connection_pool.rb | 25 +++++++++++++++++-------- test/test_connection_pool.rb | 4 ++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/connection_pool.rb b/lib/connection_pool.rb index 547f587..4e0037f 100644 --- a/lib/connection_pool.rb +++ b/lib/connection_pool.rb @@ -24,11 +24,15 @@ require 'timed_queue' # - :size - number of connections to pool, defaults to 5 # - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds # -class ConnectionPool < BasicObject +class ConnectionPool DEFAULTS = { :size => 5, :timeout => 5 } + def self.wrap(options, &block) + Wrapper.new(options, &block) + end + def initialize(options={}, &block) - ::Kernel.raise ::ArgumentError, 'Connection pool requires a block' unless block + raise ArgumentError, 'Connection pool requires a block' unless block @available = ::TimedQueue.new @oid = @available.object_id @@ -45,12 +49,6 @@ class ConnectionPool < BasicObject end alias_method :with_connection, :with - def method_missing(name, *args, &block) - checkout.send(name, *args, &block) - ensure - checkin - end - private def checkout @@ -67,4 +65,15 @@ class ConnectionPool < BasicObject nil end + class Wrapper < BasicObject + def initialize(options = {}, &block) + @pool = ::ConnectionPool.new(options, &block) + end + + def method_missing(name, *args, &block) + @pool.with do |connection| + connection.send(name, *args, &block) + end + end + end end diff --git a/test/test_connection_pool.rb b/test/test_connection_pool.rb index 1f88969..88295fe 100644 --- a/test/test_connection_pool.rb +++ b/test/test_connection_pool.rb @@ -50,7 +50,7 @@ class TestConnectionPool < MiniTest::Unit::TestCase end sleep 0.05 assert_raises Timeout::Error do - pool.do_something + pool.with { |net| net.do_something } end sleep 0.05 @@ -60,7 +60,7 @@ class TestConnectionPool < MiniTest::Unit::TestCase end def test_passthru - pool = ConnectionPool.new(:timeout => 0.1, :size => 1) { NetworkConnection.new } + pool = ConnectionPool.wrap(:timeout => 0.1, :size => 1) { NetworkConnection.new } assert_equal 1, pool.do_something assert_equal 2, pool.do_something assert_equal 5, pool.do_something_with_block { 3 } From 26a7262802d209bcdd87c709db3da2fe2977e4e3 Mon Sep 17 00:00:00 2001 From: Damian Janowski Date: Wed, 14 Mar 2012 10:10:09 -0300 Subject: [PATCH 2/5] Update docs for wrapper change. --- lib/connection_pool.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/connection_pool.rb b/lib/connection_pool.rb index 4e0037f..4833ffd 100644 --- a/lib/connection_pool.rb +++ b/lib/connection_pool.rb @@ -12,12 +12,12 @@ require 'timed_queue' # redis.lpop('my-list') if redis.llen('my-list') > 0 # end # -# Example usage replacing a global connection (slower): +# Example usage replacing an existing connection (slower): # -# REDIS = ConnectionPool.new { Redis.new } +# $redis = ConnectionPool.wrap { Redis.new } # # def do_work -# REDIS.lpop('my-list') if REDIS.llen('my-list') > 0 +# $redis.lpop('my-list') if $redis.llen('my-list') > 0 # end # # Accepts the following options: From bff870bf87add73a1f9040dcdd6c2e23e120c0f6 Mon Sep 17 00:00:00 2001 From: Damian Janowski Date: Wed, 14 Mar 2012 10:21:07 -0300 Subject: [PATCH 3/5] Nicer syntax for building TimedQueues. --- lib/connection_pool.rb | 7 ++----- lib/timed_queue.rb | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/connection_pool.rb b/lib/connection_pool.rb index 4833ffd..259e4c2 100644 --- a/lib/connection_pool.rb +++ b/lib/connection_pool.rb @@ -34,12 +34,9 @@ class ConnectionPool def initialize(options={}, &block) raise ArgumentError, 'Connection pool requires a block' unless block - @available = ::TimedQueue.new - @oid = @available.object_id @options = DEFAULTS.merge(options) - @options[:size].times do - @available << block.call - end + @available = ::TimedQueue.new(options[:size], &block) + @oid = @available.object_id end def with(&block) diff --git a/lib/timed_queue.rb b/lib/timed_queue.rb index 442e79b..2bb3e09 100644 --- a/lib/timed_queue.rb +++ b/lib/timed_queue.rb @@ -2,8 +2,8 @@ require 'thread' require 'timeout' class TimedQueue - def initialize - @que = [] + def initialize(size = 0) + @que = Array.new(size) { yield } @mutex = Mutex.new @resource = ConditionVariable.new end From e3cb08c172842dd7935021041013eb51b7b7d79a Mon Sep 17 00:00:00 2001 From: Damian Janowski Date: Wed, 14 Mar 2012 10:24:47 -0300 Subject: [PATCH 4/5] Cache options to instance variables for faster access. Also lets the hash be GC'd. --- lib/connection_pool.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/connection_pool.rb b/lib/connection_pool.rb index 259e4c2..1554533 100644 --- a/lib/connection_pool.rb +++ b/lib/connection_pool.rb @@ -34,8 +34,12 @@ class ConnectionPool def initialize(options={}, &block) raise ArgumentError, 'Connection pool requires a block' unless block - @options = DEFAULTS.merge(options) - @available = ::TimedQueue.new(options[:size], &block) + options = DEFAULTS.merge(options) + + @size = options[:size] || DEFAULTS[:size] + @timeout = options[:timeout] || DEFAULTS + + @available = ::TimedQueue.new(@size, &block) @oid = @available.object_id end @@ -50,7 +54,7 @@ class ConnectionPool def checkout ::Thread.current[:"current-#{@oid}"] ||= begin - @available.timed_pop(@options[:timeout]) + @available.timed_pop(@timeout) end end From 03a92e5b261fbad515a51d21f86955a75c4d3e51 Mon Sep 17 00:00:00 2001 From: Damian Janowski Date: Wed, 14 Mar 2012 10:27:21 -0300 Subject: [PATCH 5/5] Cache the key to Thread.current. --- lib/connection_pool.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/connection_pool.rb b/lib/connection_pool.rb index 1554533..2c50f7d 100644 --- a/lib/connection_pool.rb +++ b/lib/connection_pool.rb @@ -40,7 +40,7 @@ class ConnectionPool @timeout = options[:timeout] || DEFAULTS @available = ::TimedQueue.new(@size, &block) - @oid = @available.object_id + @key = :"current-#{@available.object_id}" end def with(&block) @@ -53,14 +53,12 @@ class ConnectionPool private def checkout - ::Thread.current[:"current-#{@oid}"] ||= begin - @available.timed_pop(@timeout) - end + ::Thread.current[@key] ||= @available.timed_pop(@timeout) end def checkin - conn = ::Thread.current[:"current-#{@oid}"] - ::Thread.current[:"current-#{@oid}"] = nil + conn = ::Thread.current[@key] + ::Thread.current[@key] = nil return unless conn @available << conn nil