nonnative 3.34.0 → 3.35.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: '028726d6f2465ec50e55504560c08adf2a1d4cb1cc487687f4308e03e50ebcaa'
4
- data.tar.gz: 3309d497da450d41ac5044d446375ed8a937588045dc4a0305240880b59ac03d
3
+ metadata.gz: a9a3ed94a590e8273136387f186364d01270536a472d476fc806b8e9d1d06c07
4
+ data.tar.gz: 9b1b14eb944a13c83c93bf816d4b485d0f1818080167a6f5339c7bba73d59c31
5
5
  SHA512:
6
- metadata.gz: 6213f4f042a0dab8767bb29021f80cd4d483b66a805165b746ba28c658532747778c51cf7c6720471d56ab5c2c8412f9694116331171d02b98e722d96eeef815
7
- data.tar.gz: 1f36fe9bac571d834dfe837fb19173b1d9c062a7389e044df8620a940d38aa3bca40864a7e429dacfb20f15c57a36706d2ed352a1de53f5e86d9b2c8736e1407
6
+ metadata.gz: 370d1071e0665f5ea99a06a5dcf4fbe18224aee3d47b9f4279f3fd881c03340869fc11b1b8a58349065bae3d98ffa7be5243a34c26351366b53063676a7cb859
7
+ data.tar.gz: 94ad2439c76e863ee3b6a34655454e0610f3a7f8c0b5db23df9af0c0bfc54924ca63410008eccf4b261b6693f601c0495d2bc668bce5a96f9a5a319db44088da
data/README.md CHANGED
@@ -79,6 +79,7 @@ Process/server fields:
79
79
  - `ports`: client-facing ports. These are also used for readiness/shutdown port checks.
80
80
  - `timeout`: max time (seconds) for each readiness/shutdown check. For processes, the same value also
81
81
  bounds optional HTTP/gRPC probes and graceful child exit after the stop signal. Defaults to `1.0`.
82
+ A value of `0` fails immediately; setting it to `nil` programmatically does the same.
82
83
  - `wait`: small sleep (seconds) between lifecycle steps.
83
84
  - `log`: per-runner log file used by process output redirection or server implementations.
84
85
 
@@ -89,7 +90,8 @@ Process-only fields:
89
90
 
90
91
  Service fields:
91
92
  - `port`: client-facing service port.
92
- - `timeout`: max time (seconds) for opt-in service readiness checks. Defaults to `1.0`.
93
+ - `timeout`: max time (seconds) for opt-in service readiness checks. Defaults to `1.0`. A value of
94
+ `0` fails immediately; setting it to `nil` programmatically does the same.
93
95
  - `readiness`: optional list of startup readiness checks. Supported kind is `tcp`, which requires
94
96
  explicit `host` and `port`.
95
97
 
@@ -114,8 +116,8 @@ Nonnative.stop
114
116
 
115
117
  `Nonnative.start` runs ordered tiers: service lifecycle calls and optional readiness checks complete,
116
118
  then server lifecycle and readiness checks complete, then process lifecycle and readiness checks run.
117
- A failed service readiness check prevents later tiers; other collected startup errors trigger rollback
118
- after the attempted tiers finish. `Nonnative.stop` reverses the tiers: processes, servers, then
119
+ A failed service lifecycle call or readiness check prevents later tiers; other collected startup errors
120
+ trigger rollback after the attempted tiers finish. `Nonnative.stop` reverses the tiers: processes, servers, then
119
121
  services. Model dependencies in that direction; a managed server can satisfy a process dependency,
120
122
  but a server cannot wait on a managed process. Startup failures raise `Nonnative::StartError`, and
121
123
  shutdown failures raise `Nonnative::StopError`.
@@ -52,6 +52,8 @@ module Nonnative
52
52
  # @see Nonnative::Proxy
53
53
  # @see Nonnative::SocketPairFactory
54
54
  class FaultInjectionProxy < Nonnative::Proxy
55
+ STOP_DRAIN_TIMEOUT = 1
56
+
55
57
  class Connection
56
58
  attr_accessor :pair, :thread
57
59
  attr_reader :socket
@@ -73,6 +75,7 @@ module Nonnative
73
75
  @logger = Logger.new(service.proxy.log)
74
76
  @mutex = Mutex.new
75
77
  @state = :none
78
+ @stopping = false
76
79
 
77
80
  super
78
81
  end
@@ -84,6 +87,7 @@ module Nonnative
84
87
  #
85
88
  # @return [void]
86
89
  def start
90
+ mutex.synchronize { @stopping = false }
87
91
  @tcp_server = ::TCPServer.new(service.host, service.port)
88
92
  @thread = Thread.new { perform_start }
89
93
 
@@ -95,6 +99,9 @@ module Nonnative
95
99
  # @return [void]
96
100
  def stop
97
101
  server = tcp_server
102
+ mark_stopping
103
+ close_connections
104
+ close_queued_connections(server)
98
105
  server&.close
99
106
 
100
107
  listener_thread = thread
@@ -107,8 +114,6 @@ module Nonnative
107
114
  @tcp_server = nil
108
115
  @thread = nil
109
116
 
110
- close_connections
111
-
112
117
  Nonnative.logger.info "stopped with host '#{service.host}' and port '#{service.port}' for proxy 'fault_injection'"
113
118
  end
114
119
 
@@ -225,13 +230,16 @@ module Nonnative
225
230
 
226
231
  private
227
232
 
228
- attr_reader :tcp_server, :thread, :connections, :mutex, :state, :logger
233
+ attr_reader :tcp_server, :thread, :connections, :mutex, :state, :logger, :stopping
229
234
 
230
235
  def perform_start
231
236
  loop do
232
237
  local_socket = tcp_server.accept
233
238
  id = local_socket.object_id
234
- register_connection(id, local_socket)
239
+ unless register_connection(id, local_socket)
240
+ local_socket.close
241
+ next
242
+ end
235
243
  connection_thread = Thread.start(local_socket) do |accepted_socket|
236
244
  accept_connection id, accepted_socket
237
245
  end
@@ -275,6 +283,19 @@ module Nonnative
275
283
  end
276
284
  end
277
285
 
286
+ def close_queued_connections(server)
287
+ return unless server
288
+
289
+ # This connection is queued after every client that connected before shutdown began.
290
+ # When the listener accepts and closes it, those earlier clients have been closed as well.
291
+ barrier = TCPSocket.new(service.host, service.port)
292
+ barrier.wait_readable(STOP_DRAIN_TIMEOUT)
293
+ rescue IOError, SystemCallError
294
+ nil
295
+ ensure
296
+ barrier&.close
297
+ end
298
+
278
299
  def apply_state(state)
279
300
  Nonnative.logger.info "applying state '#{state}' for proxy 'fault_injection'"
280
301
 
@@ -293,7 +314,16 @@ module Nonnative
293
314
  end
294
315
 
295
316
  def register_connection(id, socket)
296
- mutex.synchronize { connections[id] = Connection.new(socket) }
317
+ mutex.synchronize do
318
+ return false if stopping
319
+
320
+ connections[id] = Connection.new(socket)
321
+ true
322
+ end
323
+ end
324
+
325
+ def mark_stopping
326
+ mutex.synchronize { @stopping = true }
297
327
  end
298
328
 
299
329
  def attach_connection_thread(id, thread)
@@ -37,7 +37,7 @@ module Nonnative
37
37
  errors.concat(service_lifecycle(services, :start, :start))
38
38
  service_readiness_errors = check_service_readiness(services)
39
39
  errors.concat(service_readiness_errors)
40
- return errors if service_readiness_errors.any?
40
+ return errors if errors.any?
41
41
 
42
42
  [servers, processes].each { |runners| errors.concat(run_lifecycle_checks(runners, :start, :open?, :start)) }
43
43
 
@@ -15,7 +15,7 @@ module Nonnative
15
15
  # # ok is either the block result or false if the timeout elapsed
16
16
  #
17
17
  class Timeout
18
- # @param time [Numeric] timeout duration in seconds
18
+ # @param time [Numeric, nil] timeout duration in seconds; zero and `nil` values time out immediately
19
19
  def initialize(time)
20
20
  @time = time
21
21
  end
@@ -23,10 +23,13 @@ module Nonnative
23
23
  # Executes the given block with the configured timeout.
24
24
  #
25
25
  # If the timeout elapses, returns `false` instead of raising `Timeout::Error`.
26
+ # Zero and `nil` durations also return `false` without running the block.
26
27
  #
27
28
  # @yield the work to execute under a timeout
28
29
  # @return [Object, false] the block's return value, or `false` if the timeout elapsed
29
30
  def perform(&)
31
+ return false if time.nil? || time.zero?
32
+
30
33
  ::Timeout.timeout(time, &)
31
34
  rescue ::Timeout::Error
32
35
  false
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '3.34.0'
7
+ VERSION = '3.35.0'
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nonnative
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.34.0
4
+ version: 3.35.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski