Class: HotCell::Cell
- Inherits:
-
Object
- Object
- HotCell::Cell
- Defined in:
- lib/hot_cell/cell.rb
Overview
One registered cell: where its sockets are, how long this application will wait, and which of its own exception classes to raise for each side of the permanent split.
Both socket paths are derived from one directory, so the volume mounts are mechanical rather than something to remember and the two sockets cannot end up apart.
Instance Attribute Summary collapse
-
#control_timeout ⇒ Object
readonly
Returns the value of attribute control_timeout.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#permanent ⇒ Object
readonly
Returns the value of attribute permanent.
-
#timeout ⇒ Object
readonly
Returns the value of attribute timeout.
-
#transient ⇒ Object
readonly
Returns the value of attribute transient.
-
#transport ⇒ Object
readonly
Returns the value of attribute transport.
Instance Method Summary collapse
- #control_socket ⇒ Object
-
#describe ⇒ Object
Static, and called once at boot.
-
#directory ⇒ Object
Resolved on every call rather than at registration, which is what makes turning a path on a configuration change instead of a release.
-
#enabled? ⇒ Boolean
Unset means this path is off, and the caller runs in process exactly as it did before.
- #exception_for(failure) ⇒ Object
-
#initialize(name, dir: nil, timeout: 30, control_timeout: 5, permanent: PermanentFailure, transient: TransientFailure, on_contract_skew: nil, transport: Transport::Socket.new) ⇒ Cell
constructor
timeoutcovers work, so it is sized to clear the cell'sanswer_withinand a saturated cell reports its own verdict rather than a transport failure. - #metrics ⇒ Object
-
#report_contract_skew(error) ⇒ Object
Contract skew needs its own reporting hook because applications rescue broadly around representations, so "raise" is indistinguishable from "placeholder" and the skew is otherwise invisible.
- #work_socket ⇒ Object
Constructor Details
#initialize(name, dir: nil, timeout: 30, control_timeout: 5, permanent: PermanentFailure, transient: TransientFailure, on_contract_skew: nil, transport: Transport::Socket.new) ⇒ Cell
timeout covers work, so it is sized to clear the cell's answer_within and a saturated cell reports
its own verdict rather than a transport failure. control_timeout covers describe and metrics,
which the supervisor answers inline with no fork or queue — so it is short on purpose. Sharing one
number would give the call whose job is to say "this cell is down" the patience of a video transcode.
Both bound the answer rather than the whole call: connecting is not covered. Transport::Socket says why that is left alone.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/hot_cell/cell.rb', line 19 def initialize(name, dir: nil, timeout: 30, control_timeout: 5, permanent: PermanentFailure, transient: TransientFailure, on_contract_skew: nil, transport: Transport::Socket.new) @name = name.to_s @dir = dir @timeout = timeout @control_timeout = control_timeout @permanent = permanent @transient = transient @on_contract_skew = on_contract_skew @transport = transport verify_bounds! verify_classification! end |
Instance Attribute Details
#control_timeout ⇒ Object (readonly)
Returns the value of attribute control_timeout.
10 11 12 |
# File 'lib/hot_cell/cell.rb', line 10 def control_timeout @control_timeout end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
10 11 12 |
# File 'lib/hot_cell/cell.rb', line 10 def name @name end |
#permanent ⇒ Object (readonly)
Returns the value of attribute permanent.
10 11 12 |
# File 'lib/hot_cell/cell.rb', line 10 def permanent @permanent end |
#timeout ⇒ Object (readonly)
Returns the value of attribute timeout.
10 11 12 |
# File 'lib/hot_cell/cell.rb', line 10 def timeout @timeout end |
#transient ⇒ Object (readonly)
Returns the value of attribute transient.
10 11 12 |
# File 'lib/hot_cell/cell.rb', line 10 def transient @transient end |
#transport ⇒ Object (readonly)
Returns the value of attribute transport.
10 11 12 |
# File 'lib/hot_cell/cell.rb', line 10 def transport @transport end |
Instance Method Details
#control_socket ⇒ Object
53 54 55 |
# File 'lib/hot_cell/cell.rb', line 53 def control_socket File.join directory, "control.sock" end |
#describe ⇒ Object
Static, and called once at boot. 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.
Boot must not fail when a cell does not answer. A cell that is down at app boot is a degraded deployment rather than a broken one, and an application that refuses to start because its thumbnail cell is restarting is worse than one that serves placeholders. So this warns and carries on.
Nor when a cell answers something this client cannot read. The three warnings below reach into the
description without checking types, so a cell that sends the wrong ones raises here — and the README
calls describe_cells from after_initialize, where that is not a failed check but an application
that does not boot. The process that wrote the description is the one that runs untrusted content.
So rescue: returning nil is what an unreachable cell already returns, and every caller handles it.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/hot_cell/cell.rb', line 81 def describe return nil unless enabled? response = control(DESCRIBE) return warn_unreachable(response.failure) unless response.ok? response.result.tap do |described| warn_about_timeout described warn_about_missing_operations described warn_about_group_skew described end rescue StandardError => error HotCell.logger.warn "hotcell #{name}: this cell's description could not be read and is being " \ "ignored (#{error.class}: #{Failure.one_line error.}). Boot continues; " \ "nothing it carries is assumed." nil end |
#directory ⇒ Object
Resolved on every call rather than at registration, which is what makes turning a path on a configuration change instead of a release. A directory consulted once in HotCell.register would make every flip a deploy, and reverting one too.
38 39 40 41 42 |
# File 'lib/hot_cell/cell.rb', line 38 def directory return @dir.call if @dir.respond_to?(:call) @dir || (HotCell.root && File.join(HotCell.root, name)) end |
#enabled? ⇒ Boolean
Unset means this path is off, and the caller runs in process exactly as it did before.
45 46 47 |
# File 'lib/hot_cell/cell.rb', line 45 def enabled? !directory.nil? end |
#exception_for(failure) ⇒ Object
57 58 59 |
# File 'lib/hot_cell/cell.rb', line 57 def exception_for(failure) failure.permanent? ? permanent : transient end |
#metrics ⇒ Object
99 100 101 |
# File 'lib/hot_cell/cell.rb', line 99 def metrics enabled? ? control(METRICS) : nil end |
#report_contract_skew(error) ⇒ Object
Contract skew needs its own reporting hook because applications rescue broadly around representations, so "raise" is indistinguishable from "placeholder" and the skew is otherwise invisible. An application running several clients against several independently-booted cells needs to know which one skewed.
65 66 67 |
# File 'lib/hot_cell/cell.rb', line 65 def report_contract_skew(error) @on_contract_skew&.call error, self end |
#work_socket ⇒ Object
49 50 51 |
# File 'lib/hot_cell/cell.rb', line 49 def work_socket File.join directory, "work.sock" end |