Class: LeanPool::Pool
- Inherits:
-
Object
- Object
- LeanPool::Pool
- Defined in:
- lib/lean_pool/pool.rb
Overview
A lightweight, process-free resource pool implementation.
Pool manages a collection of resources (connections, sockets, file handles, etc.) without creating per-resource processes. It provides thread-safe access to resources with automatic lifecycle management, timeout handling, and resource reuse.
Key features:
- Thread-safe resource access using concurrent-ruby
- Lazy or eager resource initialization
- Automatic resource cleanup and removal
- Configurable timeouts for checkout operations
- Resource state tracking and statistics
- Graceful shutdown and reload capabilities
The pool maintains a fixed maximum number of resources and automatically manages
their lifecycle. Resources can be marked for removal by returning a hash with
remove: true from the checkout block.
Instance Method Summary collapse
-
#checkout(timeout: nil) {|resource| ... } ⇒ Object
Checkout a resource from the pool and execute a block with it.
-
#checkout!(timeout: nil) {|resource| ... } ⇒ Object
Checkout a resource, raising on error (alias for checkout).
-
#initialize(size: 5, timeout: 5.0, lazy: true, pool_state: nil) {|pool_state| ... } ⇒ Pool
constructor
Initialize a new resource pool.
-
#reload(&cleanup) {|resource| ... } ⇒ void
Reload the pool by shutting down and reinitializing.
-
#shutdown(&cleanup) {|resource| ... } ⇒ void
Shutdown the pool gracefully.
-
#stats ⇒ Hash
Get current pool statistics.
Constructor Details
#initialize(size: 5, timeout: 5.0, lazy: true, pool_state: nil) {|pool_state| ... } ⇒ Pool
Initialize a new resource pool.
Creates a new pool with the specified configuration. Resources can be initialized eagerly (all at once) or lazily (on-demand when needed).
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/lean_pool/pool.rb', line 108 def initialize(size: 5, timeout: 5.0, lazy: true, pool_state: nil, &block) raise ArgumentError, "size must be greater than 0" if size <= 0 raise ArgumentError, "timeout must be positive" if timeout <= 0 raise ArgumentError, "initializer block required" unless block_given? @size = size @timeout = timeout @lazy = lazy @pool_state = pool_state @initializer = block @shutdown = Concurrent::AtomicBoolean.new(false) # Thread-safe collections @available = Concurrent::Array.new @in_use = Concurrent::Hash.new @mutex = Concurrent::ReentrantReadWriteLock.new # Initialize resources if not lazy initialize_resources unless lazy end |
Instance Method Details
#checkout(timeout: nil) {|resource| ... } ⇒ Object
Checkout a resource from the pool and execute a block with it.
Retrieves an available resource from the pool (or creates a new one if the pool is not full and lazy initialization is enabled), executes the provided block with the resource, and then returns the resource to the pool (or removes it if indicated by the block's return value).
If the pool is full and no resources are available, this method will wait up to the specified timeout for a resource to become available.
The resource is automatically returned to the pool after the block executes,
unless the block returns a hash with remove: true or error: true, in which
case the resource is removed from the pool.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/lean_pool/pool.rb', line 173 def checkout(timeout: nil, &block) raise ShutdownError, "Pool is shutdown" if @shutdown.true? raise ArgumentError, "block required" unless block_given? timeout ||= @timeout deadline = Time.now + timeout resource = nil thread_id = Thread.current.object_id begin # Try to get an available resource @mutex.with_write_lock do resource = @available.pop # Create new resource if none available and pool not full if resource.nil? && @in_use.size < @size resource = create_resource end end # Wait for resource if pool is full while resource.nil? && Time.now < deadline sleep(0.01) # Small sleep to avoid busy waiting @mutex.with_write_lock do resource = @available.pop # Try to create new resource if none available and pool not full if resource.nil? && @in_use.size < @size resource = create_resource end end end raise TimeoutError, "Timeout waiting for resource" if resource.nil? # Mark resource as in use @mutex.with_write_lock do @in_use[thread_id] = resource end # Execute block with resource result = block.call(resource) # Check resource state and return to pool or remove checkin_resource(resource, result) result rescue => e # Remove resource if error occurred discard_resource(resource) if resource raise ensure # Always remove from in_use @mutex.with_write_lock do @in_use.delete(thread_id) end end end |
#checkout!(timeout: nil) {|resource| ... } ⇒ Object
Checkout a resource, raising on error (alias for checkout).
This is an alias for #checkout that provides a more explicit name for operations that should raise exceptions on errors.
245 246 247 |
# File 'lib/lean_pool/pool.rb', line 245 def checkout!(timeout: nil, &block) checkout(timeout: timeout, &block) end |
#reload(&cleanup) {|resource| ... } ⇒ void
This method returns an undefined value.
Reload the pool by shutting down and reinitializing.
Shuts down the pool (cleaning up all existing resources), then re-enables
the pool for new checkouts. If the pool was initialized with lazy: false,
resources will be recreated immediately. Otherwise, resources will be created
on-demand as before.
This is useful after forking processes or when you need to reset the pool state without creating a new pool instance.
345 346 347 348 349 |
# File 'lib/lean_pool/pool.rb', line 345 def reload(&cleanup) shutdown(&cleanup) @shutdown.make_false initialize_resources unless @lazy end |
#shutdown(&cleanup) {|resource| ... } ⇒ void
Cleanup errors are silently ignored to ensure all resources are processed.
This method returns an undefined value.
Shutdown the pool gracefully.
Marks the pool as shutdown, preventing new checkouts, and cleans up all resources. The method will wait for in-use resources to be returned (up to the pool's timeout), then force cleanup of any remaining resources.
After shutdown, all subsequent checkout attempts will raise ShutdownError.
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/lean_pool/pool.rb', line 302 def shutdown(&cleanup) @shutdown.make_true @mutex.with_write_lock do # Cleanup all available resources while resource = @available.pop cleanup_resource(resource, cleanup) end # Wait for in-use resources (with timeout) deadline = Time.now + @timeout while !@in_use.empty? && Time.now < deadline sleep(0.1) end # Force cleanup remaining in-use resources @in_use.each_value do |resource| cleanup_resource(resource, cleanup) end @in_use.clear end end |
#stats ⇒ Hash
This method is thread-safe and uses a read lock for minimal contention.
Get current pool statistics.
Returns a hash containing information about the current state of the pool, including the maximum size, number of available resources, number of resources currently in use, and total number of resources.
266 267 268 269 270 271 272 273 274 275 |
# File 'lib/lean_pool/pool.rb', line 266 def stats @mutex.with_read_lock do { size: @size, available: @available.size, in_use: @in_use.size, total: @available.size + @in_use.size } end end |