async-container-supervisor 0.1.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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/lib/async/container/supervisor/client.rb +125 -0
- data/lib/async/container/supervisor/connection.rb +165 -0
- data/lib/async/container/supervisor/endpoint.rb +16 -0
- data/lib/async/container/supervisor/environment.rb +51 -0
- data/lib/async/container/supervisor/memory_monitor.rb +71 -0
- data/lib/async/container/supervisor/server.rb +78 -0
- data/lib/async/container/supervisor/service.rb +76 -0
- data/lib/async/container/supervisor/version.rb +12 -0
- data/lib/async/container/supervisor.rb +14 -0
- data/license.md +21 -0
- data/readme.md +43 -0
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +150 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ecde9321579fc86b6e3af0434fe518fefa5c63e161a68e8b982fa74028b5db43
|
4
|
+
data.tar.gz: 754cf3efc6d28f4f4e8e97da1ddfbad020a23578bbc710a81d4a7cc302d89912
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c0105355ee822e38cf60d3adf43192ab070766efbf5b54ef87283ac4cf7e0a7ed211f4154045ce91e2f9566cfe4478e1a51799fc04a40e1504c1749dd0420802
|
7
|
+
data.tar.gz: 21ffe53df327d2d8ba1b669316473bf571bbdae79dac7f1c50bb88c5198c72be9025b6620218ccac7e1a9bba0a19bb457182873d3b5a56399f8c09977d4707e5
|
checksums.yaml.gz.sig
ADDED
Binary file
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require "io/stream"
|
7
|
+
require_relative "connection"
|
8
|
+
|
9
|
+
module Async
|
10
|
+
module Container
|
11
|
+
module Supervisor
|
12
|
+
class Client
|
13
|
+
def self.run(...)
|
14
|
+
self.new(...).run
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(instance, endpoint = Supervisor.endpoint)
|
18
|
+
@instance = instance
|
19
|
+
@endpoint = endpoint
|
20
|
+
end
|
21
|
+
|
22
|
+
def dispatch(call)
|
23
|
+
method_name = "do_#{call.message[:do]}"
|
24
|
+
self.public_send(method_name, call)
|
25
|
+
end
|
26
|
+
|
27
|
+
def connect
|
28
|
+
unless @connection
|
29
|
+
peer = @endpoint.connect
|
30
|
+
stream = IO::Stream(peer)
|
31
|
+
@connection = Connection.new(stream, 0, instance: @instance)
|
32
|
+
|
33
|
+
# Register the instance with the server:
|
34
|
+
Async do
|
35
|
+
@connection.call(do: :register, state: @instance)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
return @connection unless block_given?
|
40
|
+
|
41
|
+
begin
|
42
|
+
yield @connection
|
43
|
+
ensure
|
44
|
+
@connection.close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def close
|
49
|
+
if connection = @connection
|
50
|
+
@connection = nil
|
51
|
+
connection.close
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private def dump(call)
|
56
|
+
if path = call[:path]
|
57
|
+
File.open(path, "w") do |file|
|
58
|
+
yield file
|
59
|
+
end
|
60
|
+
|
61
|
+
call.finish(path: path)
|
62
|
+
else
|
63
|
+
buffer = StringIO.new
|
64
|
+
yield buffer
|
65
|
+
|
66
|
+
call.finish(data: buffer.string)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def do_scheduler_dump(call)
|
71
|
+
dump(call) do |file|
|
72
|
+
Fiber.scheduler.print_hierarchy(file)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def do_memory_dump(call)
|
77
|
+
require "objspace"
|
78
|
+
|
79
|
+
dump(call) do |file|
|
80
|
+
ObjectSpace.dump_all(output: file)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def do_thread_dump(call)
|
85
|
+
dump(call) do |file|
|
86
|
+
Thread.list.each do |thread|
|
87
|
+
file.puts(thread.inspect)
|
88
|
+
file.puts(thread.backtrace)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def do_garbage_profile_start(call)
|
94
|
+
GC::Profiler.enable
|
95
|
+
call.finish(started: true)
|
96
|
+
end
|
97
|
+
|
98
|
+
def do_garbage_profile_stop(call)
|
99
|
+
GC::Profiler.disable
|
100
|
+
|
101
|
+
dump(connection, message) do |file|
|
102
|
+
file.puts GC::Profiler.result
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def run
|
107
|
+
Async do |task|
|
108
|
+
loop do
|
109
|
+
connect do |connection|
|
110
|
+
connection.run(self)
|
111
|
+
end
|
112
|
+
rescue => error
|
113
|
+
Console.error(self, "Unexpected error while running client!", exception: error)
|
114
|
+
|
115
|
+
# Retry after a small delay:
|
116
|
+
sleep(rand)
|
117
|
+
end
|
118
|
+
ensure
|
119
|
+
task.stop
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
module Async
|
9
|
+
module Container
|
10
|
+
module Supervisor
|
11
|
+
class Connection
|
12
|
+
class Call
|
13
|
+
def initialize(connection, id, message)
|
14
|
+
@connection = connection
|
15
|
+
@id = id
|
16
|
+
@message = message
|
17
|
+
|
18
|
+
@queue = ::Thread::Queue.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# @attribute [Connection] The connection that initiated the call.
|
22
|
+
attr :connection
|
23
|
+
|
24
|
+
# @attribute [Hash] The message that initiated the call.
|
25
|
+
attr :message
|
26
|
+
|
27
|
+
def [] key
|
28
|
+
@message[key]
|
29
|
+
end
|
30
|
+
|
31
|
+
def push(**response)
|
32
|
+
@queue.push(response)
|
33
|
+
end
|
34
|
+
|
35
|
+
def pop(...)
|
36
|
+
@queue.pop(...)
|
37
|
+
end
|
38
|
+
|
39
|
+
def each(&block)
|
40
|
+
while response = self.pop
|
41
|
+
yield response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def finish(**response)
|
46
|
+
self.push(id: @id, finished: true, **response)
|
47
|
+
@queue.close
|
48
|
+
end
|
49
|
+
|
50
|
+
def closed?
|
51
|
+
@queue.closed?
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.dispatch(connection, target, id, message)
|
55
|
+
Async do
|
56
|
+
call = self.new(connection, id, message)
|
57
|
+
connection.calls[id] = call
|
58
|
+
|
59
|
+
target.dispatch(call)
|
60
|
+
|
61
|
+
while response = call.pop
|
62
|
+
connection.write(id: id, **response)
|
63
|
+
end
|
64
|
+
ensure
|
65
|
+
# If the queue is closed, we don't need to send a finished message.
|
66
|
+
unless call.closed?
|
67
|
+
connection.write(id: id, finished: true)
|
68
|
+
end
|
69
|
+
|
70
|
+
connection.calls.delete(id)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.call(connection, **message, &block)
|
75
|
+
id = connection.next_id
|
76
|
+
call = self.new(connection, id, message)
|
77
|
+
|
78
|
+
connection.calls[id] = call
|
79
|
+
connection.write(id: id, **message)
|
80
|
+
|
81
|
+
if block_given?
|
82
|
+
call.each(&block)
|
83
|
+
else
|
84
|
+
return call.pop
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def initialize(stream, id, **state)
|
90
|
+
@stream = stream
|
91
|
+
@state = state
|
92
|
+
|
93
|
+
@calls = {}
|
94
|
+
|
95
|
+
@id = id
|
96
|
+
end
|
97
|
+
|
98
|
+
# @attribute [Hash(Integer, Call)] Calls in progress.
|
99
|
+
attr :calls
|
100
|
+
|
101
|
+
# @attribute [Hash(Symbol, Object)] State associated with this connection, for example the process ID, etc.
|
102
|
+
attr_accessor :state
|
103
|
+
|
104
|
+
def next_id
|
105
|
+
@id += 2
|
106
|
+
end
|
107
|
+
|
108
|
+
def write(**message)
|
109
|
+
@stream.write(JSON.dump(message) << "\n")
|
110
|
+
@stream.flush
|
111
|
+
end
|
112
|
+
|
113
|
+
def call(timeout: nil, **message)
|
114
|
+
id = next_id
|
115
|
+
calls[id] = ::Thread::Queue.new
|
116
|
+
|
117
|
+
write(id: id, **message)
|
118
|
+
|
119
|
+
return calls[id].pop(timeout: timeout)
|
120
|
+
ensure
|
121
|
+
calls.delete(id)
|
122
|
+
end
|
123
|
+
|
124
|
+
def read
|
125
|
+
if line = @stream&.gets
|
126
|
+
JSON.parse(line, symbolize_names: true)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def each
|
131
|
+
while message = self.read
|
132
|
+
yield message
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def call(...)
|
137
|
+
Call.call(self, ...)
|
138
|
+
end
|
139
|
+
|
140
|
+
def run(target)
|
141
|
+
self.each do |message|
|
142
|
+
if id = message[:id]
|
143
|
+
if call = @calls[id]
|
144
|
+
# Response to a call:
|
145
|
+
call.push(**message)
|
146
|
+
else
|
147
|
+
# Incoming call:
|
148
|
+
Call.dispatch(self, target, id, message)
|
149
|
+
end
|
150
|
+
else
|
151
|
+
Console.error(self, "Unknown message:", message)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def close
|
157
|
+
if stream = @stream
|
158
|
+
@stream = nil
|
159
|
+
stream.close
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require "io/endpoint/unix_endpoint"
|
7
|
+
|
8
|
+
module Async
|
9
|
+
module Container
|
10
|
+
module Supervisor
|
11
|
+
def self.endpoint(path = "supervisor.ipc")
|
12
|
+
::IO::Endpoint.unix(path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require "async/service/environment"
|
7
|
+
|
8
|
+
module Async
|
9
|
+
module Container
|
10
|
+
module Supervisor
|
11
|
+
module Environment
|
12
|
+
# The service class to use for the supervisor.
|
13
|
+
# @returns [Class]
|
14
|
+
def service_class
|
15
|
+
Supervisor::Service
|
16
|
+
end
|
17
|
+
|
18
|
+
# The name of the supervisor
|
19
|
+
# @returns [String]
|
20
|
+
def name
|
21
|
+
"supervisor"
|
22
|
+
end
|
23
|
+
|
24
|
+
# The IPC path to use for communication with the supervisor.
|
25
|
+
# @returns [String]
|
26
|
+
def ipc_path
|
27
|
+
::File.expand_path("supervisor.ipc", root)
|
28
|
+
end
|
29
|
+
|
30
|
+
# The endpoint the supervisor will bind to.
|
31
|
+
# @returns [::IO::Endpoint::Generic]
|
32
|
+
def endpoint
|
33
|
+
::IO::Endpoint.unix(ipc_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Options to use when creating the container.
|
37
|
+
def container_options
|
38
|
+
{restart: true, count: 1, health_check_timeout: 30}
|
39
|
+
end
|
40
|
+
|
41
|
+
def monitors
|
42
|
+
[]
|
43
|
+
end
|
44
|
+
|
45
|
+
def make_server(endpoint)
|
46
|
+
Server.new(endpoint, monitors: self.monitors)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require "memory/leak/cluster"
|
7
|
+
require "set"
|
8
|
+
|
9
|
+
module Async
|
10
|
+
module Container
|
11
|
+
module Supervisor
|
12
|
+
class MemoryMonitor
|
13
|
+
def initialize(interval: 10, limit: nil)
|
14
|
+
@interval = interval
|
15
|
+
@cluster = Memory::Leak::Cluster.new(limit: limit)
|
16
|
+
@processes = Hash.new{|hash, key| hash[key] = Set.new.compare_by_identity}
|
17
|
+
end
|
18
|
+
|
19
|
+
def register(connection)
|
20
|
+
if process_id = connection.state[:process_id]
|
21
|
+
connections = @processes[process_id]
|
22
|
+
|
23
|
+
if connections.empty?
|
24
|
+
Console.info(self, "Registering process:", process_id: process_id)
|
25
|
+
@cluster.add(process_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
connections.add(connection)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove(connection)
|
33
|
+
if process_id = connection.state[:process_id]
|
34
|
+
connections = @processes[process_id]
|
35
|
+
|
36
|
+
connections.delete(connection)
|
37
|
+
|
38
|
+
if connections.empty?
|
39
|
+
Console.info(self, "Removing process:", process_id: process_id)
|
40
|
+
@cluster.remove(process_id)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def run
|
46
|
+
Async do
|
47
|
+
while true
|
48
|
+
@cluster.check! do |process_id, monitor|
|
49
|
+
Console.error(self, "Memory leak detected in process:", process_id: process_id, monitor: monitor)
|
50
|
+
connections = @processes[process_id]
|
51
|
+
|
52
|
+
connections.each do |connection|
|
53
|
+
path = "/tmp/memory_dump_#{process_id}.json"
|
54
|
+
|
55
|
+
response = connection.call(do: :memory_dump, path: path, timeout: 30)
|
56
|
+
Console.info(self, "Memory dump saved to:", path, response: response)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Kill the process:
|
60
|
+
Console.info(self, "Killing process:", process_id: process_id)
|
61
|
+
Process.kill(:INT, process_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
sleep(@interval)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative "connection"
|
7
|
+
require_relative "endpoint"
|
8
|
+
|
9
|
+
require "io/stream"
|
10
|
+
|
11
|
+
module Async
|
12
|
+
module Container
|
13
|
+
module Supervisor
|
14
|
+
class Server
|
15
|
+
def initialize(endpoint = Supervisor.endpoint, monitors: [])
|
16
|
+
@endpoint = endpoint
|
17
|
+
@monitors = monitors
|
18
|
+
end
|
19
|
+
|
20
|
+
attr :monitors
|
21
|
+
|
22
|
+
def dispatch(call)
|
23
|
+
method_name = "do_#{call.message[:do]}"
|
24
|
+
self.public_send(method_name, call)
|
25
|
+
end
|
26
|
+
|
27
|
+
def do_register(call)
|
28
|
+
call.connection.state.merge!(call.message[:state])
|
29
|
+
|
30
|
+
@monitors.each do |monitor|
|
31
|
+
begin
|
32
|
+
monitor.register(call.connection)
|
33
|
+
rescue => error
|
34
|
+
Console.error(self, "Error while registering process!", monitor: monitor, exception: error)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
ensure
|
38
|
+
call.finish
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove(connection)
|
42
|
+
@monitors.each do |monitor|
|
43
|
+
begin
|
44
|
+
monitor.remove(connection)
|
45
|
+
rescue => error
|
46
|
+
Console.error(self, "Error while removing process!", monitor: monitor, exception: error)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run
|
52
|
+
Async do |task|
|
53
|
+
@monitors.each do |monitor|
|
54
|
+
begin
|
55
|
+
monitor.run
|
56
|
+
rescue => error
|
57
|
+
Console.error(self, "Error while starting monitor!", monitor: monitor, exception: error)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@endpoint.accept do |peer|
|
62
|
+
stream = IO::Stream(peer)
|
63
|
+
connection = Connection.new(stream, 1, remote_address: peer.remote_address)
|
64
|
+
connection.run(self)
|
65
|
+
ensure
|
66
|
+
connection.close
|
67
|
+
remove(connection)
|
68
|
+
end
|
69
|
+
|
70
|
+
task.children&.each(&:wait)
|
71
|
+
ensure
|
72
|
+
task.stop
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require "async"
|
7
|
+
require "async/service/generic"
|
8
|
+
require "io/endpoint/bound_endpoint"
|
9
|
+
|
10
|
+
module Async
|
11
|
+
module Container
|
12
|
+
module Supervisor
|
13
|
+
class Service < Async::Service::Generic
|
14
|
+
# Initialize the supervisor using the given environment.
|
15
|
+
# @parameter environment [Build::Environment]
|
16
|
+
def initialize(...)
|
17
|
+
super
|
18
|
+
|
19
|
+
@bound_endpoint = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
# The endpoint which the supervisor will bind to.
|
23
|
+
# Typically a unix pipe in the same directory as the host.
|
24
|
+
def endpoint
|
25
|
+
@evaluator.endpoint
|
26
|
+
end
|
27
|
+
|
28
|
+
# Bind the supervisor to the specified endpoint.
|
29
|
+
def start
|
30
|
+
@bound_endpoint = self.endpoint.bound
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def name
|
36
|
+
@evaluator.name
|
37
|
+
end
|
38
|
+
|
39
|
+
def setup(container)
|
40
|
+
container_options = @evaluator.container_options
|
41
|
+
health_check_timeout = container_options[:health_check_timeout]
|
42
|
+
|
43
|
+
container.run(name: self.name, **container_options) do |instance|
|
44
|
+
evaluator = @environment.evaluator
|
45
|
+
|
46
|
+
Async do
|
47
|
+
server = evaluator.make_server(@bound_endpoint)
|
48
|
+
server.run
|
49
|
+
|
50
|
+
instance.ready!
|
51
|
+
|
52
|
+
if health_check_timeout
|
53
|
+
Async(transient: true) do
|
54
|
+
while true
|
55
|
+
sleep(health_check_timeout / 2)
|
56
|
+
instance.ready!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
# Release the bound endpoint.
|
67
|
+
def stop
|
68
|
+
@bound_endpoint&.close
|
69
|
+
@bound_endpoint = nil
|
70
|
+
|
71
|
+
super
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative "supervisor/version"
|
7
|
+
|
8
|
+
require_relative "supervisor/server"
|
9
|
+
require_relative "supervisor/client"
|
10
|
+
|
11
|
+
require_relative "supervisor/memory_monitor"
|
12
|
+
|
13
|
+
require_relative "supervisor/environment"
|
14
|
+
require_relative "supervisor/service"
|
data/license.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# MIT License
|
2
|
+
|
3
|
+
Copyright, 2025, by Samuel Williams.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/readme.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Async::Container::Supervisor
|
2
|
+
|
3
|
+
Provides a supervisor service for
|
4
|
+
|
5
|
+
[](https://github.com/socketry/async-container-supervisor/actions?workflow=Test)
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- Supports multi-process, multi-thread and hybrid containers.
|
10
|
+
- Automatic scalability based on physical hardware.
|
11
|
+
- Direct integration with [systemd](https://www.freedesktop.org/software/systemd/man/sd_notify.html) using `$NOTIFY_SOCKET`.
|
12
|
+
- Internal process readiness protocol for handling state changes.
|
13
|
+
- Automatic restart of failed processes.
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
Please see the [project documentation](https://socketry.github.io/async-container-supervisor/) for more details.
|
18
|
+
|
19
|
+
## Releases
|
20
|
+
|
21
|
+
Please see the [project releases](https://socketry.github.io/async-container-supervisor/releases/index) for all releases.
|
22
|
+
|
23
|
+
### v0.1.0
|
24
|
+
|
25
|
+
- Initial implementation.
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
We welcome contributions to this project.
|
30
|
+
|
31
|
+
1. Fork it.
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
35
|
+
5. Create new Pull Request.
|
36
|
+
|
37
|
+
### Developer Certificate of Origin
|
38
|
+
|
39
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
40
|
+
|
41
|
+
### Community Guidelines
|
42
|
+
|
43
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data/releases.md
ADDED
data.tar.gz.sig
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: async-container-supervisor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Samuel Williams
|
8
|
+
bindir: bin
|
9
|
+
cert_chain:
|
10
|
+
- |
|
11
|
+
-----BEGIN CERTIFICATE-----
|
12
|
+
MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
|
13
|
+
ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
|
14
|
+
CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
|
15
|
+
MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
|
16
|
+
MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
|
17
|
+
bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
|
18
|
+
igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
|
19
|
+
9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
|
20
|
+
sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
|
21
|
+
e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
|
22
|
+
XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
|
23
|
+
RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
|
24
|
+
tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
|
25
|
+
zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
|
26
|
+
xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
27
|
+
BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
|
28
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
|
29
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
|
30
|
+
cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
|
31
|
+
xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
|
32
|
+
c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
|
33
|
+
8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
|
34
|
+
JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
|
35
|
+
eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
|
36
|
+
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
37
|
+
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
38
|
+
-----END CERTIFICATE-----
|
39
|
+
date: 2025-02-26 00:00:00.000000000 Z
|
40
|
+
dependencies:
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: async-container
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.22'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.22'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: async-service
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: io-endpoint
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: io-stream
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: memory-leak
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.3'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.3'
|
111
|
+
executables: []
|
112
|
+
extensions: []
|
113
|
+
extra_rdoc_files: []
|
114
|
+
files:
|
115
|
+
- lib/async/container/supervisor.rb
|
116
|
+
- lib/async/container/supervisor/client.rb
|
117
|
+
- lib/async/container/supervisor/connection.rb
|
118
|
+
- lib/async/container/supervisor/endpoint.rb
|
119
|
+
- lib/async/container/supervisor/environment.rb
|
120
|
+
- lib/async/container/supervisor/memory_monitor.rb
|
121
|
+
- lib/async/container/supervisor/server.rb
|
122
|
+
- lib/async/container/supervisor/service.rb
|
123
|
+
- lib/async/container/supervisor/version.rb
|
124
|
+
- license.md
|
125
|
+
- readme.md
|
126
|
+
- releases.md
|
127
|
+
homepage: https://github.com/socketry/async-container-supervisor
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
metadata:
|
131
|
+
documentation_uri: https://socketry.github.io/async-container-supervisor/
|
132
|
+
source_code_uri: https://github.com/socketry/async-container-supervisor.git
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '3.1'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubygems_version: 3.6.2
|
148
|
+
specification_version: 4
|
149
|
+
summary: A supervisor for managing multiple container processes.
|
150
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|