Class: HotCell::Control

Inherits:
Object
  • Object
show all
Defined in:
lib/hot_cell/control.rb

Overview

The two things a cell answers that are not conversions, on control.sock rather than work.sock.

Keeping control off the data path buys three things. It answers when the cell is saturated, where a health check or a metrics scrape sharing the work queue would fail under load and report the same thing as an outage. It gets its own allowance and its own much shorter deadline, which are not the same numbers — a scrape is milliseconds and a conversion is seconds. And the socket a connection arrived on is itself the discriminator, so routing costs nothing and cannot be confused by a payload.

The supervisor answers these itself rather than forking a worker for them, which is a deliberate departure from the original design. Forking would work — a child inherits the counters and could read them without being told — but the whole value of this channel is being available when nothing else is, and a channel that needs a fork to answer is a channel that goes quiet exactly when a fork is what is failing. Neither operation takes a descriptor, touches a tool, or evaluates a byte of image data, so none of the reasons the supervisor stays out of a conversion apply. Reading a bounded control line from the trusted side starts no thread pool and cannot deadlock a later fork.

Instance Method Summary collapse

Constructor Details

#initialize(configuration:, counters:) ⇒ Control

Returns a new instance of Control.



20
21
22
23
# File 'lib/hot_cell/control.rb', line 20

def initialize(configuration:, counters:)
  @configuration = configuration
  @counters = counters
end

Instance Method Details

#answer(line, running:, queued:) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/hot_cell/control.rb', line 25

def answer(line, running:, queued:)
  request = Request.parse(line)

  unless request.current_version?
    return failed("protocol", request.version_mismatch)
  end

  case request.op
  when DESCRIBE then Response.ok(result: describe)
  when METRICS then Response.ok(result: @counters.to_h(running: running, queued: queued))
  else failed "unsupported", "control.sock answers #{CONTROL_OPERATIONS.join(" and ")}, not #{request.op.inspect}"
  end
rescue MessageError => error
  failed "invalid", error.message
end

#describeObject

Static, and called once per registered cell at app boot. It is the cheapest way to catch a client pointed at a cell that does not carry the operation it wants, which is otherwise an unsupported on the first real request, and to catch a client whose own timeout is below what this cell may take.



44
45
46
# File 'lib/hot_cell/control.rb', line 44

def describe
  { v: PROTOCOL_VERSION, operations: Registry.names, groups: groups, **@configuration.to_h }
end