async-service-supervisor 0.15.0 → 0.17.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/service/supervisor/monitor.rb +9 -0
- data/lib/async/service/supervisor/process_monitor.rb +9 -4
- data/lib/async/service/supervisor/utilization_monitor.rb +9 -2
- data/lib/async/service/supervisor/version.rb +1 -1
- data/lib/metrics/provider/async/service/supervisor/process_monitor.rb +39 -0
- data/lib/metrics/provider/async/service/supervisor/utilization_monitor.rb +52 -0
- data/lib/metrics/provider/async/service/supervisor.rb +7 -0
- data/lib/traces/provider/async/service/supervisor/server.rb +25 -0
- data/lib/traces/provider/async/service/supervisor/supervisor_controller.rb +24 -0
- data/lib/traces/provider/async/service/supervisor.rb +7 -0
- data/readme.md +8 -12
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +8 -2
- 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: df5a5c251964d3435c184c712d4ccc2aa8c74a255d78f3632669a1a527ce78af
|
|
4
|
+
data.tar.gz: ea832f3d1e9136ccca58f5c5025fe180de773389a739d9b0ad99aae453df2ee2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f54af527ecda8c37129a39f6061095cbaf66b47eec102fd21c1aecb9e5791f487c3d48f567f0a0898a7168ba33bc3a60eef18f7fc1beb656640ce53e3b5f02ed
|
|
7
|
+
data.tar.gz: a9e0b9c6e8a489665d72ed11b0af0eeca18fa7256bfbcdf9d7bef364554fcd219a15c1dbae49d073d8631a92ca023c689f91936444630198b596fcf2eae110dc
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -8,11 +8,20 @@ require "async/loop"
|
|
|
8
8
|
module Async
|
|
9
9
|
module Service
|
|
10
10
|
module Supervisor
|
|
11
|
+
# Base class for supervisor monitors that run periodically within the supervisor process.
|
|
12
|
+
#
|
|
13
|
+
# Subclasses should override {#run_once} to implement specific monitoring logic.
|
|
11
14
|
class Monitor
|
|
15
|
+
# Initialize a new monitor.
|
|
16
|
+
#
|
|
17
|
+
# @parameter interval [Numeric] The interval in seconds between each invocation of {#run_once}.
|
|
12
18
|
def initialize(interval: 1.0)
|
|
13
19
|
@interval = interval
|
|
14
20
|
end
|
|
15
21
|
|
|
22
|
+
# Serialize the monitor state for JSON representation.
|
|
23
|
+
#
|
|
24
|
+
# @returns [Hash] An empty hash by default; subclasses should override to include relevant state.
|
|
16
25
|
def as_json(...)
|
|
17
26
|
{}
|
|
18
27
|
end
|
|
@@ -67,15 +67,20 @@ module Async
|
|
|
67
67
|
{ppid: @ppid, metrics: self.metrics}
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
# Emit the process metrics.
|
|
71
|
+
#
|
|
72
|
+
# @parameter metrics [Hash] The process metrics to emit.
|
|
73
|
+
def emit(metrics)
|
|
74
74
|
# Log each process individually for better searchability in log platforms:
|
|
75
75
|
metrics.each do |process_id, general|
|
|
76
76
|
Console.info(self, "Process metrics captured.", general: general)
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
|
+
|
|
80
|
+
# Run one iteration of the process monitor.
|
|
81
|
+
def run_once
|
|
82
|
+
self.emit(self.metrics)
|
|
83
|
+
end
|
|
79
84
|
end
|
|
80
85
|
end
|
|
81
86
|
end
|
|
@@ -277,10 +277,10 @@ module Async
|
|
|
277
277
|
:utilization_monitor
|
|
278
278
|
end
|
|
279
279
|
|
|
280
|
-
#
|
|
280
|
+
# Sample aggregated utilization data.
|
|
281
281
|
#
|
|
282
282
|
# @returns [Hash] Hash mapping service names to aggregated utilization metrics.
|
|
283
|
-
def
|
|
283
|
+
def sample
|
|
284
284
|
@guard.synchronize do
|
|
285
285
|
aggregated = {}
|
|
286
286
|
|
|
@@ -313,6 +313,13 @@ module Async
|
|
|
313
313
|
end
|
|
314
314
|
end
|
|
315
315
|
|
|
316
|
+
# Serialize utilization data for JSON.
|
|
317
|
+
#
|
|
318
|
+
# @returns [Hash] Hash mapping service names to aggregated utilization metrics.
|
|
319
|
+
def as_json
|
|
320
|
+
self.sample
|
|
321
|
+
end
|
|
322
|
+
|
|
316
323
|
# Emit the utilization metrics.
|
|
317
324
|
#
|
|
318
325
|
# @parameter status [Hash] The utilization metrics.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "metrics/provider"
|
|
7
|
+
|
|
8
|
+
require_relative "../../../../../async/service/supervisor/process_monitor"
|
|
9
|
+
|
|
10
|
+
class Async::Service::Supervisor::ProcessMonitor
|
|
11
|
+
Metrics::Provider(self) do
|
|
12
|
+
PROCESS_METRICS_GENERAL_PROCESSOR_UTILIZATION = Metrics.metric("process.metrics.general.processor_utilization", :gauge, description: "Processor utilization for a supervised process.")
|
|
13
|
+
PROCESS_METRICS_GENERAL_TOTAL_SIZE = Metrics.metric("process.metrics.general.total_size", :gauge, description: "Total memory size for a supervised process.", unit: "bytes")
|
|
14
|
+
PROCESS_METRICS_GENERAL_VIRTUAL_SIZE = Metrics.metric("process.metrics.general.virtual_size", :gauge, description: "Virtual memory size for a supervised process.", unit: "bytes")
|
|
15
|
+
PROCESS_METRICS_GENERAL_RESIDENT_SIZE = Metrics.metric("process.metrics.general.resident_size", :gauge, description: "Resident memory size for a supervised process.", unit: "bytes")
|
|
16
|
+
|
|
17
|
+
def emit(metrics)
|
|
18
|
+
metrics.each_value do |general|
|
|
19
|
+
if value = general[:processor_utilization]
|
|
20
|
+
PROCESS_METRICS_GENERAL_PROCESSOR_UTILIZATION.emit(value)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if value = general[:total_size]
|
|
24
|
+
PROCESS_METRICS_GENERAL_TOTAL_SIZE.emit(value)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if value = general[:virtual_size]
|
|
28
|
+
PROCESS_METRICS_GENERAL_VIRTUAL_SIZE.emit(value)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if value = general[:resident_size]
|
|
32
|
+
PROCESS_METRICS_GENERAL_RESIDENT_SIZE.emit(value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
super
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "metrics/provider"
|
|
7
|
+
require "metrics/tags"
|
|
8
|
+
|
|
9
|
+
require_relative "../../../../../async/service/supervisor/utilization_monitor"
|
|
10
|
+
|
|
11
|
+
class Async::Service::Supervisor::UtilizationMonitor
|
|
12
|
+
Metrics::Provider(self) do
|
|
13
|
+
UTILIZATION = Metrics.metric("async.utilization", :gauge, description: "Active requests per worker.")
|
|
14
|
+
UTILIZATION_CONNECTIONS_ACTIVE = Metrics.metric("async.utilization.connections.active", :gauge, description: "The number of active connections.")
|
|
15
|
+
UTILIZATION_CONNECTIONS_TOTAL = Metrics.metric("async.utilization.connections.total", :gauge, description: "The total number of connections.")
|
|
16
|
+
UTILIZATION_REQUESTS_ACTIVE = Metrics.metric("async.utilization.requests.active", :gauge, description: "The number of active requests.")
|
|
17
|
+
UTILIZATION_REQUESTS_TOTAL = Metrics.metric("async.utilization.requests.total", :gauge, description: "The total number of requests.")
|
|
18
|
+
UTILIZATION_WORKERS = Metrics.metric("async.utilization.workers", :gauge, description: "The number of workers contributing utilization metrics.")
|
|
19
|
+
|
|
20
|
+
def emit(metrics)
|
|
21
|
+
metrics.each do |service_name, fields|
|
|
22
|
+
tags = Metrics::Tags.normalize(service: service_name)
|
|
23
|
+
|
|
24
|
+
if connections_active = fields[:connections_active]
|
|
25
|
+
UTILIZATION_CONNECTIONS_ACTIVE.emit(connections_active, tags: tags)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if connections_total = fields[:connections_total]
|
|
29
|
+
UTILIZATION_CONNECTIONS_TOTAL.emit(connections_total, tags: tags)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if requests_active = fields[:requests_active]
|
|
33
|
+
UTILIZATION_REQUESTS_ACTIVE.emit(requests_active, tags: tags)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if requests_total = fields[:requests_total]
|
|
37
|
+
UTILIZATION_REQUESTS_TOTAL.emit(requests_total, tags: tags)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
if worker_count = fields[:worker_count]
|
|
41
|
+
UTILIZATION_WORKERS.emit(worker_count, tags: tags)
|
|
42
|
+
|
|
43
|
+
if worker_count > 0 and requests_active = fields[:requests_active]
|
|
44
|
+
UTILIZATION.emit(requests_active.to_f / worker_count, tags: tags)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
super
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "traces/provider"
|
|
7
|
+
|
|
8
|
+
require_relative "../../../../../async/service/supervisor/server"
|
|
9
|
+
|
|
10
|
+
Traces::Provider(Async::Service::Supervisor::Server) do
|
|
11
|
+
def remove(controller)
|
|
12
|
+
return super unless controller.id
|
|
13
|
+
|
|
14
|
+
attributes = {
|
|
15
|
+
"worker.id" => controller.id,
|
|
16
|
+
"worker.process_id" => controller.process_id,
|
|
17
|
+
"service.name" => controller.state[:name],
|
|
18
|
+
"monitor.count" => @monitors.size,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Traces.trace("async.service.supervisor.worker.remove", attributes: attributes) do
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "traces/provider"
|
|
7
|
+
|
|
8
|
+
require_relative "../../../../../async/service/supervisor/supervisor_controller"
|
|
9
|
+
|
|
10
|
+
Traces::Provider(Async::Service::Supervisor::SupervisorController) do
|
|
11
|
+
def register(worker, process_id:, state: {})
|
|
12
|
+
attributes = {
|
|
13
|
+
"worker.process_id" => process_id,
|
|
14
|
+
"service.name" => state[:name],
|
|
15
|
+
"monitor.count" => @server.monitors.size,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Traces.trace("async.service.supervisor.worker.register", attributes: attributes) do |span|
|
|
19
|
+
super.tap do |id|
|
|
20
|
+
span["worker.id"] = id
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/readme.md
CHANGED
|
@@ -28,6 +28,14 @@ Please see the [project documentation](https://socketry.github.io/async-service-
|
|
|
28
28
|
|
|
29
29
|
Please see the [project releases](https://socketry.github.io/async-service-supervisor/releases/index) for all releases.
|
|
30
30
|
|
|
31
|
+
### v0.17.0
|
|
32
|
+
|
|
33
|
+
- Add opt-in `metrics` and `traces` providers for supervisor process metrics, utilization metrics, and worker lifecycle tracing.
|
|
34
|
+
|
|
35
|
+
### v0.16.0
|
|
36
|
+
|
|
37
|
+
- Add `ProcessMonitor#emit(metrics)` as an override point for subclasses to consume captured process metrics (e.g. emitting StatsD gauges).
|
|
38
|
+
|
|
31
39
|
### v0.15.0
|
|
32
40
|
|
|
33
41
|
- Improve robustness and error handling of default monitors and server loop, ensuring that monitor failures either completely crash the server or retry appropriately, rather than leaving the server in a broken state.
|
|
@@ -65,18 +73,6 @@ Please see the [project releases](https://socketry.github.io/async-service-super
|
|
|
65
73
|
|
|
66
74
|
- Close `Call` queue if asynchronous call fails during dispatch - further messages will fail with `ClosedQueueError`.
|
|
67
75
|
|
|
68
|
-
### v0.9.0
|
|
69
|
-
|
|
70
|
-
- Better handling of write failures in `Connection::Call.dispatch`, ensuring we don't leak calls.
|
|
71
|
-
- Robust monitor loop handling - restart on failure, and align loop iterations.
|
|
72
|
-
- Disable memory sampler by default and use text output format.
|
|
73
|
-
- Introduce support for redirecting dump output to logs.
|
|
74
|
-
|
|
75
|
-
### v0.8.0
|
|
76
|
-
|
|
77
|
-
- Add `Async::Service::Supervisor::ProcessMonitor` for logging CPU and memory metrics periodically.
|
|
78
|
-
- Fix documentation to use correct `maximum_size_limit:` parameter name for `MemoryMonitor` (was incorrectly documented as `limit:`).
|
|
79
|
-
|
|
80
76
|
## Contributing
|
|
81
77
|
|
|
82
78
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.17.0
|
|
4
|
+
|
|
5
|
+
- Add opt-in `metrics` and `traces` providers for supervisor process metrics, utilization metrics, and worker lifecycle tracing.
|
|
6
|
+
|
|
7
|
+
## v0.16.0
|
|
8
|
+
|
|
9
|
+
- Add `ProcessMonitor#emit(metrics)` as an override point for subclasses to consume captured process metrics (e.g. emitting StatsD gauges).
|
|
10
|
+
|
|
3
11
|
## v0.15.0
|
|
4
12
|
|
|
5
13
|
- Improve robustness and error handling of default monitors and server loop, ensuring that monitor failures either completely crash the server or retry appropriately, rather than leaving the server in a broken state.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-service-supervisor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -176,6 +176,12 @@ files:
|
|
|
176
176
|
- lib/async/service/supervisor/version.rb
|
|
177
177
|
- lib/async/service/supervisor/worker.rb
|
|
178
178
|
- lib/async/service/supervisor/worker_controller.rb
|
|
179
|
+
- lib/metrics/provider/async/service/supervisor.rb
|
|
180
|
+
- lib/metrics/provider/async/service/supervisor/process_monitor.rb
|
|
181
|
+
- lib/metrics/provider/async/service/supervisor/utilization_monitor.rb
|
|
182
|
+
- lib/traces/provider/async/service/supervisor.rb
|
|
183
|
+
- lib/traces/provider/async/service/supervisor/server.rb
|
|
184
|
+
- lib/traces/provider/async/service/supervisor/supervisor_controller.rb
|
|
179
185
|
- license.md
|
|
180
186
|
- readme.md
|
|
181
187
|
- releases.md
|
|
@@ -199,7 +205,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
199
205
|
- !ruby/object:Gem::Version
|
|
200
206
|
version: '0'
|
|
201
207
|
requirements: []
|
|
202
|
-
rubygems_version: 4.0.
|
|
208
|
+
rubygems_version: 4.0.10
|
|
203
209
|
specification_version: 4
|
|
204
210
|
summary: A supervisor for managing multiple container processes.
|
|
205
211
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|