Class: Ratomic::LocalPool
- Inherits:
-
Object
- Object
- Ratomic::LocalPool
- Defined in:
- lib/ratomic/local_pool.rb
Overview
LocalPool is implemented in pure Ruby. It is not backed by the Rust native extension used by Counter, Map, and Queue. Its safety comes from Ruby Ractor locality: live resources are created and reused inside the Ractor that owns them.
Design Note
LocalPool originated while investigating Redis clients under Ruby Ractors. The original goal was to reuse Pool, but ownership-transfer semantics proved incompatible with live resources containing internal state.
The resulting architecture became known as the “Inception Pool” design:
LocalPool facade
↓
Ractor-local pool
↓
Live resources
or informally:
Pool
↓
Pool
↓
Resource
The public API intentionally uses the more descriptive name ‘LocalPool`.
A shareable facade over resources that stay local to each Ractor.
LocalPool is intended for live resources such as Redis clients, database connections, sockets, and other objects which must not be moved between Ractors. The facade itself is shareable, but each Ractor lazily creates and owns an independent thread-safe local pool. Threads inside the same Ractor share that local pool; different Ractors never share the live resources.
This is the correct shape for resources with process, socket, connection, or native state. Move work across Ractor boundaries, not live clients.
The factory must be Ractor-shareable because the facade stores it and each Ractor calls it when its local resource pool needs to create a resource. Prefer a small immutable callable object instead of a block when the pool will be used from multiple Ractors.
Instance Method Summary collapse
-
#close ⇒ nil
Close the current Ractor’s local pool, if it has been initialized.
-
#initialize(size: 5, timeout: 1.0, factory: nil, &block) ⇒ LocalPool
constructor
Create a per-Ractor local pool facade.
-
#with {|object| ... } ⇒ Object
Checkout a current-Ractor-owned resource, yield it, then return it to the same Ractor-local pool.
Constructor Details
#initialize(size: 5, timeout: 1.0, factory: nil, &block) ⇒ LocalPool
Create a per-Ractor local pool facade.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/ratomic/local_pool.rb', line 80 def initialize(size: 5, timeout: 1.0, factory: nil, &block) raise ArgumentError, "pool size must be positive" unless size.is_a?(Integer) && size.positive? raise ArgumentError, "pool timeout must be numeric or nil" unless timeout.nil? || timeout.is_a?(Numeric) raise ArgumentError, "pool timeout must be non-negative" if timeout && timeout.negative? raise ArgumentError, "use either factory: or block, not both" if factory && block factory ||= block raise LocalJumpError, "no factory given" unless factory raise ArgumentError, "factory must respond to #call" unless factory.respond_to?(:call) raise ArgumentError, "factory must be Ractor-shareable" unless Ractor.shareable?(factory) @size = size @timeout = timeout&.to_f @factory = factory @storage_key = :"ratomic_local_pool_#{object_id}" freeze Ractor.make_shareable(self) end |
Instance Method Details
#close ⇒ nil
Close the current Ractor’s local pool, if it has been initialized.
Other Ractors own independent local pools and are not affected. Available resources are closed if they respond to #close. Resources currently checked out by threads in this Ractor are closed when returned.
122 123 124 125 126 127 128 129 |
# File 'lib/ratomic/local_pool.rb', line 122 def close pool = Ractor.current[@storage_key] return nil unless pool Ractor.current[@storage_key] = nil pool.close nil end |
#with {|object| ... } ⇒ Object
Checkout a current-Ractor-owned resource, yield it, then return it to the same Ractor-local pool.
No resource is moved between Ractors. The yielded object belongs to the Ractor which called this method.
109 110 111 112 113 |
# File 'lib/ratomic/local_pool.rb', line 109 def with local_pool.with { |object| yield object } rescue Timeout::Error raise Ratomic::Error, "pool checkout timeout" end |