async-container-supervisor 0.6.1 → 0.6.3
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 +3 -2
- data/context/getting-started.md +166 -0
- data/context/index.yaml +12 -0
- data/lib/async/container/supervisor/connection.rb +4 -1
- data/lib/async/container/supervisor/dispatchable.rb +1 -1
- data/lib/async/container/supervisor/server.rb +9 -15
- data/lib/async/container/supervisor/version.rb +1 -1
- data/readme.md +10 -0
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +3 -1
- 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: ad71770c6717fe41a49b28a2f4614e7f539d572332805e44cc8f857d0c6cc404
|
|
4
|
+
data.tar.gz: 2f2ed9f22cce34b84f85c132f87b5c3296b539530ea26d0127a24dacdc4b5aac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 360ca6f0fe692937d47579307d9abd59f4879ce4eef88d927b32441eb71f40ac827ea63bc66e2c45b9b489072385de13f49c2bfb2698ab0ff878853d6a34d768
|
|
7
|
+
data.tar.gz: 8c655ae5432df6b742660ca02251b8f7d25ca2b8a2937aa59d66cf485b562e7abc9f1bd7154cdded585f0e773de40dc9c1bb93871871a56f8cda5288ae61fc60
|
checksums.yaml.gz.sig
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
d
|
|
2
|
-
|
|
1
|
+
M4������v7Ȫ'[C���͟��Ą��O9����pY����i��B�v`Znj��)��+E�a����V��d���}I�QY���=qw���U�wz��i�M��p "�X-�.����}�Ȼ�A����@��saB��y�`��*e#5x-s��\��]���m3]�>� ���7�/�(��oE`#@##~���Ǎstٱ�B�)hQ�t��@�1�,���_���)<$�nk0o
|
|
2
|
+
�"�{�*~K�o��rW�Am������_����S�N�-r����jG��+��1���=h���Mw��@����a���-��%�d4�@
|
|
3
|
+
X��2p�o*��j�!z�Smޚ������@\��j2�Ґ�f��L�
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
This guide explains how to get started with `async-container-supervisor` to supervise and monitor worker processes in your Ruby applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add the gem to your project:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
$ bundle add async-container-supervisor
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Core Concepts
|
|
14
|
+
|
|
15
|
+
`async-container-supervisor` provides a robust process supervision system built on top of {ruby Async::Service::Generic}. The key components are:
|
|
16
|
+
|
|
17
|
+
- {ruby Async::Container::Supervisor::Environment}: An environment mixin that sets up a supervisor service in your application.
|
|
18
|
+
- {ruby Async::Container::Supervisor::Supervised}: An environment mixin that enables workers to connect to and be supervised by the supervisor.
|
|
19
|
+
- {ruby Async::Container::Supervisor::Server}: The server that handles communication with workers and performs monitoring.
|
|
20
|
+
- {ruby Async::Container::Supervisor::Worker}: A client that connects workers to the supervisor for health monitoring and diagnostics.
|
|
21
|
+
|
|
22
|
+
### Process Architecture
|
|
23
|
+
|
|
24
|
+
The supervisor operates as a multi-process architecture with three layers:
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
graph TD
|
|
28
|
+
Controller[Async::Container::Controller<br/>Root Process]
|
|
29
|
+
|
|
30
|
+
Controller -->|spawns & manages| Supervisor[Supervisor Process<br/>async-container-supervisor]
|
|
31
|
+
Controller -->|spawns & manages| Worker1[Worker Process 1]
|
|
32
|
+
Controller -->|spawns & manages| Worker2[Worker Process 2]
|
|
33
|
+
Controller -->|spawns & manages| WorkerN[Worker Process N...]
|
|
34
|
+
|
|
35
|
+
Worker1 -.->|connects via IPC| Supervisor
|
|
36
|
+
Worker2 -.->|connects via IPC| Supervisor
|
|
37
|
+
WorkerN -.->|connects via IPC| Supervisor
|
|
38
|
+
|
|
39
|
+
style Controller fill:#e1f5ff
|
|
40
|
+
style Supervisor fill:#fff4e1
|
|
41
|
+
style Worker1 fill:#e8f5e9
|
|
42
|
+
style Worker2 fill:#e8f5e9
|
|
43
|
+
style WorkerN fill:#e8f5e9
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Important:** The supervisor process is itself just another process managed by the root controller. If the supervisor crashes, the controller will restart it, and all worker processes will automatically reconnect to the new supervisor. This design ensures high availability and fault tolerance.
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
To use the supervisor, you need to define two services: one for the supervisor itself and one for your workers that will be supervised.
|
|
51
|
+
|
|
52
|
+
### Basic Example
|
|
53
|
+
|
|
54
|
+
Create a service configuration file (e.g., `service.rb`):
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
#!/usr/bin/env async-service
|
|
58
|
+
# frozen_string_literal: true
|
|
59
|
+
|
|
60
|
+
require "async/container/supervisor"
|
|
61
|
+
|
|
62
|
+
class MyWorkerService < Async::Service::Generic
|
|
63
|
+
def setup(container)
|
|
64
|
+
super
|
|
65
|
+
|
|
66
|
+
container.run(name: self.class.name, count: 4, restart: true) do |instance|
|
|
67
|
+
Async do
|
|
68
|
+
# Connect to the supervisor if available:
|
|
69
|
+
if @environment.implements?(Async::Container::Supervisor::Supervised)
|
|
70
|
+
@evaluator.make_supervised_worker(instance).run
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Mark the worker as ready:
|
|
74
|
+
instance.ready!
|
|
75
|
+
|
|
76
|
+
# Your worker logic here:
|
|
77
|
+
loop do
|
|
78
|
+
# Do work...
|
|
79
|
+
sleep 1
|
|
80
|
+
|
|
81
|
+
# Periodically update readiness:
|
|
82
|
+
instance.ready!
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Define the worker service:
|
|
90
|
+
service "worker" do
|
|
91
|
+
service_class MyWorkerService
|
|
92
|
+
|
|
93
|
+
# Enable supervision for this service:
|
|
94
|
+
include Async::Container::Supervisor::Supervised
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Define the supervisor service:
|
|
98
|
+
service "supervisor" do
|
|
99
|
+
include Async::Container::Supervisor::Environment
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Running the Service
|
|
104
|
+
|
|
105
|
+
Make the service executable and run it:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
$ chmod +x service.rb
|
|
109
|
+
$ ./service.rb
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This will start:
|
|
113
|
+
- A supervisor process listening on a Unix socket
|
|
114
|
+
- Four worker processes that connect to the supervisor
|
|
115
|
+
|
|
116
|
+
### Adding Health Monitors
|
|
117
|
+
|
|
118
|
+
You can add monitors to detect and respond to unhealthy conditions. For example, to add a memory monitor:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
service "supervisor" do
|
|
122
|
+
include Async::Container::Supervisor::Environment
|
|
123
|
+
|
|
124
|
+
monitors do
|
|
125
|
+
[
|
|
126
|
+
# Restart workers that exceed 500MB of memory:
|
|
127
|
+
Async::Container::Supervisor::MemoryMonitor.new(
|
|
128
|
+
interval: 10, # Check every 10 seconds
|
|
129
|
+
limit: 1024 * 1024 * 500 # 500MB limit
|
|
130
|
+
)
|
|
131
|
+
]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The {ruby Async::Container::Supervisor::MemoryMonitor} will periodically check worker memory usage and restart any workers that exceed the configured limit.
|
|
137
|
+
|
|
138
|
+
### Collecting Diagnostics
|
|
139
|
+
|
|
140
|
+
The supervisor can collect various diagnostics from workers on demand:
|
|
141
|
+
|
|
142
|
+
- **Memory dumps**: Full heap dumps for memory analysis
|
|
143
|
+
- **Thread dumps**: Stack traces of all threads
|
|
144
|
+
- **Scheduler dumps**: Async fiber hierarchy
|
|
145
|
+
- **Garbage collection profiles**: GC performance data
|
|
146
|
+
|
|
147
|
+
These can be triggered programmatically or via command-line tools (when available).
|
|
148
|
+
|
|
149
|
+
## Advanced Usage
|
|
150
|
+
|
|
151
|
+
### Custom Monitors
|
|
152
|
+
|
|
153
|
+
You can create custom monitors by implementing the monitor interface. A monitor should:
|
|
154
|
+
|
|
155
|
+
1. Accept connections and periodically check worker health
|
|
156
|
+
2. Take action (like restarting workers) when unhealthy conditions are detected
|
|
157
|
+
|
|
158
|
+
### Fault Tolerance
|
|
159
|
+
|
|
160
|
+
The supervisor architecture is designed for fault tolerance:
|
|
161
|
+
|
|
162
|
+
- **Supervisor crashes**: When the supervisor process crashes, the root controller automatically restarts it. Workers detect the disconnection and reconnect to the new supervisor.
|
|
163
|
+
- **Worker crashes**: The container automatically restarts crashed workers based on the `restart: true` configuration.
|
|
164
|
+
- **Communication failures**: Workers gracefully handle supervisor unavailability and will attempt to reconnect.
|
|
165
|
+
|
|
166
|
+
This design ensures your application remains operational even when individual processes fail.
|
data/context/index.yaml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
|
3
|
+
---
|
|
4
|
+
description: A supervisor for managing multiple container processes.
|
|
5
|
+
metadata:
|
|
6
|
+
documentation_uri: https://socketry.github.io/async-container-supervisor/
|
|
7
|
+
source_code_uri: https://github.com/socketry/async-container-supervisor.git
|
|
8
|
+
files:
|
|
9
|
+
- path: getting-started.md
|
|
10
|
+
title: Getting Started
|
|
11
|
+
description: This guide explains how to get started with `async-container-supervisor`
|
|
12
|
+
to supervise and monitor worker processes in your Ruby applications.
|
|
@@ -185,9 +185,12 @@ module Async
|
|
|
185
185
|
if call = @calls[id]
|
|
186
186
|
# Response to a call:
|
|
187
187
|
call.push(**message)
|
|
188
|
-
|
|
188
|
+
elsif message.key?(:do)
|
|
189
189
|
# Incoming call:
|
|
190
190
|
Call.dispatch(self, target, id, message)
|
|
191
|
+
else
|
|
192
|
+
# Likely a response to a timed-out call, ignore it:
|
|
193
|
+
Console.debug(self, "Ignoring message:", message)
|
|
191
194
|
end
|
|
192
195
|
else
|
|
193
196
|
Console.error(self, "Unknown message:", message)
|
|
@@ -14,7 +14,7 @@ module Async
|
|
|
14
14
|
method_name = "do_#{call.message[:do]}"
|
|
15
15
|
self.public_send(method_name, call)
|
|
16
16
|
rescue => error
|
|
17
|
-
Console.error(self, "Error while dispatching call
|
|
17
|
+
Console.error(self, "Error while dispatching call!", exception: error, call: call)
|
|
18
18
|
|
|
19
19
|
call.fail(error: {
|
|
20
20
|
class: error.class,
|
|
@@ -27,11 +27,9 @@ module Async
|
|
|
27
27
|
call.connection.state.merge!(call.message[:state])
|
|
28
28
|
|
|
29
29
|
@monitors.each do |monitor|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Console.error(self, "Error while registering process!", monitor: monitor, exception: error)
|
|
34
|
-
end
|
|
30
|
+
monitor.register(call.connection)
|
|
31
|
+
rescue => error
|
|
32
|
+
Console.error(self, "Error while registering process!", monitor: monitor, exception: error)
|
|
35
33
|
end
|
|
36
34
|
ensure
|
|
37
35
|
call.finish
|
|
@@ -59,22 +57,18 @@ module Async
|
|
|
59
57
|
|
|
60
58
|
def remove(connection)
|
|
61
59
|
@monitors.each do |monitor|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Console.error(self, "Error while removing process!", monitor: monitor, exception: error)
|
|
66
|
-
end
|
|
60
|
+
monitor.remove(connection)
|
|
61
|
+
rescue => error
|
|
62
|
+
Console.error(self, "Error while removing process!", monitor: monitor, exception: error)
|
|
67
63
|
end
|
|
68
64
|
end
|
|
69
65
|
|
|
70
66
|
def run(parent: Task.current)
|
|
71
67
|
parent.async do |task|
|
|
72
68
|
@monitors.each do |monitor|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Console.error(self, "Error while starting monitor!", monitor: monitor, exception: error)
|
|
77
|
-
end
|
|
69
|
+
monitor.run
|
|
70
|
+
rescue => error
|
|
71
|
+
Console.error(self, "Error while starting monitor!", monitor: monitor, exception: error)
|
|
78
72
|
end
|
|
79
73
|
|
|
80
74
|
@endpoint.accept do |peer|
|
data/readme.md
CHANGED
|
@@ -16,10 +16,20 @@ Provides a supervisor service for
|
|
|
16
16
|
|
|
17
17
|
Please see the [project documentation](https://socketry.github.io/async-container-supervisor/) for more details.
|
|
18
18
|
|
|
19
|
+
- [Getting Started](https://socketry.github.io/async-container-supervisor/guides/getting-started/index) - This guide explains how to get started with `async-container-supervisor` to supervise and monitor worker processes in your Ruby applications.
|
|
20
|
+
|
|
19
21
|
## Releases
|
|
20
22
|
|
|
21
23
|
Please see the [project releases](https://socketry.github.io/async-container-supervisor/releases/index) for all releases.
|
|
22
24
|
|
|
25
|
+
### v0.6.3
|
|
26
|
+
|
|
27
|
+
- Add agent context documentation.
|
|
28
|
+
|
|
29
|
+
### v0.6.2
|
|
30
|
+
|
|
31
|
+
- Fix timed out RPCs and subsequent responses which should be ignored.
|
|
32
|
+
|
|
23
33
|
### v0.6.0
|
|
24
34
|
|
|
25
35
|
- Add `async:container:supervisor:reload` command to restart the container (blue/green deployment).
|
data/releases.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.6.3
|
|
4
|
+
|
|
5
|
+
- Add agent context documentation.
|
|
6
|
+
|
|
7
|
+
## v0.6.2
|
|
8
|
+
|
|
9
|
+
- Fix timed out RPCs and subsequent responses which should be ignored.
|
|
10
|
+
|
|
3
11
|
## v0.6.0
|
|
4
12
|
|
|
5
13
|
- Add `async:container:supervisor:reload` command to restart the container (blue/green deployment).
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-container-supervisor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -85,6 +85,8 @@ extensions: []
|
|
|
85
85
|
extra_rdoc_files: []
|
|
86
86
|
files:
|
|
87
87
|
- bake/async/container/supervisor.rb
|
|
88
|
+
- context/getting-started.md
|
|
89
|
+
- context/index.yaml
|
|
88
90
|
- lib/async/container/supervisor.rb
|
|
89
91
|
- lib/async/container/supervisor/client.rb
|
|
90
92
|
- lib/async/container/supervisor/connection.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|