Skip to content

Commit 9168b79

Browse files
committed
refactor: separate Runner and Job actors
- Extract Job Actor from Scheduler for per-job lifecycle control - Add Runner Actor for global coordination and supervision - Each job now manages its own tick, sync, and execution - Implement supervision for unexpected actor stops
1 parent 986fee5 commit 9168b79

File tree

13 files changed

+1041
-832
lines changed

13 files changed

+1041
-832
lines changed

CLAUDE.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,20 @@ rollcron is a Rust CLI tool that functions as a self-updating cron scheduler, si
88

99
```
1010
src/
11-
├── main.rs # Entry point, CLI parsing, orchestration
12-
├── config.rs # YAML config parsing, Job struct
13-
├── git.rs # Git operations (clone, pull, archive)
14-
└── scheduler.rs # Cron scheduling, job execution
11+
├── main.rs # Entry point, CLI parsing
12+
├── actor/
13+
│ ├── runner/ # Runner Actor - lifecycle management
14+
│ │ ├── mod.rs # Actor definition, messages
15+
│ │ ├── git_poll.rs # git fetch/reset loop
16+
│ │ └── lifecycle.rs # Job Actor supervision
17+
│ └── job/ # Job Actor - single job control
18+
│ ├── mod.rs # Actor definition, state machine
19+
│ ├── tick.rs # cron evaluation, jitter
20+
│ └── executor.rs # command execution, retry, timeout, logging
21+
├── config.rs # YAML config parsing, Job struct
22+
├── git.rs # Git operations (clone, pull, archive)
23+
├── env.rs # Environment variable handling
24+
└── logging.rs # Logging setup
1525
```
1626

1727
## Key Types

PLAN.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Refactoring Plan: Runner / Job Actor 分離
2+
3+
**Status: COMPLETED**
4+
5+
## 現状の問題
6+
7+
scheduler.rs に責務が混在している:
8+
9+
```
10+
Scheduler Actor
11+
├── Tick (1秒ごと cron評価)
12+
├── try_sync_job() ← Tick をブロック
13+
├── Concurrency 制御
14+
├── Executor spawn
15+
├── job_handles 管理
16+
└── JobCompleted 処理
17+
```
18+
19+
問題点:
20+
1. **Sync が Tick をブロック** - git sync がスケジューラのイベントループを止める
21+
2. **ConfigUpdate で job_handles 孤児化** - 削除されたジョブのタスクが残る
22+
3. **Wait モードが無制限キュー** - バックプレッシャーなし
23+
4. **Job Actor 不在** - Executor は単なる async task
24+
25+
## 提案: Runner / Job Actor 分離
26+
27+
```
28+
┌─────────────────────────────────────────────────────────────────┐
29+
│ ROOT RUNNER │
30+
│ 責務: ライフサイクル管理 │
31+
├─────────────────────────────────────────────────────────────────┤
32+
│ • CLI解析 │
33+
│ • Git pull cycle (async task) │
34+
│ • Config parse → Job Actor 生成/更新/削除 │
35+
│ • Graceful shutdown (SIGTERM → 全 Actor に通知) │
36+
│ • グローバル env 解決 │
37+
└──────────────────────────┬──────────────────────────────────────┘
38+
│ spawn / update / kill
39+
40+
┌─────────────────────────────────────────────────────────────────┐
41+
│ JOB ACTOR (per job) │
42+
│ 責務: 単一ジョブの完全な自律制御 │
43+
├─────────────────────────────────────────────────────────────────┤
44+
│ • 自身の Tick (毎秒 cron 評価) │
45+
│ • Jitter 適用 │
46+
│ • Concurrency 制御 (状態機械) │
47+
│ • Git sync (自分のディレクトリのみ) │
48+
│ • Command 実行 + timeout │
49+
│ • Retry (backoff + jitter) │
50+
│ • Log 出力・ローテーション │
51+
│ • Webhook 送信 │
52+
│ • Job 固有 env 解決 │
53+
└─────────────────────────────────────────────────────────────────┘
54+
```
55+
56+
## 通信プロトコル
57+
58+
```
59+
┌──────────────────────────────────────────────────────┐
60+
│ Runner Actor │
61+
│ │
62+
│ ┌─────────────┐ ┌─────────────────────────┐ │
63+
SIGTERM ──────▶│ │ Signal Loop │ │ Git Poll Loop │ │
64+
│ └──────┬──────┘ │ fetch → parse config │ │
65+
│ │ └───────────┬─────────────┘ │
66+
│ │ │ │
67+
│ │ ┌────────────────────┘ │
68+
│ │ │ │
69+
│ ▼ ▼ │
70+
│ ┌─────────────────────────────────────────────┐ │
71+
│ │ Lifecycle Manager │ │
72+
│ │ HashMap<JobId, Addr<JobActor>> │ │
73+
│ └─────────────────────────────────────────────┘ │
74+
└───────────────────────┬──────────────────────────────┘
75+
76+
┌─────────────────────────────┼─────────────────────────────┐
77+
│ │ │
78+
▼ ▼ ▼
79+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
80+
│ Job Actor [A] │ │ Job Actor [B] │ │ Job Actor [C] │
81+
└─────────────────┘ └─────────────────┘ └─────────────────┘
82+
```
83+
84+
### メッセージ一覧
85+
86+
```
87+
Runner → Job Actor:
88+
┌─────────────────┬────────────────────────────────────────────────┐
89+
│ Update(Job) │ config変更時。Actor は次の tick から新設定で動作 │
90+
│ Shutdown │ job削除時。即座に停止 │
91+
│ GracefulStop │ SIGTERM時。現在の実行完了後に停止 │
92+
└─────────────────┴────────────────────────────────────────────────┘
93+
94+
Job Actor → Runner:
95+
(明示的メッセージなし - xtra の Addr で停止検知)
96+
```
97+
98+
### Supervision
99+
100+
```
101+
Runner の監視責務:
102+
- 各 Actor の Addr<JobActor> を保持
103+
- 予期せぬ停止を検知 → 同じ設定で再spawn
104+
- GracefulStop 後は addr.stopped() で完了待機
105+
```
106+
107+
### ログの責務
108+
109+
```
110+
Runner がログ:
111+
- 設定エラー (cron パース失敗、必須フィールド欠落など)
112+
- Job Actor の spawn/停止/再spawn
113+
- Git pull の結果
114+
115+
Job Actor がログ:
116+
- コマンド実行結果 (log_file へ)
117+
- sync 失敗、retry、timeout (stderr へ)
118+
```
119+
120+
### ライフサイクルイベント
121+
122+
```
123+
[起動時]
124+
Runner: config parse → 有効な job ごとに spawn(Job) → HashMap に登録
125+
126+
[git pull 後]
127+
Runner: 新旧 config を diff
128+
- 新規 job → spawn(Job)
129+
- 既存 job → send Update(Job)
130+
- 削除 job → send Shutdown, HashMap から削除
131+
- 無効 job → スキップ + error log
132+
133+
[SIGTERM]
134+
Runner: 全 Job Actor に GracefulStop → 完了待ち → exit
135+
```
136+
137+
## 設計原則
138+
139+
- **Job Actor は停止しない** - コマンド失敗、sync失敗、timeout は状態遷移であり Actor の停止理由にならない
140+
- **停止するのは Runner からの指示のみ** - Shutdown / GracefulStop メッセージ
141+
- **ジョブは独立** - 1つのジョブの設定エラーが他のジョブに影響しない
142+
- 起動時: 無効なジョブはスキップ + エラーログ、有効なジョブは起動
143+
- 更新時: 無効なジョブはスキップ + エラーログ、有効なジョブは更新
144+
145+
## Job Actor 状態遷移
146+
147+
```
148+
┌────────┐
149+
─────────│ Idle │◀────────────────────────────────────┐
150+
└───┬────┘ │
151+
│ [cron due] │
152+
▼ │
153+
┌────────┐ │
154+
│Syncing │── fail ──▶ [retry next tick] ──────┤
155+
└───┬────┘ │
156+
│ [ok] │
157+
▼ │
158+
┌────────┐ │
159+
│Running │── success ─────────────────────────▶│
160+
└───┬────┘ │
161+
│ [fail + retry left] │
162+
▼ │
163+
┌────────┐ │
164+
│Backoff │── delay ──▶ Running ───────────────▶│
165+
└────────┘
166+
```
167+
168+
## ファイル分割案
169+
170+
```
171+
src/
172+
├── main.rs # CLI, bootstrap, signal handling
173+
├── actor/
174+
│ ├── runner/ # Runner Actor
175+
│ │ ├── mod.rs # Actor 定義、メッセージ
176+
│ │ ├── git_poll.rs # git fetch/reset ループ
177+
│ │ └── lifecycle.rs # Job Actor の生成/削除
178+
│ └── job/ # Job Actor
179+
│ ├── mod.rs # Actor 定義、状態機械
180+
│ ├── tick.rs # cron評価、jitter
181+
│ ├── executor.rs # command実行、retry、timeout
182+
│ └── logger.rs # log出力、rotation
183+
├── config.rs # (変更なし)
184+
├── git.rs # (変更なし)
185+
└── env.rs # (変更なし)
186+
```
187+
188+
各 actor ディレクトリを見ればその actor の責務が完結する。
189+
190+
## 実装順序
191+
192+
1. actor/job/mod.rs - Job Actor の骨格
193+
2. actor/job/* - Scheduler から job 単位ロジックを抽出
194+
3. actor/runner/mod.rs - Runner Actor の骨格
195+
4. actor/runner/lifecycle.rs - Job Actor の生成/更新/削除
196+
5. actor/runner/git_poll.rs - git fetch ループを移動
197+
6. main.rs - Runner Actor を起動するだけに簡素化

rollcron.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
jobs:
22
health-check:
33
schedule:
4-
cron: "*/5 * * * *"
4+
cron: "* * * * *"
55
run: echo Hello from rollcron!
66
wc-readme:
77
schedule:
8-
cron: "*/5 * * * *"
8+
cron: "* * * * *"
99
run: wc ./README.md

0 commit comments

Comments
 (0)