Class: Quicsilver::Client::ConnectionPool

Inherits:
Object
  • Object
show all
Defined in:
lib/quicsilver/client/connection_pool.rb

Overview

Thread-safe pool of connected Client instances, keyed by (host, port). Idle connections are reused automatically. Stale ones are evicted at checkout.

Quicsilver::Client.get("example.com", 4433, "/users")

Constant Summary collapse

DEFAULT_MAX_SIZE =
4
DEFAULT_IDLE_TIMEOUT =

seconds

60

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_size: DEFAULT_MAX_SIZE, idle_timeout: DEFAULT_IDLE_TIMEOUT) ⇒ ConnectionPool

Returns a new instance of ConnectionPool.



16
17
18
19
20
21
# File 'lib/quicsilver/client/connection_pool.rb', line 16

def initialize(max_size: DEFAULT_MAX_SIZE, idle_timeout: DEFAULT_IDLE_TIMEOUT)
  @max_size = max_size
  @idle_timeout = idle_timeout
  @pools = {} # "host:port" => [{ client:, checked_out: }]
  @mutex = Mutex.new
end

Instance Attribute Details

#idle_timeoutObject (readonly)

Returns the value of attribute idle_timeout.



11
12
13
# File 'lib/quicsilver/client/connection_pool.rb', line 11

def idle_timeout
  @idle_timeout
end

#max_sizeObject (readonly)

Returns the value of attribute max_size.



11
12
13
# File 'lib/quicsilver/client/connection_pool.rb', line 11

def max_size
  @max_size
end

Instance Method Details

#checkin(client) ⇒ Object

Return a Client to the pool.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/quicsilver/client/connection_pool.rb', line 63

def checkin(client)
  key = "#{client.hostname}:#{client.port}"

  @mutex.synchronize do
    entries = @pools[key]
    return unless entries

    entry = entries.find { |e| e[:client].equal?(client) }
    return unless entry

    if client.connected?
      entry[:checked_out] = false
      entry[:last_used] = Time.now
    else
      entries.delete(entry)
      client.close_connection
      @pools.delete(key) if entries.empty?
    end
  end
end

#checkout(hostname, port, **options) ⇒ Object

Check out a connected Client. Reuses an idle one or creates a new one.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/quicsilver/client/connection_pool.rb', line 24

def checkout(hostname, port, **options)
  key = "#{hostname}:#{port}"

  @mutex.synchronize do
    entries = @pools[key] ||= []

    # Evict dead/stale/draining
    entries.reject! do |e|
      if !e[:checked_out] && (!e[:client].connected? || e[:client].draining? || e[:last_used] < Time.now - @idle_timeout)
        e[:client].close_connection
        true
      end
    end

    # Reuse an idle client (skip draining ones)
    idle = entries.find { |e| !e[:checked_out] && e[:client].connected? && !e[:client].draining? }
    if idle
      idle[:checked_out] = true
      idle[:last_used] = Time.now
      return idle[:client]
    end

    if entries.size >= @max_size
      raise ConnectionError, "Connection pool full for #{key} (max: #{@max_size})"
    end
  end

  # Create outside the lock (blocking I/O)
  client = Client.new(hostname, port, **options)
  client.open_connection

  @mutex.synchronize do
    (@pools[key] ||= []) << { client: client, checked_out: true, last_used: Time.now }
  end

  client
end

#closeObject

Close all clients.



85
86
87
88
89
90
91
92
# File 'lib/quicsilver/client/connection_pool.rb', line 85

def close
  @mutex.synchronize do
    @pools.each_value do |entries|
      entries.each { |e| e[:client].close_connection }
    end
    @pools.clear
  end
end

#size(host = nil, port = nil) ⇒ Object

Total clients in the pool, optionally filtered by host:port.



95
96
97
98
99
100
101
102
103
# File 'lib/quicsilver/client/connection_pool.rb', line 95

def size(host = nil, port = nil)
  @mutex.synchronize do
    if host && port
      (@pools["#{host}:#{port}"] || []).size
    else
      @pools.values.sum(&:size)
    end
  end
end