async-container 0.19.0 → 0.20.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/container/forked.rb +18 -0
- data/lib/async/container/generic.rb +19 -2
- data/lib/async/container/group.rb +40 -3
- data/lib/async/container/threaded.rb +29 -1
- data/lib/async/container/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a95321eeaead4fafe8c8db6bfa186d13559c8b39185c566db474ff2111e3add8
|
4
|
+
data.tar.gz: 873a71e53157cf3f5c94908daff4848f88b0bb06c35e10fd89c569fe6a646386
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e974514dc85a284427791fffa2299c1541f4330b60f4d1470a64c9d2591d9c9c5a59250d9560e4b61ca43024869681f5f2cdc89ab086b178a391105318691b9d
|
7
|
+
data.tar.gz: 712370e1ea844ede123bb6215422c393109e50133db7991242b748f013e75904494af9300fdcbcd5de490c65c473fb195eac46eabbb5d5f34e1a774235cf5e8e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -116,6 +116,24 @@ module Async
|
|
116
116
|
self.close_write
|
117
117
|
end
|
118
118
|
|
119
|
+
# Convert the child process to a hash, suitable for serialization.
|
120
|
+
#
|
121
|
+
# @returns [Hash] The request as a hash.
|
122
|
+
def as_json(...)
|
123
|
+
{
|
124
|
+
name: @name,
|
125
|
+
pid: @pid,
|
126
|
+
status: @status&.to_i,
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
# Convert the request to JSON.
|
131
|
+
#
|
132
|
+
# @returns [String] The request as JSON.
|
133
|
+
def to_json(...)
|
134
|
+
as_json.to_json(...)
|
135
|
+
end
|
136
|
+
|
119
137
|
# Set the name of the process.
|
120
138
|
# Invokes {::Process.setproctitle} if invoked in the child process.
|
121
139
|
def name= value
|
@@ -4,6 +4,7 @@
|
|
4
4
|
# Copyright, 2019-2024, by Samuel Williams.
|
5
5
|
|
6
6
|
require "etc"
|
7
|
+
require "async/clock"
|
7
8
|
|
8
9
|
require_relative "group"
|
9
10
|
require_relative "keyed"
|
@@ -141,7 +142,8 @@ module Async
|
|
141
142
|
# @parameter name [String] The name of the child instance.
|
142
143
|
# @parameter restart [Boolean] Whether to restart the child instance if it fails.
|
143
144
|
# @parameter key [Symbol] A key used for reloading child instances.
|
144
|
-
|
145
|
+
# @parameter health_check_timeout [Numeric | Nil] The maximum time a child instance can run without updating its state, before it is terminated as unhealthy.
|
146
|
+
def spawn(name: nil, restart: false, key: nil, health_check_timeout: nil, &block)
|
145
147
|
name ||= UNNAMED
|
146
148
|
|
147
149
|
if mark?(key)
|
@@ -157,9 +159,24 @@ module Async
|
|
157
159
|
|
158
160
|
state = insert(key, child)
|
159
161
|
|
162
|
+
# If a health check is specified, we will monitor the child process and terminate it if it does not update its state within the specified time.
|
163
|
+
if health_check_timeout
|
164
|
+
age_clock = state[:age] = Clock.start
|
165
|
+
end
|
166
|
+
|
160
167
|
begin
|
161
168
|
status = @group.wait_for(child) do |message|
|
162
|
-
|
169
|
+
case message
|
170
|
+
when :health_check!
|
171
|
+
if health_check_timeout&.<(age_clock.total)
|
172
|
+
Console.warn(self, "Child failed health check!", child: child, age: age_clock.total, health_check_timeout: health_check_timeout)
|
173
|
+
# If the child has failed the health check, we assume the worst and terminate it (SIGTERM).
|
174
|
+
child.terminate!
|
175
|
+
end
|
176
|
+
else
|
177
|
+
state.update(message)
|
178
|
+
age_clock&.reset!
|
179
|
+
end
|
163
180
|
end
|
164
181
|
ensure
|
165
182
|
delete(key, child)
|
@@ -13,7 +13,12 @@ module Async
|
|
13
13
|
# Manages a group of running processes.
|
14
14
|
class Group
|
15
15
|
# Initialize an empty group.
|
16
|
-
|
16
|
+
#
|
17
|
+
# @parameter health_check_interval [Numeric | Nil] The (biggest) interval at which health checks are performed.
|
18
|
+
def initialize(health_check_interval: 1.0)
|
19
|
+
@health_check_interval = health_check_interval
|
20
|
+
|
21
|
+
# The running fibers, indexed by IO:
|
17
22
|
@running = {}
|
18
23
|
|
19
24
|
# This queue allows us to wait for processes to complete, without spawning new processes as a result.
|
@@ -57,8 +62,36 @@ module Async
|
|
57
62
|
def wait
|
58
63
|
self.resume
|
59
64
|
|
60
|
-
|
61
|
-
self.wait_for_children
|
65
|
+
with_health_checks do |duration|
|
66
|
+
self.wait_for_children(duration)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private def with_health_checks
|
71
|
+
if @health_check_interval
|
72
|
+
health_check_clock = Clock.start
|
73
|
+
|
74
|
+
while self.running?
|
75
|
+
duration = [@health_check_interval - health_check_clock.total, 0].max
|
76
|
+
|
77
|
+
yield duration
|
78
|
+
|
79
|
+
if health_check_clock.total > @health_check_interval
|
80
|
+
self.health_check!
|
81
|
+
health_check_clock.reset!
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
while self.running?
|
86
|
+
yield nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Perform a health check on all running processes.
|
92
|
+
def health_check!
|
93
|
+
@running.each_value do |fiber|
|
94
|
+
fiber.resume(:health_check!)
|
62
95
|
end
|
63
96
|
end
|
64
97
|
|
@@ -119,15 +152,19 @@ module Async
|
|
119
152
|
@running[io] = Fiber.current
|
120
153
|
|
121
154
|
while @running.key?(io)
|
155
|
+
# Wait for some event on the channel:
|
122
156
|
result = Fiber.yield
|
123
157
|
|
124
158
|
if result == Interrupt
|
125
159
|
channel.interrupt!
|
126
160
|
elsif result == Terminate
|
127
161
|
channel.terminate!
|
162
|
+
elsif result
|
163
|
+
yield result
|
128
164
|
elsif message = channel.receive
|
129
165
|
yield message
|
130
166
|
else
|
167
|
+
# Wait for the channel to exit:
|
131
168
|
return channel.wait
|
132
169
|
end
|
133
170
|
end
|
@@ -90,7 +90,10 @@ module Async
|
|
90
90
|
def self.fork(**options)
|
91
91
|
self.new(**options) do |thread|
|
92
92
|
::Thread.new do
|
93
|
-
|
93
|
+
# This could be a configuration option (see forked implementation too):
|
94
|
+
::Thread.handle_interrupt(SignalException => :immediate) do
|
95
|
+
yield Instance.for(thread)
|
96
|
+
end
|
94
97
|
end
|
95
98
|
end
|
96
99
|
end
|
@@ -122,6 +125,23 @@ module Async
|
|
122
125
|
end
|
123
126
|
end
|
124
127
|
|
128
|
+
# Convert the child process to a hash, suitable for serialization.
|
129
|
+
#
|
130
|
+
# @returns [Hash] The request as a hash.
|
131
|
+
def as_json(...)
|
132
|
+
{
|
133
|
+
name: @thread.name,
|
134
|
+
status: @status&.as_json,
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
# Convert the request to JSON.
|
139
|
+
#
|
140
|
+
# @returns [String] The request as JSON.
|
141
|
+
def to_json(...)
|
142
|
+
as_json.to_json(...)
|
143
|
+
end
|
144
|
+
|
125
145
|
# Set the name of the thread.
|
126
146
|
# @parameter value [String] The name to set.
|
127
147
|
def name= value
|
@@ -188,6 +208,14 @@ module Async
|
|
188
208
|
@error.nil?
|
189
209
|
end
|
190
210
|
|
211
|
+
def as_json(...)
|
212
|
+
if @error
|
213
|
+
@error.inspect
|
214
|
+
else
|
215
|
+
true
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
191
219
|
# A human readable representation of the status.
|
192
220
|
def to_s
|
193
221
|
"\#<#{self.class} #{success? ? "success" : "failure"}>"
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-container
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -40,7 +40,7 @@ cert_chain:
|
|
40
40
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
41
41
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
42
42
|
-----END CERTIFICATE-----
|
43
|
-
date: 2025-02-
|
43
|
+
date: 2025-02-07 00:00:00.000000000 Z
|
44
44
|
dependencies:
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: async
|
@@ -48,14 +48,14 @@ dependencies:
|
|
48
48
|
requirements:
|
49
49
|
- - "~>"
|
50
50
|
- !ruby/object:Gem::Version
|
51
|
-
version: '2.
|
51
|
+
version: '2.22'
|
52
52
|
type: :runtime
|
53
53
|
prerelease: false
|
54
54
|
version_requirements: !ruby/object:Gem::Requirement
|
55
55
|
requirements:
|
56
56
|
- - "~>"
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version: '2.
|
58
|
+
version: '2.22'
|
59
59
|
executables: []
|
60
60
|
extensions: []
|
61
61
|
extra_rdoc_files: []
|
metadata.gz.sig
CHANGED
Binary file
|