Class: Philiprehberger::Pool::ResourcePool

Inherits:
Object
  • Object
show all
Defined in:
lib/philiprehberger/pool.rb

Instance Method Summary collapse

Constructor Details

#initialize(size:, timeout: 5, idle_timeout: nil, health_check: nil, &factory) ⇒ ResourcePool

Returns a new instance of ResourcePool.

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/philiprehberger/pool.rb', line 16

def initialize(size:, timeout: 5, idle_timeout: nil, health_check: nil, &factory)
  raise ArgumentError, 'size must be positive' unless size.positive?
  raise ArgumentError, 'factory block is required' unless factory

  @size = size
  @timeout = timeout
  @idle_timeout = idle_timeout
  @health_check = health_check
  @factory = factory

  @mutex = Mutex.new
  @condition = ConditionVariable.new
  @available = []
  @created = 0
  @in_use = Set.new
  @shutdown = false
end

Instance Method Details

#checkin(resource) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/philiprehberger/pool.rb', line 89

def checkin(resource)
  @mutex.synchronize do
    return unless @in_use.delete?(resource)

    if @shutdown
      destroy_resource(resource)
    else
      @available.push(PoolEntry.new(resource: resource, last_used: Time.now))
      @condition.signal
    end
  end
end

#checkout(timeout: nil) ⇒ Object

Raises:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/philiprehberger/pool.rb', line 45

def checkout(timeout: nil)
  raise ShutdownError, 'pool is shut down' if @shutdown

  effective_timeout = timeout || @timeout
  deadline = Time.now + effective_timeout

  @mutex.synchronize do
    loop do
      raise ShutdownError, 'pool is shut down' if @shutdown

      # Try to get a valid resource from the available pool
      until @available.empty?
        entry = @available.pop

        if idle_expired?(entry)
          destroy_resource(entry.resource)
          next
        end

        if health_check_fails?(entry.resource)
          destroy_resource(entry.resource)
          next
        end

        @in_use.add(entry.resource)
        return entry.resource
      end

      # Create new resource if under capacity
      if @created < @size
        resource = create_resource
        @in_use.add(resource)
        return resource
      end

      # Wait for a resource to become available
      remaining = deadline - Time.now
      raise TimeoutError, "could not obtain resource within #{effective_timeout}s" if remaining <= 0

      @condition.wait(@mutex, remaining)
    end
  end
end

#drainObject



154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/philiprehberger/pool.rb', line 154

def drain
  @mutex.synchronize do
    raise ShutdownError, 'pool is shut down' if @shutdown

    drained = @available.dup
    @available.clear
    @size -= drained.size
    drained.each do |entry|
      entry.resource.close if entry.resource.respond_to?(:close)
    end
    drained.size
  end
end

#prune_idleObject



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/philiprehberger/pool.rb', line 127

def prune_idle
  @mutex.synchronize do
    raise ShutdownError, 'pool is shut down' if @shutdown
    return 0 unless @idle_timeout

    now = Time.now
    expired, kept = @available.partition { |entry| (now - entry.last_used) > @idle_timeout }
    @available = kept
    expired.each { |entry| destroy_resource(entry.resource) }
    expired.size
  end
end

#shutdownObject



140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/philiprehberger/pool.rb', line 140

def shutdown
  @mutex.synchronize do
    @shutdown = true

    @available.each { |entry| destroy_resource(entry.resource) }
    @available.clear

    @in_use.each { |resource| destroy_resource(resource) }
    @in_use.clear

    @condition.broadcast
  end
end

#shutdown?Boolean

Returns:

  • (Boolean)


168
169
170
# File 'lib/philiprehberger/pool.rb', line 168

def shutdown?
  @shutdown
end

#sizeObject



108
109
110
# File 'lib/philiprehberger/pool.rb', line 108

def size
  @mutex.synchronize { @size }
end

#statsObject



102
103
104
105
106
# File 'lib/philiprehberger/pool.rb', line 102

def stats
  @mutex.synchronize do
    { size: @created, available: @available.size, in_use: @in_use.size, max: @size }
  end
end

#utilizationFloat

Fraction of the pool currently in use.

Returns ‘in_use / max` as a `Float` between `0.0` and `1.0`. Useful for dashboards/metrics without needing to unpack `#stats`. Returns `0.0` when the pool has been shut down or has zero capacity.

Returns:

  • (Float)

    fraction of capacity in use (0.0..1.0)



119
120
121
122
123
124
125
# File 'lib/philiprehberger/pool.rb', line 119

def utilization
  @mutex.synchronize do
    return 0.0 if @size <= 0

    @in_use.size.to_f / @size
  end
end

#withObject

Raises:



34
35
36
37
38
39
40
41
42
43
# File 'lib/philiprehberger/pool.rb', line 34

def with
  raise ShutdownError, 'pool is shut down' if @shutdown

  resource = checkout
  begin
    yield resource
  ensure
    checkin(resource)
  end
end