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(size: 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
Reduce the created count
# File lib/connection_pool/timed_stack.rb, line 143 def decrement_created @created -= 1 unless @created == 0 end
Returns true if there are no available connections.
# File lib/connection_pool/timed_stack.rb, line 125 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 137 def idle @que.length end
The number of connections available on the stack.
# File lib/connection_pool/timed_stack.rb, line 131 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.
@option options [Float] :timeout (0.5) Wait this many seconds for an available entry @option options [Class] :exception (ConnectionPool::TimeoutError) Exception class to raise
if an entry was not available within the timeout period. Use `exception: false` to return nil.
Other options may be used by subclasses that extend TimedStack.
# File lib/connection_pool/timed_stack.rb, line 62 def pop(timeout: 0.5, exception: ConnectionPool::TimeoutError, **) deadline = current_time + timeout @mutex.synchronize do loop do raise ConnectionPool::PoolShuttingDownError if @shutdown_block if (conn = try_fetch_connection(**)) return conn end connection = try_create(**) return connection if connection to_wait = deadline - current_time if to_wait <= 0 if exception raise exception, "Waited #{timeout} sec, #{length}/#{@max} available" else return nil end end @resource.wait(@mutex, to_wait) end end end
Returns obj to the stack. Additional kwargs are ignored in TimedStack but may be used by subclasses that extend TimedStack.
# File lib/connection_pool/timed_stack.rb, line 38 def push(obj, **) @mutex.synchronize do if @shutdown_block @created -= 1 unless @created == 0 @shutdown_block.call(obj) else store_connection obj, ** end @resource.broadcast end end
Reaps connections that were checked in more than idle_seconds ago.
# File lib/connection_pool/timed_stack.rb, line 106 def reap(idle_seconds:) raise ArgumentError, "reap must receive a block" unless block_given? raise ArgumentError, "idle_seconds must be a number" unless idle_seconds.is_a?(Numeric) raise ConnectionPool::PoolShuttingDownError if @shutdown_block count = idle count.times do conn = @mutex.synchronize do raise ConnectionPool::PoolShuttingDownError if @shutdown_block reserve_idle_connection(idle_seconds) end break unless conn yield 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 92 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 167 def connection_stored?(**) !@que.empty? end
# File lib/connection_pool/timed_stack.rb, line 149 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 175 def fetch_connection(**) @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 209 def idle_connections?(idle_seconds) return unless connection_stored? # Most idle will be at the head so `first` age = (current_time - @que.first.last) age > 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 195 def reserve_idle_connection(idle_seconds) return unless idle_connections?(idle_seconds) @created -= 1 unless @created == 0 # Most active elements are at the tail of the array. # Most idle will be at the head so `shift` rather than `pop`. @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 183 def shutdown_connections(**) while (conn = try_fetch_connection(**)) @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 220 def store_connection(obj, **) @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 229 def try_create(**) 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 159 def try_fetch_connection(**) connection_stored?(**) && fetch_connection(**) end