Class: HotCell::Supervisor::Child
- Inherits:
-
Struct
- Object
- Struct
- HotCell::Supervisor::Child
- Defined in:
- lib/hot_cell/supervisor.rb
Overview
Owns "is this worker busy" and the two transitions that change the answer, because the supervisor asking
busy? and the supervisor assigning the four fields busy? is computed from are the same fact. Spread
across the caller, a new field is one the next transition forgets to clear.
Instance Attribute Summary collapse
-
#buffer ⇒ Object
Returns the value of attribute buffer.
-
#connection ⇒ Object
Returns the value of attribute connection.
-
#control ⇒ Object
Returns the value of attribute control.
-
#deadline ⇒ Object
Returns the value of attribute deadline.
-
#dispatched_at ⇒ Object
Returns the value of attribute dispatched_at.
-
#killed_for ⇒ Object
Returns the value of attribute killed_for.
-
#pid ⇒ Object
Returns the value of attribute pid.
-
#retired_at ⇒ Object
Returns the value of attribute retired_at.
-
#served ⇒ Object
Returns the value of attribute served.
-
#slot ⇒ Object
Returns the value of attribute slot.
Class Method Summary collapse
Instance Method Summary collapse
- #available? ⇒ Boolean
- #busy? ⇒ Boolean
- #dispatched(connection, deadline, at:) ⇒ Object
- #expires_at ⇒ Object
-
#finished ⇒ Object
Not a reset of
deadline: the supervisor re-establishes it on the next dispatch, and leaving the last one readable is what lets a log line after the fact say what this worker was being held to. -
#lingering?(now, grace) ⇒ Boolean
The retirement analogue of
overdue?, and the only timer that can reach a worker whose idle report was early:finishclears whatoverdue?reads, so a worker still running what it reported finished answers to this and nothing else. - #lingers_until(grace) ⇒ Object
-
#overdue?(now) ⇒ Boolean
A child already killed for its deadline stops being a timer, and that guard is load-bearing rather than tidy.
-
#released ⇒ Object
Stop being busy while leaving the connection open, for the one caller that still has to answer on it.
Instance Attribute Details
#buffer ⇒ Object
Returns the value of attribute buffer
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def buffer @buffer end |
#connection ⇒ Object
Returns the value of attribute connection
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def connection @connection end |
#control ⇒ Object
Returns the value of attribute control
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def control @control end |
#deadline ⇒ Object
Returns the value of attribute deadline
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def deadline @deadline end |
#dispatched_at ⇒ Object
Returns the value of attribute dispatched_at
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def dispatched_at @dispatched_at end |
#killed_for ⇒ Object
Returns the value of attribute killed_for
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def killed_for @killed_for end |
#pid ⇒ Object
Returns the value of attribute pid
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def pid @pid end |
#retired_at ⇒ Object
Returns the value of attribute retired_at
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def retired_at @retired_at end |
#served ⇒ Object
Returns the value of attribute served
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def served @served end |
#slot ⇒ Object
Returns the value of attribute slot
23 24 25 |
# File 'lib/hot_cell/supervisor.rb', line 23 def slot @slot end |
Class Method Details
.build(slot:, pid:, control:, deadline:) ⇒ Object
25 26 27 |
# File 'lib/hot_cell/supervisor.rb', line 25 def self.build(slot:, pid:, control:, deadline:) new slot: slot, pid: pid, control: control, deadline: deadline, served: 0, buffer: "".b end |
Instance Method Details
#available? ⇒ Boolean
54 55 56 |
# File 'lib/hot_cell/supervisor.rb', line 54 def available? !busy? && retired_at.nil? end |
#busy? ⇒ Boolean
50 51 52 |
# File 'lib/hot_cell/supervisor.rb', line 50 def busy? !connection.nil? end |
#dispatched(connection, deadline, at:) ⇒ Object
29 30 31 32 33 34 35 |
# File 'lib/hot_cell/supervisor.rb', line 29 def dispatched(connection, deadline, at:) self.connection = connection self.dispatched_at = at self.deadline = deadline self.killed_for = nil self.served += 1 end |
#expires_at ⇒ Object
69 70 71 |
# File 'lib/hot_cell/supervisor.rb', line 69 def expires_at dispatched_at + deadline if busy? && killed_for.nil? end |
#finished ⇒ Object
Not a reset of deadline: the supervisor re-establishes it on the next dispatch, and leaving the last
one readable is what lets a log line after the fact say what this worker was being held to.
39 40 41 42 |
# File 'lib/hot_cell/supervisor.rb', line 39 def finished connection&.close released end |
#lingering?(now, grace) ⇒ Boolean
The retirement analogue of overdue?, and the only timer that can reach a worker whose idle
report was early: finish clears what overdue? reads, so a worker still running what it
reported finished answers to this and nothing else. A retired worker has nothing left to do but
exit — its closed control socket ends its await_dispatch — so the wait for it is bounded where an
available worker's is not. Busy is excluded because a busy retired child is a worker that already
died mid-request, which the reap answers for. killed_for is the same one-kill latch overdue?
reads.
80 81 82 |
# File 'lib/hot_cell/supervisor.rb', line 80 def lingering?(now, grace) !busy? && killed_for.nil? && !retired_at.nil? && now - retired_at >= grace end |
#lingers_until(grace) ⇒ Object
84 85 86 |
# File 'lib/hot_cell/supervisor.rb', line 84 def lingers_until(grace) retired_at + grace if !busy? && killed_for.nil? && !retired_at.nil? end |
#overdue?(now) ⇒ Boolean
A child already killed for its deadline stops being a timer, and that guard is load-bearing rather
than tidy. Killing does not clear busy? — the supervisor still holds the connection it has to answer
on — so without it expires_at stays in the past, wait_for returns 0, IO.select returns at once,
and the loop re-kills and re-logs on every pass until the reap. Measured at a 0.2s deadline: 72
SIGKILLs and 72 synchronous stdout writes for one breach. The window is longest exactly when the host
is already struggling — a worker in uninterruptible sleep, or one tearing down gigabytes of mappings —
and the loop it starves is the one enforcing every other request's deadline.
65 66 67 |
# File 'lib/hot_cell/supervisor.rb', line 65 def overdue?(now) busy? && killed_for.nil? && now - dispatched_at >= deadline end |
#released ⇒ Object
Stop being busy while leaving the connection open, for the one caller that still has to answer on it.
45 46 47 48 |
# File 'lib/hot_cell/supervisor.rb', line 45 def released self.connection = nil self.dispatched_at = nil end |