Class: Apartment::PoolReaper

Inherits:
Object
  • Object
show all
Defined in:
lib/apartment/pool_reaper.rb

Overview

Evicts idle and excess tenant pools on a background timer. Complementary to ActiveRecord’s ConnectionPool::Reaper which handles intra-pool connection reaping — this handles inter-pool (tenant) eviction.

Instance Method Summary collapse

Constructor Details

#initialize(pool_manager:, interval:, idle_timeout:, max_total: nil, default_tenant: nil, shard_key_prefix: nil, on_evict: nil) ⇒ PoolReaper

Returns a new instance of PoolReaper.

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/apartment/pool_reaper.rb', line 11

def initialize(pool_manager:, interval:, idle_timeout:, max_total: nil,
               default_tenant: nil, shard_key_prefix: nil, on_evict: nil)
  raise(ArgumentError, 'interval must be a positive number') unless interval.is_a?(Numeric) && interval.positive?
  unless idle_timeout.is_a?(Numeric) && idle_timeout.positive?
    raise(ArgumentError, 'idle_timeout must be a positive number')
  end
  if max_total && (!max_total.is_a?(Integer) || max_total < 1)
    raise(ArgumentError, 'max_total must be a positive integer or nil')
  end

  @pool_manager = pool_manager
  @interval = interval
  @idle_timeout = idle_timeout
  @max_total = max_total
  @default_tenant = default_tenant
  @shard_key_prefix = shard_key_prefix
  @on_evict = on_evict
  @mutex = Mutex.new
  @timer = nil
end

Instance Method Details

#run_cycleObject

Perform one synchronous eviction pass (idle + LRU). Returns the total number of pools evicted. Called by the background timer and by CLI ‘pool evict`.



52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/apartment/pool_reaper.rb', line 52

def run_cycle
  count = 0
  count += evict_idle
  count += evict_lru if @max_total
  count
rescue Apartment::ApartmentError => e
  warn "[Apartment::PoolReaper] #{e.class}: #{e.message}"
  0
rescue StandardError => e
  warn "[Apartment::PoolReaper] Unexpected error: #{e.class}: #{e.message}"
  warn e.backtrace&.first(5)&.join("\n") if e.backtrace
  0
end

#running?Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/apartment/pool_reaper.rb', line 45

def running?
  @mutex.synchronize { @timer&.running? || false }
end

#startObject



32
33
34
35
36
37
38
39
# File 'lib/apartment/pool_reaper.rb', line 32

def start
  @mutex.synchronize do
    stop_internal
    @timer = Concurrent::TimerTask.new(execution_interval: @interval) { reap }
    @timer.execute
  end
  self
end

#stopObject



41
42
43
# File 'lib/apartment/pool_reaper.rb', line 41

def stop
  @mutex.synchronize { stop_internal }
end