Skip to content

Commit f322ffb

Browse files
committed
Integrate async-container-supervisor.
1 parent be54958 commit f322ffb

File tree

4 files changed

+21
-131
lines changed

4 files changed

+21
-131
lines changed

falcon.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ Gem::Specification.new do |spec|
2828

2929
spec.add_dependency "async"
3030
spec.add_dependency "async-container", "~> 0.20"
31+
spec.add_dependency "async-container-supervisor", "~> 0.3.0"
3132
spec.add_dependency "async-http", "~> 0.75"
3233
spec.add_dependency "async-http-cache", "~> 0.4"
3334
spec.add_dependency "async-service", "~> 0.10"
3435
spec.add_dependency "bundler"
3536
spec.add_dependency "localhost", "~> 1.1"
3637
spec.add_dependency "openssl", "~> 3.0"
37-
spec.add_dependency "process-metrics", "~> 0.2"
3838
spec.add_dependency "protocol-http", "~> 0.31"
3939
spec.add_dependency "protocol-rack", "~> 0.7"
4040
spec.add_dependency "samovar", "~> 2.3"

lib/falcon/environment/supervisor.rb

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,28 @@
66
require_relative "../service/supervisor"
77
require_relative "../environment"
88

9-
require "io/endpoint/unix_endpoint"
9+
require "async/container/supervisor"
1010

1111
module Falcon
1212
module Environment
1313
# Provides an environment for hosting a supervisor which can monitor multiple applications.
1414
module Supervisor
15+
include Async::Container::Supervisor::Environment
16+
1517
# The service class to use for the supervisor.
1618
# @returns [Class]
1719
def service_class
1820
::Falcon::Service::Supervisor
1921
end
2022

21-
# The name of the supervisor
22-
# @returns [String]
23-
def name
24-
"supervisor"
25-
end
26-
2723
# The IPC path to use for communication with the supervisor.
2824
# @returns [String]
2925
def ipc_path
3026
::File.expand_path("supervisor.ipc", root)
3127
end
3228

33-
# The endpoint the supervisor will bind to.
34-
# @returns [::IO::Endpoint::Generic]
35-
def endpoint
36-
::IO::Endpoint.unix(ipc_path)
37-
end
38-
39-
# Options to use when creating the container.
40-
def container_options
41-
{restart: true, count: 1, health_check_timeout: 30}
29+
def monitors
30+
[Async::Container::Supervisor::MemoryMonitor.new(interval: 10)]
4231
end
4332
end
4433

lib/falcon/service/supervisor.rb

Lines changed: 8 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -3,119 +3,19 @@
33
# Released under the MIT License.
44
# Copyright, 2019-2024, by Samuel Williams.
55

6-
require "process/metrics"
7-
require "json"
8-
9-
require "async/service/generic"
10-
11-
require "io/endpoint/bound_endpoint"
12-
require "io/stream"
6+
require "async/container/supervisor/service"
137

148
module Falcon
159
module Service
1610
# Implements a host supervisor which can restart the host services and provide various metrics about the running processes.
17-
class Supervisor < Async::Service::Generic
18-
# Initialize the supervisor using the given environment.
19-
# @parameter environment [Build::Environment]
20-
def initialize(...)
21-
super
22-
23-
@bound_endpoint = nil
24-
end
25-
26-
# The endpoint which the supervisor will bind to.
27-
# Typically a unix pipe in the same directory as the host.
28-
def endpoint
29-
@evaluator.endpoint
30-
end
31-
32-
# Restart the process group that the supervisor belongs to.
33-
def do_restart(message)
34-
# Tell the parent of this process group to spin up a new process group/container.
35-
# Wait for that to start accepting new connections.
36-
# Stop accepting connections.
37-
# Wait for existing connnections to drain.
38-
# Terminate this process group.
39-
signal = message[:signal] || :INT
40-
41-
Process.kill(signal, Process.ppid)
42-
end
43-
44-
# Capture process metrics relating to the process group that the supervisor belongs to.
45-
def do_metrics(message)
46-
Process::Metrics::General.capture(pid: Process.ppid, ppid: Process.ppid)
47-
end
48-
49-
# Handle an incoming request.
50-
# @parameter message [Hash] The decoded message.
51-
def handle(message)
52-
case message[:please]
53-
when "restart"
54-
self.do_restart(message)
55-
when "metrics"
56-
self.do_metrics(message)
57-
end
58-
end
59-
60-
# Bind the supervisor to the specified endpoint.
61-
def start
62-
Console.logger.info(self) {"Binding to #{self.endpoint}..."}
63-
64-
@bound_endpoint = Sync{self.endpoint.bound}
65-
66-
super
67-
end
68-
69-
# Start the supervisor process which accepts connections from the bound endpoint and processes JSON formatted messages.
70-
# @parameter container [Async::Container::Generic]
71-
def setup(container)
72-
container_options = @evaluator.container_options
73-
health_check_timeout = container_options[:health_check_timeout]
74-
75-
container.run(name: self.name, **container_options) do |instance|
76-
Async do
77-
@bound_endpoint.accept do |peer|
78-
stream = ::IO::Stream(peer)
79-
80-
while message = stream.read_until("\0")
81-
response = handle(JSON.parse(message, symbolize_names: true))
82-
stream.puts(response.to_json, separator: "\0")
83-
end
84-
end
85-
86-
instance.ready!
87-
88-
if health_check_timeout
89-
Async(transient: true) do
90-
while true
91-
sleep(health_check_timeout / 2)
92-
instance.ready!
93-
end
94-
end
95-
end
96-
end
97-
end
98-
99-
super
100-
end
101-
102-
# Release the bound endpoint.
103-
def stop
104-
@bound_endpoint&.close
105-
@bound_endpoint = nil
106-
107-
super
108-
end
109-
110-
def invoke(command)
111-
@bound_endpoint.local_address_endpoint.connect do |peer|
112-
stream = ::IO::Stream(peer)
113-
114-
stream.puts(command.to_json, separator: "\0")
11+
class Supervisor < Async::Container::Supervisor::Service
12+
def invoke(message)
13+
Sync do
14+
client = Async::Container::Supervisor::Client.new(endpoint: self.endpoint)
11515

116-
response = JSON.parse(stream.read_until("\0"), symbolize_names: true)
117-
118-
return response
16+
client.connect do |connection|
17+
connection.call(**message)
18+
end
11919
end
12020
end
12121
end

test/falcon/service/supervisor.rb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@
3535

3636
expect(container.group.running).to have_attributes(size: be == 1)
3737

38-
response = supervisor.invoke({please: "metrics"})
38+
response = supervisor.invoke(do: "status")
3939

40-
expect(response).to be_a(Hash)
40+
expect(response).to be_a(Array)
41+
expect(response.size).to be == 1
4142

42-
# The supervisor should report itself:
43-
expect(response.values).to have_value(have_keys(
44-
command: be == "supervisor"
45-
))
43+
first = response.first
44+
expect(first).to have_keys(
45+
memory_monitor: be_a(Hash),
46+
)
4647
ensure
4748
supervisor.stop
4849
container.stop

0 commit comments

Comments
 (0)