class ConnectionPool::TimedStack
The TimedStack
manages a pool of homogeneous connections (or any resource you wish to manage). Connections are created lazily up to a given maximum number.
Examples:
ts = TimedStack.new(1) { MyConnection.new } # fetch a connection conn = ts.pop # return a connection ts.push conn conn = ts.pop ts.pop timeout: 5 #=> raises ConnectionPool::TimeoutError after 5 seconds
Attributes
Public Class Methods
Creates a new pool with size
connections that are created from the given block
.
# File lib/connection_pool/timed_stack.rb, line 25 def initialize(size = 0, &block) @create_block = block @created = 0 @que = [] @max = size @mutex = Thread::Mutex.new @resource = Thread::ConditionVariable.new @shutdown_block = nil end
Public Instance Methods
Returns true
if there are no available connections.
# File lib/connection_pool/timed_stack.rb, line 121 def empty? (@created - @que.length) >= @max end
The number of connections created and available on the stack.
# File lib/connection_pool/timed_stack.rb, line 133 def idle @que.length end
The number of connections available on the stack.
# File lib/connection_pool/timed_stack.rb, line 127 def length @max - @created + @que.length end
Retrieves a connection from the stack. If a connection is available it is immediately returned. If no connection is available within the given timeout a ConnectionPool::TimeoutError
is raised.
:timeout
is the only checked entry in options
and is preferred over the timeout
argument (which will be removed in a future release). Other options may be used by subclasses that extend TimedStack
.
# File lib/connection_pool/timed_stack.rb, line 60 def pop(timeout = 0.5, options = {}) options, timeout = timeout, 0.5 if Hash === timeout timeout = options.fetch :timeout, timeout deadline = current_time + timeout @mutex.synchronize do loop do raise ConnectionPool::PoolShuttingDownError if @shutdown_block if (conn = try_fetch_connection(options)) return conn end connection = try_create(options) return connection if connection to_wait = deadline - current_time raise ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" if to_wait <= 0 @resource.wait(@mutex, to_wait) end end end
Returns obj
to the stack. options
is ignored in TimedStack
but may be used by subclasses that extend TimedStack
.
# File lib/connection_pool/timed_stack.rb, line 38 def push(obj, options = {}) @mutex.synchronize do if @shutdown_block @created -= 1 unless @created == 0 @shutdown_block.call(obj) else store_connection obj, options end @resource.broadcast end end
Reaps connections that were checked in more than idle_seconds
ago.
# File lib/connection_pool/timed_stack.rb, line 101 def reap(idle_seconds, &block) raise ArgumentError, "reap must receive a block" unless block raise ArgumentError, "idle_seconds must be a number" unless idle_seconds.is_a?(Numeric) raise ConnectionPool::PoolShuttingDownError if @shutdown_block idle.times do conn = @mutex.synchronize do raise ConnectionPool::PoolShuttingDownError if @shutdown_block reserve_idle_connection(idle_seconds) end break unless conn block.call(conn) end end
Shuts down the TimedStack
by passing each connection to block
and then removing it from the pool. Attempting to checkout a connection after shutdown will raise ConnectionPool::PoolShuttingDownError
unless :reload
is true
.
# File lib/connection_pool/timed_stack.rb, line 87 def shutdown(reload: false, &block) raise ArgumentError, "shutdown must receive a block" unless block @mutex.synchronize do @shutdown_block = block @resource.broadcast shutdown_connections @shutdown_block = nil if reload end end
Private Instance Methods
This is an extension point for TimedStack
and is called with a mutex.
This method must returns true if a connection is available on the stack.
# File lib/connection_pool/timed_stack.rb, line 157 def connection_stored?(options = nil) !@que.empty? end
# File lib/connection_pool/timed_stack.rb, line 139 def current_time Process.clock_gettime(Process::CLOCK_MONOTONIC) end
This is an extension point for TimedStack
and is called with a mutex.
This method must return a connection from the stack.
# File lib/connection_pool/timed_stack.rb, line 165 def fetch_connection(options = nil) @que.pop&.first end
This is an extension point for TimedStack
and is called with a mutex.
Returns true if the first connection in the stack has been idle for more than idle_seconds
# File lib/connection_pool/timed_stack.rb, line 197 def idle_connections?(idle_seconds) connection_stored? && (current_time - @que.first.last > idle_seconds) end
This is an extension point for TimedStack
and is called with a mutex.
This method returns the oldest idle connection if it has been idle for more than idle_seconds. This requires that the stack is kept in order of checked in time (oldest first).
# File lib/connection_pool/timed_stack.rb, line 185 def reserve_idle_connection(idle_seconds) return unless idle_connections?(idle_seconds) @created -= 1 unless @created == 0 @que.shift.first end
This is an extension point for TimedStack
and is called with a mutex.
This method must shut down all connections on the stack.
# File lib/connection_pool/timed_stack.rb, line 173 def shutdown_connections(options = nil) while (conn = try_fetch_connection(options)) @created -= 1 unless @created == 0 @shutdown_block.call(conn) end end
This is an extension point for TimedStack
and is called with a mutex.
This method must return obj
to the stack.
# File lib/connection_pool/timed_stack.rb, line 205 def store_connection(obj, options = nil) @que.push [obj, current_time] end
This is an extension point for TimedStack
and is called with a mutex.
This method must create a connection if and only if the total number of connections allowed has not been met.
# File lib/connection_pool/timed_stack.rb, line 214 def try_create(options = nil) unless @created == @max object = @create_block.call @created += 1 object end end
This is an extension point for TimedStack
and is called with a mutex.
This method must returns a connection from the stack if one exists. Allows subclasses with expensive match/search algorithms to avoid double-handling their stack.
# File lib/connection_pool/timed_stack.rb, line 149 def try_fetch_connection(options = nil) connection_stored?(options) && fetch_connection(options) end