Skip to content

Commit 38b4247

Browse files
Introduce UtilizationMonitor.
1 parent 7f03407 commit 38b4247

File tree

9 files changed

+914
-2
lines changed

9 files changed

+914
-2
lines changed

async-service-supervisor.gemspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ Gem::Specification.new do |spec|
2626

2727
spec.add_dependency "async-bus"
2828
spec.add_dependency "async-service", "~> 0.15"
29+
spec.add_dependency "async-utilization", "~> 0.2"
2930
spec.add_dependency "io-endpoint"
3031
spec.add_dependency "memory", "~> 0.7"
3132
spec.add_dependency "memory-leak", "~> 0.10"
3233
spec.add_dependency "process-metrics"
34+
spec.add_dependency "thread-local"
3335
end

examples/echo/service.rb

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env async-service
2+
# frozen_string_literal: true
3+
4+
# Released under the MIT License.
5+
# Copyright, 2025, by Samuel Williams.
6+
7+
require "async/service/supervisor"
8+
require "async/service/managed/service"
9+
require "async/utilization"
10+
require "io/endpoint/host_endpoint"
11+
12+
class EchoService < Async::Service::Managed::Service
13+
def initialize(...)
14+
super
15+
16+
@bound_endpoint = nil
17+
@endpoint = nil
18+
end
19+
20+
# Prepare the bound endpoint for the server.
21+
def start
22+
@endpoint = IO::Endpoint.tcp("127.0.0.1", 8080)
23+
24+
Sync do
25+
@bound_endpoint = @endpoint.bound
26+
end
27+
28+
Console.info(self, "Starting echo server on #{@endpoint}")
29+
30+
super
31+
end
32+
33+
def run(instance, evaluator)
34+
evaluator.prepare!(instance)
35+
36+
instance.ready!
37+
38+
Async do |task|
39+
@bound_endpoint.accept do |peer|
40+
Async::Utilization.increment(:total_connections)
41+
Async::Utilization.increment(:active_connections) do
42+
Console.info(self, "Client connected", peer: peer)
43+
44+
peer.each_line do |line|
45+
Async::Utilization.increment(:total_messages)
46+
peer.write(line)
47+
end
48+
end
49+
50+
Console.info(self, "Client disconnected", peer: peer)
51+
end
52+
end
53+
54+
# Return the bound endpoint for health checking
55+
@bound_endpoint
56+
end
57+
58+
# Close the bound endpoint.
59+
def stop(...)
60+
if @bound_endpoint
61+
@bound_endpoint.close
62+
@bound_endpoint = nil
63+
end
64+
65+
@endpoint = nil
66+
67+
super
68+
end
69+
end
70+
71+
service "echo" do
72+
include Async::Service::Managed::Environment
73+
include Async::Service::Supervisor::Supervised
74+
75+
service_class EchoService
76+
77+
utilization_schema do
78+
{
79+
connections_total: :u64,
80+
connections_active: :u32,
81+
messages_total: :u64
82+
}
83+
end
84+
end
85+
86+
service "supervisor" do
87+
include Async::Service::Supervisor::Environment
88+
89+
monitors do
90+
[
91+
Async::Service::Supervisor::UtilizationMonitor.new(
92+
path: "utilization.shm",
93+
interval: 1
94+
)
95+
]
96+
end
97+
end

examples/echo/utilization.shm

128 KB
Binary file not shown.

lib/async/service/supervisor.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
require_relative "supervisor/memory_monitor"
1414
require_relative "supervisor/process_monitor"
15+
require_relative "supervisor/utilization_monitor"
1516

1617
require_relative "supervisor/environment"
1718
require_relative "supervisor/supervised"

lib/async/service/supervisor/supervised.rb

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Copyright, 2025, by Samuel Williams.
55

66
require "async/service/environment"
7+
require "async/utilization"
78

89
module Async
910
module Service
@@ -30,10 +31,38 @@ def supervisor_worker_state
3031
{name: self.name}
3132
end
3233

34+
# A default schema for utilization metrics.
35+
# @returns [Hash | Nil] The utilization schema or nil if utilization is disabled.
36+
def utilization_schema
37+
{
38+
connections_active: :u32,
39+
connections_total: :u64,
40+
requests_active: :u32,
41+
requests_total: :u64,
42+
}
43+
end
44+
45+
# Get the utilization registry for this service.
46+
#
47+
# Creates a new registry instance for tracking utilization metrics.
48+
# This registry is used by workers to emit metrics that can be collected
49+
# by the supervisor's utilization monitor.
50+
#
51+
# @returns [Async::Utilization::Registry] A new utilization registry instance.
52+
def utilization_registry
53+
Async::Utilization::Registry.new
54+
end
55+
3356
# The supervised worker for the current process.
3457
# @returns [Worker] The worker client.
3558
def supervisor_worker
36-
Worker.new(process_id: Process.pid, endpoint: supervisor_endpoint, state: self.supervisor_worker_state)
59+
Worker.new(
60+
process_id: Process.pid,
61+
endpoint: supervisor_endpoint,
62+
state: self.supervisor_worker_state,
63+
utilization_schema: self.utilization_schema,
64+
utilization_registry: self.utilization_registry,
65+
)
3766
end
3867

3968
# Create a supervised worker for the given instance.

0 commit comments

Comments
 (0)