mirror of
https://github.com/mperham/connection_pool
synced 2023-03-27 23:22:21 -04:00
Initial pass at a generic connection pool
This commit is contained in:
commit
035e7519d2
7 changed files with 148 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*.gem
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
pkg/*
|
4
Gemfile
Normal file
4
Gemfile
Normal file
|
@ -0,0 +1,4 @@
|
|||
source "http://rubygems.org"
|
||||
|
||||
# Specify your gem's dependencies in connection_pool.gemspec
|
||||
gemspec
|
2
Rakefile
Normal file
2
Rakefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
require 'bundler'
|
||||
Bundler::GemHelper.install_tasks
|
17
connection_pool.gemspec
Normal file
17
connection_pool.gemspec
Normal file
|
@ -0,0 +1,17 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
require "./lib/connection_pool/version"
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "connection_pool"
|
||||
s.version = ConnectionPool::VERSION
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.authors = ["Mike Perham"]
|
||||
s.email = ["mperham@gmail.com"]
|
||||
s.homepage = ""
|
||||
s.description = s.summary = %q{Generic connection pool for Ruby}
|
||||
|
||||
s.files = `git ls-files`.split("\n")
|
||||
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
||||
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||
s.require_paths = ["lib"]
|
||||
end
|
70
lib/connection_pool.rb
Normal file
70
lib/connection_pool.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
require 'connection_pool/timed_queue'
|
||||
|
||||
# Generic connection pool class for e.g. sharing a limited number of network connections
|
||||
# among many threads. Note: Connections are eager created.
|
||||
#
|
||||
# Example usage with block (faster):
|
||||
#
|
||||
# @pool = ConnectionPool.new { Redis.new }
|
||||
#
|
||||
# @pool.with do |redis|
|
||||
# redis.lpop if redis.llen('my-list') > 0
|
||||
# end
|
||||
#
|
||||
# Example usage replacing a global connection (slower):
|
||||
#
|
||||
# REDIS = ConnectionPool.new { Redis.new }
|
||||
#
|
||||
# def do_work
|
||||
# REDIS.lpop if REDIS.llen('my-list') > 0
|
||||
# end
|
||||
#
|
||||
# Accepts the following options:
|
||||
# - :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
|
||||
DEFAULTS = { :size => 5, :timeout => 5 }
|
||||
|
||||
def initialize(options={})
|
||||
raise ArgumentError, 'Connection pool requires a block' unless block_given?
|
||||
|
||||
@available = TimedQueue.new
|
||||
@options = DEFAULTS.merge(options)
|
||||
@options[:size].times do
|
||||
@available << yield
|
||||
end
|
||||
@busy = []
|
||||
end
|
||||
|
||||
def with(&block)
|
||||
yield checkout
|
||||
ensure
|
||||
checkin
|
||||
end
|
||||
|
||||
def method_missing(name, *args)
|
||||
checkout.send(name, *args)
|
||||
ensure
|
||||
checkin
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def checkout
|
||||
Thread.current[:"current-#{self.object_id}"] ||= begin
|
||||
conn = @available.timed_pop(@options[:timeout])
|
||||
@busy << conn
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
def checkin
|
||||
conn = Thread.current[:"current-#{self.object_id}"]
|
||||
Thread.current[:"current-#{self.object_id}"] = nil
|
||||
@busy.delete(conn)
|
||||
@available << conn
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
48
lib/connection_pool/timed_queue.rb
Normal file
48
lib/connection_pool/timed_queue.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
require 'thread'
|
||||
require 'timeout'
|
||||
|
||||
class TimedQueue
|
||||
def initialize
|
||||
@que = []
|
||||
@waiting = []
|
||||
@mutex = Mutex.new
|
||||
@resource = ConditionVariable.new
|
||||
end
|
||||
|
||||
def push(obj)
|
||||
@mutex.synchronize do
|
||||
@que.push obj
|
||||
@resource.signal
|
||||
end
|
||||
end
|
||||
alias << push
|
||||
|
||||
def timed_pop(timeout=0.5)
|
||||
while true
|
||||
@mutex.synchronize do
|
||||
@waiting.delete(Thread.current)
|
||||
if @que.empty?
|
||||
@waiting.push Thread.current
|
||||
@resource.wait(@mutex, timeout)
|
||||
raise TimeoutError if @que.empty?
|
||||
else
|
||||
retval = @que.shift
|
||||
@resource.signal
|
||||
return retval
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def empty?
|
||||
@que.empty?
|
||||
end
|
||||
|
||||
def clear
|
||||
@que.clear
|
||||
end
|
||||
|
||||
def length
|
||||
@que.length
|
||||
end
|
||||
end
|
3
lib/connection_pool/version.rb
Normal file
3
lib/connection_pool/version.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
module ConnectionPool
|
||||
VERSION = "0.0.1"
|
||||
end
|
Loading…
Reference in a new issue