Class: HotCell::Supervisor
- Inherits:
-
Object
- Object
- HotCell::Supervisor
- Defined in:
- lib/hot_cell/supervisor.rb
Overview
Accepts, queues, dispatches, times, kills, reaps, and cleans up. It never evaluates image data.
That last rule is mechanical rather than defensive: libvips starts its thread pool on the first evaluation and that pool does not survive fork, so a supervisor that has touched an image forks workers that deadlock forever. Reading a request line would be harmless — it is a control message from the trusted side on a bounded buffer — but the supervisor does not need to, and staying out of the request is what lets it dispatch a connection whose descriptors are still queued on it.
peeked_op is the exception, on the one path where no worker will read the request.
Dispatching rather than letting workers accept is what makes the rest work. The supervisor needs to own
the accept anyway, for the queue, for queued_ms, and to answer capacity. It also means the supervisor
knows when every worker started its current request, which is what the deadline needs.
Defined Under Namespace
Modules: Timed Classes: Child, Pending, Sweep
Constant Summary collapse
- SUN_PATH_MAX =
A path longer than this fails to bind with an error that does not say so. Darwin allows four fewer bytes than Linux, and control.sock is the longer of the two names, so it overflows first.
RUBY_PLATFORM.include?("darwin") ? 104 : 108
- SOCKETS =
[ "work.sock", "control.sock" ].freeze
- PTRACE_SCOPE =
Request memory is protected by kernel.yama.ptrace_scope >= 1, and nothing else protects it. That is a host sysctl no container flag can supply.
"/proc/sys/kernel/yama/ptrace_scope"- CONTROL_BACKLOG =
Only to bound the list. The channel's whole value is answering when nothing else does, so this is set far above any real scrape rate rather than as a throttle.
64- STDERR_READ_BYTES =
What one pass reads off a worker's fd 2, and how many reads the reap's final drain gets. Sized to clear a full pipe rather than to bound memory —
Child#capture_stderrdoes that — so reading further would only mean a fresher tail. 16 * 1024
- STDERR_FINAL_READS =
8
Instance Attribute Summary collapse
-
#configuration ⇒ Object
readonly
Returns the value of attribute configuration.
-
#counters ⇒ Object
readonly
Returns the value of attribute counters.
-
#directory ⇒ Object
readonly
Returns the value of attribute directory.
-
#log ⇒ Object
readonly
Returns the value of attribute log.
-
#workspace ⇒ Object
readonly
Returns the value of attribute workspace.
Instance Method Summary collapse
- #boot ⇒ Object
-
#initialize(directory:, workspace: nil, development: false, configuration: HotCell.configuration, log: Log.new, ptrace_scope_path: PTRACE_SCOPE) ⇒ Supervisor
constructor
A new instance of Supervisor.
- #run ⇒ Object
Constructor Details
#initialize(directory:, workspace: nil, development: false, configuration: HotCell.configuration, log: Log.new, ptrace_scope_path: PTRACE_SCOPE) ⇒ Supervisor
Returns a new instance of Supervisor.
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/hot_cell/supervisor.rb', line 169 def initialize(directory:, workspace: nil, development: false, configuration: HotCell.configuration, log: Log.new, ptrace_scope_path: PTRACE_SCOPE) @directory = directory @development_tmpdir = own_tmpdir if development @workspace = workspace || File.join(tmpdir, "hotcell-workspace") @configuration = configuration @log = log @ptrace_scope_path = ptrace_scope_path @children = {} @queue = [] @control_pending = [] @counters = Counters.new @stopping = false @sweep = nil @next_sweep_at = Clock.now + configuration.sweep_interval end |
Instance Attribute Details
#configuration ⇒ Object (readonly)
Returns the value of attribute configuration.
167 168 169 |
# File 'lib/hot_cell/supervisor.rb', line 167 def configuration @configuration end |
#counters ⇒ Object (readonly)
Returns the value of attribute counters.
167 168 169 |
# File 'lib/hot_cell/supervisor.rb', line 167 def counters @counters end |
#directory ⇒ Object (readonly)
Returns the value of attribute directory.
167 168 169 |
# File 'lib/hot_cell/supervisor.rb', line 167 def directory @directory end |
#log ⇒ Object (readonly)
Returns the value of attribute log.
167 168 169 |
# File 'lib/hot_cell/supervisor.rb', line 167 def log @log end |
#workspace ⇒ Object (readonly)
Returns the value of attribute workspace.
167 168 169 |
# File 'lib/hot_cell/supervisor.rb', line 167 def workspace @workspace end |
Instance Method Details
#boot ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/hot_cell/supervisor.rb', line 186 def boot verify_socket_paths! verify_limits! verify_ptrace_scope! claim_tmpdir if @development_tmpdir verify_scratches! prepare_directories preload @work = listen "work.sock" @control = listen "control.sock" @control_handler = Control.new(configuration: configuration, counters: counters) trap_signals log.write "cell.boot", pid: Process.pid, directory: directory, tmpdir: tmpdir, operations: Registry.names, configuration: configuration.to_h self end |
#run ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/hot_cell/supervisor.rb', line 204 def run until stopped? readable, = IO.select(sources, nil, nil, wait_for) Array(readable).each { |source| handle source } enforce_deadlines enforce_retirements enforce_sweep_deadline expire_queue expire_control retire_idle if @stopping pump sweep_if_due end ensure shutdown end |