Skip to content

Commit 9057190

Browse files
author
developerworks
committed
Wire up service example, generate_supervisor binary, and manual updates for 006-6
- Add service_task example with lifecycle observation and supervisor tree patterns - Add generate_supervisor bin to produce SupervisorSpec from YAML template - Update child_declaration spec with resource_limits and command_permissions fields - Update configurable.rs, control_loop, and runtime context for dynamic children - Sync en/zh manuals: configuration, runtime-control, supervisor-tree, policies - Fix dashboard error naming and Cargo dependency metadata
1 parent a14bfdb commit 9057190

31 files changed

Lines changed: 1309 additions & 179 deletions

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ Thumbs.db
1313
*.swp
1414
.vscode/
1515
.idea/
16+
/config

‎Cargo.lock‎

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ name = "rust_supervisor"
3333
path = "src/lib.rs"
3434

3535
[dependencies]
36+
clap = { version = "4.6.1", features = ["derive"] }
3637
confique = { version = "0.4.0", features = ["yaml"] }
3738
libc = "0.2"
3839
metrics = "0.24"
3940
rand = "0.10"
40-
rust-config-tree = "0.1.9"
41+
rust-config-tree = "0.2.0"
4142
schemars = { version = "1", features = ["derive"] }
4243
serde = { version = "1", features = ["derive"] }
4344
serde_json = "1"
Lines changed: 93 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,97 @@
1+
# Configuration file shape loaded from YAML.
2+
3+
# Additional configuration files included by `rust-config-tree`.
4+
#
5+
# Default value: []
6+
#include: []
7+
8+
# Optional target-side dashboard IPC configuration.
9+
#ipc:
10+
11+
# Child declarations loaded from YAML children array.
12+
#
13+
# Default value: []
14+
#children: []
15+
16+
# Root supervisor declaration values.
117
supervisor:
2-
strategy: OneForAll
18+
# Restart scope strategy for child failures.
19+
#
20+
# Required! This value must be specified.
21+
#strategy:
22+
23+
# Runtime policy values.
324
policy:
4-
child_restart_limit: 10
5-
child_restart_window_ms: 60000
6-
supervisor_failure_limit: 30
7-
supervisor_failure_window_ms: 60000
8-
initial_backoff_ms: 100
9-
max_backoff_ms: 5000
10-
jitter_ratio: 0.10
11-
heartbeat_interval_ms: 1000
12-
stale_after_ms: 3000
13-
# restart_budget: # 可选, 重启预算限流 (本切片新增)
14-
# window_secs: 60 # 滑动窗口, 默认 60s
15-
# max_burst: 10 # 窗口内最大突发故障次数, 默认 10
16-
# recovery_rate_per_sec: 0.5 # 每秒令牌恢复速率, 默认 0.5
17-
# group_strategies: # 可选, 分组策略 (本切片新增)
18-
# - name: "group-a"
19-
# children: ["child-1", "child-2"]
20-
# budget:
21-
# window_secs: 120
22-
# max_burst: 20
23-
# recovery_rate_per_sec: 1.0
25+
# Maximum child restarts within the child restart window.
26+
#
27+
# Required! This value must be specified.
28+
#child_restart_limit:
29+
30+
# Child restart window in milliseconds.
31+
#
32+
# Required! This value must be specified.
33+
#child_restart_window_ms:
34+
35+
# Maximum supervisor failures within the supervisor failure window.
36+
#
37+
# Required! This value must be specified.
38+
#supervisor_failure_limit:
39+
40+
# Supervisor failure window in milliseconds.
41+
#
42+
# Required! This value must be specified.
43+
#supervisor_failure_window_ms:
44+
45+
# Initial backoff in milliseconds.
46+
#
47+
# Required! This value must be specified.
48+
#initial_backoff_ms:
49+
50+
# Maximum backoff in milliseconds.
51+
#
52+
# Required! This value must be specified.
53+
#max_backoff_ms:
54+
55+
# Jitter ratio expressed as a fraction between zero and one.
56+
#
57+
# Required! This value must be specified.
58+
#jitter_ratio:
59+
60+
# Heartbeat interval in milliseconds.
61+
#
62+
# Required! This value must be specified.
63+
#heartbeat_interval_ms:
64+
65+
# Stale heartbeat threshold in milliseconds.
66+
#
67+
# Required! This value must be specified.
68+
#stale_after_ms:
69+
70+
# Shutdown budget values.
2471
shutdown:
25-
graceful_timeout_ms: 5000
26-
abort_wait_ms: 1000
72+
# Graceful drain timeout in milliseconds.
73+
#
74+
# Required! This value must be specified.
75+
#graceful_timeout_ms:
76+
77+
# Abort wait timeout in milliseconds.
78+
#
79+
# Required! This value must be specified.
80+
#abort_wait_ms:
81+
82+
# Observability switches and capacities.
2783
observability:
28-
event_journal_capacity: 256
29-
metrics_enabled: true
30-
audit_enabled: true
31-
ipc:
32-
enabled: false
33-
target_id: example-target
34-
path: /tmp/rust-supervisor-demo/example-target.sock
35-
permissions: "0600"
36-
bind_mode: replace_stale
37-
registration:
38-
enabled: false
39-
relay_registration_path: /tmp/rust-supervisor-demo/dashboard-relay-registration.sock
40-
display_name: "example target"
41-
lease_seconds: 30
42-
registration_heartbeat_interval_seconds: 15
84+
# Event journal capacity.
85+
#
86+
# Required! This value must be specified.
87+
#event_journal_capacity:
88+
89+
# Whether metrics recording is enabled.
90+
#
91+
# Required! This value must be specified.
92+
#metrics_enabled:
93+
94+
# Whether command audit recording is enabled.
95+
#
96+
# Required! This value must be specified.
97+
#audit_enabled:

‎examples/runtime_control_story.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use rust_supervisor::runtime::supervisor::Supervisor;
1313
type ExampleResult = Result<(), rust_supervisor::error::types::SupervisorError>;
1414

1515
// Use the Tokio runtime for the asynchronous example.
16-
#[tokio::main]
1716
// Return typed supervisor errors from the example.
18-
/// Runs the runtime control story example.
17+
// Runs the runtime control story example.
18+
#[tokio::main]
1919
async fn main() -> ExampleResult {
2020
// Load centralized YAML configuration.
2121
let state = load_config_from_yaml_file("examples/config/supervisor.yaml")?;

‎examples/service/README.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Service Role Example
2+
3+
[中文说明](README.zh.md)
4+
5+
This example shows a supervised `WorkRole::Service` child. A service role is a long-running task that should stay online, report readiness, emit heartbeats, and stop cooperatively when the supervisor shuts down.
6+
7+
Run it with:
8+
9+
```bash
10+
cargo run --package rust-tokio-supervisor --example service
11+
```
12+
13+
The process keeps running until you press `Ctrl+C`. The signal is treated as the operator stop request, then the example calls `shutdown_tree` and prints the graceful shutdown outcome.
14+
15+
## What It Shows
16+
17+
- Initialization: `service_task.rs` builds a `quote-service` child and marks it as ready.
18+
- Running: the service prints the current UNIX time once per second, then emits heartbeat and running tick facts.
19+
- Observation: `observation.rs` prints periodic `current_state`, runtime event text, and shutdown report data.
20+
- Stop: after `Ctrl+C`, `main.rs` calls `shutdown_tree`, cancellation reaches the service, and the service returns `TaskResult::Cancelled`.
21+
22+
## File Layout
23+
24+
- `main.rs`: wires the supervisor, event subscriptions, state snapshots, and shutdown command.
25+
- `service_task.rs`: declares the `WorkRole::Service` child and the async service body.
26+
- `observation.rs`: prints service facts, runtime events, current state records, and shutdown outcomes.
27+
28+
## Expected Output Shape
29+
30+
The output should include these stages:
31+
32+
```text
33+
service initialization: initialized child=quote-service path=/quote-service
34+
state after-initialization: child_count=1 shutdown_completed=false
35+
service example: running until Ctrl+C
36+
service business: child=quote-service tick=... now_unix=...
37+
service while-running: running child=quote-service tick=...
38+
operator signal=ctrl_c
39+
service during-stop: stopping child=quote-service
40+
shutdown outcome: child=quote-service status=Graceful phase=GracefulDrain cancel_delivered=true
41+
state after-shutdown: child_count=1 shutdown_completed=true
42+
```
43+
44+
The important behavior is that the service does not exit by itself. It stays active while running, reports readiness and heartbeat status through `current_state`, receives cancellation only after the operator signal, and finishes as a graceful child shutdown outcome.

‎examples/service/README.zh.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Service(常驻服务) 角色示例
2+
3+
[English README](README.md)
4+
5+
这个示例展示一个被 supervisor(监督器) 管理的 `WorkRole::Service`(工作角色: 常驻服务) 子任务. Service(常驻服务) 角色表示长期在线的任务, 它应该完成初始化, 报告 readiness(就绪状态), 发出 heartbeat(心跳), 并在 supervisor(监督器) 关闭时协作停止.
6+
7+
运行命令:
8+
9+
```bash
10+
cargo run --package rust-tokio-supervisor --example service
11+
```
12+
13+
进程会一直运行, 直到你按下 `Ctrl+C`. 这个信号会被当成 operator stop request(操作员停止请求), 然后示例调用 `shutdown_tree`(关闭监督树), 并输出 graceful shutdown outcome(优雅关闭结果).
14+
15+
## 示例展示内容
16+
17+
- 初始化: `service_task.rs` 构建 `quote-service` 子任务, 并调用 `mark_ready()` 报告 readiness(就绪状态).
18+
- 运行: service(常驻服务) 每秒输出一次当前 UNIX time(UNIX 时间), 然后发出 heartbeat(心跳) 和 running tick(运行周期) 事实.
19+
- 观测: `observation.rs` 周期性输出 `current_state`(当前状态), runtime event text(运行时事件文本) 和 shutdown report(关闭报告).
20+
- 停止: 按下 `Ctrl+C` 后, `main.rs` 调用 `shutdown_tree`(关闭监督树), cancellation(取消信号) 到达 service(常驻服务), service(常驻服务) 返回 `TaskResult::Cancelled`(任务已取消).
21+
22+
## 文件结构
23+
24+
- `main.rs`: 组合 supervisor(监督器), event subscription(事件订阅), state snapshot(状态快照) 和 shutdown command(关闭命令).
25+
- `service_task.rs`: 声明 `WorkRole::Service`(工作角色: 常驻服务) 子任务, 并实现 async service body(异步服务主体).
26+
- `observation.rs`: 输出 service fact(服务事实), runtime event(运行时事件), current state record(当前状态记录) 和 shutdown outcome(关闭结果).
27+
28+
## 预期输出形态
29+
30+
输出应该包含这些阶段:
31+
32+
```text
33+
service initialization: initialized child=quote-service path=/quote-service
34+
state after-initialization: child_count=1 shutdown_completed=false
35+
service example: running until Ctrl+C
36+
service business: child=quote-service tick=... now_unix=...
37+
service while-running: running child=quote-service tick=...
38+
operator signal=ctrl_c
39+
service during-stop: stopping child=quote-service
40+
shutdown outcome: child=quote-service status=Graceful phase=GracefulDrain cancel_delivered=true
41+
state after-shutdown: child_count=1 shutdown_completed=true
42+
```
43+
44+
关键行为是: service(常驻服务) 不会自己退出. 它在运行期保持 active(活跃), 通过 `current_state`(当前状态) 暴露 readiness(就绪状态) 和 heartbeat(心跳) 状态, 只在收到 operator signal(操作员信号) 后进入关闭阶段并收到 cancellation(取消信号), 最后形成 graceful child shutdown outcome(优雅子任务关闭结果).

‎examples/service/main.rs‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Demonstrates a supervised service role with initialization, running,
2+
//! cooperative stop, and observation output.
3+
4+
mod observation;
5+
mod service_task;
6+
7+
// Import command result variants returned by the runtime handle.
8+
use rust_supervisor::control::command::CommandResult;
9+
// Import supervisor error values.
10+
use rust_supervisor::error::types::SupervisorError;
11+
// Import the supervisor runtime entry point.
12+
use rust_supervisor::runtime::supervisor::Supervisor;
13+
// Import supervisor specification values.
14+
use rust_supervisor::spec::supervisor::SupervisorSpec;
15+
// Import shutdown timing policy.
16+
use rust_supervisor::shutdown::stage::ShutdownPolicy;
17+
// Import duration values for the example timing budget.
18+
use std::time::Duration;
19+
// Import asynchronous channel helpers.
20+
use tokio::sync::mpsc;
21+
22+
// Define the shared example result type.
23+
type ExampleResult = Result<(), rust_supervisor::error::types::SupervisorError>;
24+
25+
// Use the Tokio runtime for the asynchronous example.
26+
#[tokio::main]
27+
// Return typed supervisor errors from the example.
28+
/// Runs the service role example.
29+
async fn main() -> ExampleResult {
30+
// Build a channel that receives service lifecycle facts.
31+
let (service_event_sender, mut service_events) = mpsc::unbounded_channel();
32+
// Build one child declared as a service role.
33+
let service_child = service_task::service_child(service_event_sender);
34+
// Build a root supervisor with the service child.
35+
let mut spec = SupervisorSpec::root(vec![service_child]);
36+
// Keep enough event buffer for the full shutdown observation sequence.
37+
spec.event_channel_capacity = 32;
38+
// Use short shutdown windows so the example finishes quickly.
39+
let shutdown_policy =
40+
ShutdownPolicy::new(Duration::from_millis(250), Duration::from_millis(50), true);
41+
// Start the runtime with the service child.
42+
let handle = Supervisor::start_with_policy(spec, shutdown_policy).await?;
43+
// Subscribe to lifecycle event text before commands are sent.
44+
let mut runtime_events = handle.subscribe_events();
45+
// Wait until the service reports initialization.
46+
observation::wait_for_initialization(&mut service_events).await;
47+
// Print the state after the service has initialized.
48+
observation::print_current_state("after-initialization", handle.current_state().await?);
49+
// Print the long-running operation hint.
50+
println!("service example: running until Ctrl+C");
51+
// Build periodic observation ticks for the long-running service.
52+
let mut observation_interval = tokio::time::interval(Duration::from_secs(1));
53+
// Keep the service running until the operator requests shutdown.
54+
loop {
55+
// Wait for either an operator signal or the next observation tick.
56+
tokio::select! {
57+
// Stop the example only when the operator sends Ctrl+C.
58+
signal = tokio::signal::ctrl_c() => {
59+
// Convert signal errors into the example error type.
60+
signal.map_err(|error| {
61+
SupervisorError::fatal_config(format!(
62+
"failed to receive Ctrl+C signal: {error}"
63+
))
64+
})?;
65+
// Print the operator stop signal.
66+
println!("operator signal=ctrl_c");
67+
// Leave the persistent running loop.
68+
break;
69+
}
70+
// Print periodic service observation.
71+
_ = observation_interval.tick() => {
72+
// Print service facts emitted while the service is running.
73+
observation::drain_service_events("while-running", &mut service_events);
74+
// Print the current runtime state.
75+
observation::print_current_state("while-running", handle.current_state().await?);
76+
// Print relevant runtime events that arrived during the tick.
77+
observation::drain_runtime_events(&mut runtime_events);
78+
}
79+
}
80+
}
81+
// Request cooperative shutdown for the whole supervisor tree.
82+
let shutdown = handle
83+
.shutdown_tree("operator", "service role example stopped by operator")
84+
.await?;
85+
// Print service facts emitted during cooperative stop.
86+
observation::drain_service_events("during-stop", &mut service_events);
87+
// Print runtime events that show command and shutdown observation.
88+
observation::drain_runtime_events(&mut runtime_events);
89+
// Print the shutdown result and per-child outcome.
90+
if let CommandResult::Shutdown { result } = shutdown {
91+
// Print the completed shutdown phase.
92+
observation::print_shutdown_result(result);
93+
}
94+
// Print the state after shutdown has completed.
95+
observation::print_current_state("after-shutdown", handle.current_state().await?);
96+
// Finish the example successfully.
97+
Ok(())
98+
// End the service role example.
99+
}

0 commit comments

Comments
 (0)