hotcell-client-legacy 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c542ad1f9adfdf8dad3a0c61984107ee91ec79fe55201d88bc08050a0c57ea1
4
- data.tar.gz: 55e9c151471e12b47aafa3a49bf8790fc8610f4de1e04bd5c520c4d1fd5eab7d
3
+ metadata.gz: c69cef47499d5d65909fa3be45dbcc447b56d9f9fec0c5f2bd1fbb65b9b63e88
4
+ data.tar.gz: 4c251a71745d240adc39fb144fe0695baca23a2b65c4a0b005ea1e6209c44182
5
5
  SHA512:
6
- metadata.gz: 7686e7b921b05c0913dac6a24eb95987be455526c4d9439442767faa0732c3db515b49935c664c27578bca799977066e11a32a203a92e6f707ae3956af8ac902
7
- data.tar.gz: e1ae1d57ed45e3620dbd71d98b171f3a3034a3e70ddb190fae0365cf6a9a9f142cca8326bec1cb5f34e4bdcde03fc7ed916a0fe3a024805bf558e6be026b30a9
6
+ metadata.gz: b1efde0d7580dcf53b06db03a8256216de8b5c20085239d5eb2873cfb8668a20e289f21c6892d0cea56986d2c6448ea7552def34896f25c1418896115c71678f
7
+ data.tar.gz: b5aff56e7f0c394496865d9e9700fef4753e13cd69f3a29b8a3fc688e6b16cb43cad76308d73d1b818a53fd92d019c3a8ab6eac1c1b169b5d3789a72a7fd8ccd
data/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # hotcell-client-legacy
2
2
 
3
- Part of [HotCell](https://github.com/basecamp/hotcell). See [docs/client-legacy.md](https://github.com/basecamp/hotcell/blob/master/docs/client-legacy.md).
3
+ Part of [Hot Cell](https://github.com/basecamp/hotcell). See [docs/client-legacy.md](https://github.com/basecamp/hotcell/blob/master/docs/client-legacy.md).
@@ -14,7 +14,7 @@ module HotCell
14
14
  #
15
15
  # Written for Ruby 1.9.3, so it uses no keyword arguments, `&.`, `String#b` or `IO#wait_readable`.
16
16
  class Legacy
17
- VERSION = "1.1.0"
17
+ VERSION = "1.2.0"
18
18
 
19
19
  PROTOCOL_VERSION = 1
20
20
  MAX_RESPONSE_BYTES = 65_536
@@ -29,7 +29,7 @@ module HotCell
29
29
  INPUT_MODE = 0o640
30
30
  OUTPUT_MODE = 0o620
31
31
 
32
- # A cell's verdict on a request that did not succeed, with the readers of hotcell-core's HotCell::Failure.
32
+ # A cell's answer to a request that did not succeed, with the readers of hotcell-core's HotCell::Failure.
33
33
  class Failure
34
34
  attr_reader :code, :cause, :signal, :error_class, :message, :stderr
35
35
 
@@ -56,27 +56,50 @@ module HotCell
56
56
  def to_s
57
57
  [ (code unless code.empty?), cause, error_class, message ].compact.join(": ")
58
58
  end
59
+
60
+ # Included in both failure classes, so `rescue Failure::Exception` catches either and
61
+ # `hot_cell_failure` reads the failure, as with hotcell-client's HotCell::Failure::Exception. The
62
+ # classes share no superclass, so rescuing one by name never catches the other: a permanent failure
63
+ # may be written down against a file forever, and a transient one must be retried.
64
+ module Exception
65
+ attr_reader :hot_cell_response
66
+
67
+ def initialize(response)
68
+ @hot_cell_response = response
69
+ super(response.failure.to_s)
70
+ end
71
+
72
+ def hot_cell_failure
73
+ hot_cell_response.failure
74
+ end
75
+ end
59
76
  end
60
77
 
61
- # Included in both failure classes, so `rescue Verdict` catches either and `hot_cell_failure` reads the
62
- # failure, as with hotcell-client's HotCell::Verdict. The classes share no superclass, so rescuing one by
63
- # name never catches the other: a permanent failure may be written down against a file forever, and a
64
- # transient one must be retried.
65
- module Verdict
66
- attr_reader :hot_cell_failure
78
+ # The outcome of one call, success or failure. `timing` is the cell's own open map, such as
79
+ # `queued_ms` and `perform_ms`, and is empty when the answer carried none. `duration_ms` is the time this
80
+ # client measures across the connect, the send and the read, so `duration_ms` minus `perform_ms` is
81
+ # transport plus queueing.
82
+ class Response
83
+ attr_reader :result, :failure, :timing, :duration_ms
84
+
85
+ def initialize(result, failure, timing, duration_ms)
86
+ @result = result
87
+ @failure = failure
88
+ @timing = timing
89
+ @duration_ms = duration_ms
90
+ end
67
91
 
68
- def initialize(failure)
69
- @hot_cell_failure = failure
70
- super(failure.to_s)
92
+ def ok?
93
+ failure.nil?
71
94
  end
72
95
  end
73
96
 
74
97
  class PermanentFailure < StandardError
75
- include Verdict
98
+ include Failure::Exception
76
99
  end
77
100
 
78
101
  class TransientFailure < StandardError
79
- include Verdict
102
+ include Failure::Exception
80
103
  end
81
104
 
82
105
  class Timeout < StandardError; end
@@ -93,7 +116,7 @@ module HotCell
93
116
  @group = options[:group]
94
117
  end
95
118
 
96
- # Returns the operation's result, a Hash with String keys. Raises PermanentFailure or TransientFailure
119
+ # Returns a Response whose result is a Hash with String keys. Raises PermanentFailure or TransientFailure
97
120
  # according to the `permanent` flag on the cell's answer; a failure with no answer at all is transient.
98
121
  def perform(operation, inputs, outputs, payload = {})
99
122
  inputs = [ inputs ] unless inputs.is_a?(Array)
@@ -104,19 +127,41 @@ module HotCell
104
127
  share inputs, INPUT_MODE
105
128
  share outputs, OUTPUT_MODE
106
129
 
107
- line = JSON.generate("v" => PROTOCOL_VERSION, "op" => operation.to_s, "inputs" => inputs.size,
108
- "outputs" => outputs.size, "payload" => payload) + "\n"
130
+ call socket_path, operation.to_s, inputs, outputs, payload
131
+ end
109
132
 
110
- answer = exchange(line, inputs + outputs)
111
- return answer["result"] if answer["ok"]
133
+ # The cell's static description, from the control socket beside the work socket. Raises as `perform` does.
134
+ def describe
135
+ call control_socket_path, "hotcell.describe", [], [], {}
136
+ end
112
137
 
113
- # Built rather than passed to raise, because Ruby 2.x reads a trailing Hash given to raise as its options
114
- # and takes `cause` out of it.
115
- failure = Failure.new(sanitize(answer["error"]))
116
- raise (failure.permanent? ? PermanentFailure : TransientFailure).new(failure)
138
+ # The cell's counters, from the control socket beside the work socket. Raises as `perform` does.
139
+ def metrics
140
+ call control_socket_path, "hotcell.metrics", [], [], {}
117
141
  end
118
142
 
119
143
  private
144
+ def control_socket_path
145
+ File.join File.dirname(socket_path), "control.sock"
146
+ end
147
+
148
+ def call(path, operation, inputs, outputs, payload)
149
+ line = JSON.generate("v" => PROTOCOL_VERSION, "op" => operation, "inputs" => inputs.size,
150
+ "outputs" => outputs.size, "payload" => payload) + "\n"
151
+
152
+ started = now
153
+ answer = exchange(path, line, inputs + outputs)
154
+ duration_ms = (now - started) * 1000
155
+ timing = answer["timing"].is_a?(Hash) ? answer["timing"] : {}
156
+ return Response.new(answer["result"], nil, timing, duration_ms) if answer["ok"]
157
+
158
+ # Built rather than passed to raise, because Ruby 2.x reads a trailing Hash given to raise as its options
159
+ # and takes `cause` out of it.
160
+ failure = Failure.new(sanitize(answer["error"]))
161
+ response = Response.new(nil, failure, timing, duration_ms)
162
+ raise (failure.permanent? ? PermanentFailure : TransientFailure).new(response)
163
+ end
164
+
120
165
  def share(ios, mode)
121
166
  return if group.nil?
122
167
 
@@ -126,10 +171,10 @@ module HotCell
126
171
  end
127
172
  end
128
173
 
129
- def exchange(line, descriptors)
174
+ def exchange(path, line, descriptors)
130
175
  deadline = now + timeout
131
176
  socket = Socket.new(:UNIX, :STREAM)
132
- connect socket, deadline
177
+ connect socket, path, deadline
133
178
  deliver socket, line, descriptors, deadline
134
179
  response = receive(socket, deadline)
135
180
  return unavailable("the connection closed with no response, so the cell's supervisor is gone") if response.nil?
@@ -147,8 +192,8 @@ module HotCell
147
192
  # with no deadline, and a supervisor that has stopped calling `accept` leaves it full. Linux answers a
148
193
  # non-blocking connect to that backlog with EAGAIN and leaves the socket unconnected, so it is retried
149
194
  # rather than waited on: an unconnected Unix socket is always writable, and `select` would not wait.
150
- def connect(socket, deadline)
151
- socket.connect_nonblock Socket.sockaddr_un(socket_path)
195
+ def connect(socket, path, deadline)
196
+ socket.connect_nonblock Socket.sockaddr_un(path)
152
197
  rescue Errno::EAGAIN
153
198
  remaining = deadline - now
154
199
  raise Timeout unless remaining > 0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotcell-client-legacy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Dalessio
@@ -9,7 +9,7 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
- description: Call an operation in a HotCell container from an application that cannot
12
+ description: Call an operation in a Hot Cell container from an application that cannot
13
13
  run hotcell-client. One file, the standard library only, and legacy Ruby versions.
14
14
  email:
15
15
  - mike@37signals.com
@@ -26,8 +26,8 @@ licenses:
26
26
  - MIT
27
27
  metadata:
28
28
  homepage_uri: https://github.com/basecamp/hotcell
29
- source_code_uri: https://github.com/basecamp/hotcell/tree/v1.1.0/hotcell-client-legacy
30
- changelog_uri: https://github.com/basecamp/hotcell/blob/v1.1.0/CHANGELOG.md
29
+ source_code_uri: https://github.com/basecamp/hotcell/tree/v1.2.0/hotcell-client-legacy
30
+ changelog_uri: https://github.com/basecamp/hotcell/blob/v1.2.0/CHANGELOG.md
31
31
  documentation_uri: https://basecamp.github.io/hotcell/
32
32
  bug_tracker_uri: https://github.com/basecamp/hotcell/issues
33
33
  rubygems_mfa_required: 'true'
@@ -45,7 +45,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  requirements: []
48
- rubygems_version: 4.0.16
48
+ rubygems_version: 4.0.6
49
49
  specification_version: 4
50
- summary: Call a HotCell from a legacy Ruby version, with no dependencies.
50
+ summary: Call a Hot Cell from a legacy Ruby version, with no dependencies.
51
51
  test_files: []