Class: HotCell::Cell

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

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
# 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_classification!
end

Instance Attribute Details

#control_timeoutObject (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

#nameObject (readonly)

Returns the value of attribute name.



10
11
12
# File 'lib/hot_cell/cell.rb', line 10

def name
  @name
end

#permanentObject (readonly)

Returns the value of attribute permanent.



10
11
12
# File 'lib/hot_cell/cell.rb', line 10

def permanent
  @permanent
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



10
11
12
# File 'lib/hot_cell/cell.rb', line 10

def timeout
  @timeout
end

#transientObject (readonly)

Returns the value of attribute transient.



10
11
12
# File 'lib/hot_cell/cell.rb', line 10

def transient
  @transient
end

#transportObject (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_socketObject



52
53
54
# File 'lib/hot_cell/cell.rb', line 52

def control_socket
  File.join directory, "control.sock"
end

#describeObject

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.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/hot_cell/cell.rb', line 74

def describe
  return nil unless enabled?

  response = control(DESCRIBE)
  unless response.ok?
    HotCell.logger.warn "hotcell #{name}: #{unreachable_because response.failure}"
    return nil
  end

  warn_about_timeout response.result
  warn_about_missing_operations response.result
  warn_about_group_skew response.result
  response.result
end

#directoryObject

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.



37
38
39
40
41
# File 'lib/hot_cell/cell.rb', line 37

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.

Returns:

  • (Boolean)


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

def enabled?
  !directory.nil?
end

#exception_for(failure) ⇒ Object



56
57
58
# File 'lib/hot_cell/cell.rb', line 56

def exception_for(failure)
  failure.permanent? ? permanent : transient
end

#metricsObject



89
90
91
# File 'lib/hot_cell/cell.rb', line 89

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.



64
65
66
# File 'lib/hot_cell/cell.rb', line 64

def report_contract_skew(error)
  @on_contract_skew&.call error, self
end

#work_socketObject



48
49
50
# File 'lib/hot_cell/cell.rb', line 48

def work_socket
  File.join directory, "work.sock"
end