Class: Stud::Pool
- Inherits:
-
Object
- Object
- Stud::Pool
- Defined in:
- lib/stud/pool.rb
Overview
Public: A thread-safe, generic resource pool.
This class is agnostic as to the resources in the pool. You can put database connections, sockets, threads, etc. It’s up to you!
Examples:
pool = Pool.new
pool.add(Sequel.connect("postgres://pg-readonly-1/prod"))
pool.add(Sequel.connect("postgres://pg-readonly-2/prod"))
pool.add(Sequel.connect("postgres://pg-readonly-3/prod"))
pool.fetch # => Returns one of the Sequel::Database values from the pool
Defined Under Namespace
Classes: Error, InvalidAction, NotFound, ResourceBusy
Instance Method Summary collapse
-
#add(resource) ⇒ Object
Public: Add a new resource to this pool.
-
#count ⇒ Object
Public: the count of resources in the pool.
-
#fetch(&default_value_block) ⇒ Object
Public: Fetch an available resource.
-
#initialize(max_size = nil) ⇒ Pool
constructor
Public: initialize a new pool.
-
#release(resource) ⇒ Object
Public: Release this resource back to the pool.
-
#remove(resource) ⇒ Object
Public: Remove a resource from the pool.
-
#sized? ⇒ Boolean
Private: Is this pool size-limited?.
Constructor Details
#initialize(max_size = nil) ⇒ Pool
Public: initialize a new pool.
max_size - if specified, limits the number of resources allowed in the pool.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/stud/pool.rb', line 37 def initialize(max_size=nil) # Available resources @available = Hash.new # Busy resources @busy = Hash.new # The pool lock @lock = Mutex.new # Locks for blocking {#fetch} calls if the pool is full. @full_lock = Mutex.new @full_cv = ConditionVariable.new # Maximum size of this pool. @max_size = max_size end |
Instance Method Details
#add(resource) ⇒ Object
Public: Add a new resource to this pool.
The resource, once added, is assumed to be available for use. That means once you add it, you must not use it unless you receive it from #fetch
resource - the object resource to add to the pool.
Returns nothing
84 85 86 87 88 89 |
# File 'lib/stud/pool.rb', line 84 def add(resource) @lock.synchronize do @available[resource.object_id] = resource end return nil end |
#count ⇒ Object
Public: the count of resources in the pool
Returns the count of resources in the pool.
71 72 73 |
# File 'lib/stud/pool.rb', line 71 def count return (@busy.size + @available.size) end |
#fetch(&default_value_block) ⇒ Object
Public: Fetch an available resource.
If no resource is available, and the pool is not full, the default_value_block will be called and the return value of it used as the resource.
If no resource is availabe, and the pool is full, this call will block until a resource is available.
Returns a resource ready to be used.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/stud/pool.rb', line 101 def fetch(&default_value_block) @lock.synchronize do object_id, resource = @available.shift if !resource.nil? @busy[resource.object_id] = resource return resource end end @full_lock.synchronize do if full? # This should really use a logger. puts "=> Pool is full and nothing available. Waiting for a release..." @full_cv.wait(@full_lock) return fetch(&default_value_block) end end # TODO(sissel): If no block is given, we should block until a resource is # available. # If we get here, no resource is available and the pool is not full. resource = default_value_block.call # Only add the resource if the default_value_block returned one. if !resource.nil? add(resource) return fetch end end |
#release(resource) ⇒ Object
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/stud/pool.rb', line 197 def release(resource) @lock.synchronize do if !include?(resource) raise NotFound, "No resource, #{resource.inspect}, found in pool" end # Release is a no-op if this resource is already available. #return if available?(resource) @busy.delete(resource.object_id) @available[resource.object_id] = resource # Notify any threads waiting on a resource from the pool. @full_lock.synchronize { @full_cv.signal } end end |
#remove(resource) ⇒ Object
Public: Remove a resource from the pool.
This is useful if the resource is no longer useful. For example, if it is a database connection and that connection has failed.
This resource MUST be available and not busy.
Raises Pool::NotFound if no such resource is found. Raises Pool::ResourceBusy if the resource is found but in use.
140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/stud/pool.rb', line 140 def remove(resource) # Find the object by object_id #p [:internal, :busy => @busy, :available => @available] @lock.synchronize do if available?(resource) raise InvalidAction, "This resource must be busy for you to remove " \ "it (ie; it must be fetched from the pool)" end @busy.delete(resource.object_id) end end |
#sized? ⇒ Boolean
Private: Is this pool size-limited?
Returns true if this pool was created with a max_size. False, otherwise.
57 58 59 |
# File 'lib/stud/pool.rb', line 57 def sized? return !@max_size.nil? end |