Skip to content

feat(jobs): 本地结果 GC —— 终态 job 保留 48h 后自动清理(不重启) - #22

Merged
Chronostasys merged 1 commit into
b300-glm52from
feat/jobs-ttl-gc
Sep 16, 2026
Merged

Chronostasys merged 1 commit into
b300-glm52from
feat/jobs-ttl-gc

Conversation

@Chronostasys

@Chronostasys Chronostasys commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

这个 PR 做什么

给训练 job 加基础本地 GC:终态 job 的结果在磁盘上保留 48 小时后自动清理。此前只有显式 DELETE /v1/control/jobs/{id} 一条清理路径,不删就永久堆积。

只改代码,不重启、不动生产(避免影响在跑的训练)。


1. 为什么

1021 实测(/root/smg-jobs):

实测
总量 954 MB / 69 jobs,其中近 24h 增量 923 MB
最大单 job 159 MB(job.json 一个文件 83 MB + 结果文件 8 MB/任务)
磁盘 11T 总 / 已用 7.3T / 剩 2.6T(75%)→ 不致命但无界增长
清理路径 delete_jobremove_jobjobs.rs:1129),无任何 TTL/GC

2. 本次实现

行为
SMG_JOBS_RETENTION_HOURS 默认 48(支持小数小时);0 = 关闭 GC
SMG_JOBS_GC_INTERVAL_SECS 后台扫描周期,默认 1800s启动时立即扫一次(回收停机期间积压)
可删对象 仅终态 job(completed / failed / cancelled / partial)
永不删除 queued / running(内存状态优先于文件年龄)
"最后活动时间" job 目录内文件的最新 mtimejob.json 每次状态变更重写、task_*.json 随任务完成落盘 → 跟随真实进度)
删除动作 复用 remove_job:目录 + 内存注册表项 + 每任务 live flag/progress/partial 一并清
历史残留 未被内存登记的旧目录同样按年龄回收
日志 每删一个打一行,含释放体积:
jobs: gc removed job_xxx (idle 52.1h > retention 48.0h, freed 91.2 MB)
配置日志 jobs: gc enabled (retention 48.0h, idempotent scan every 1800s)

边界:GC 只动本地文件与内存状态 —— 不触碰引擎、LoRA、正在执行的请求。

3. 实现要点

  • JobManager { retention: Option<Duration> }new() 从 env 读取,新增私有 build(..., retention) 便于测试注入;
  • gc_once():一次扫描,返回删除数;spawn_gc_task()tokio::time::interval 循环;
  • 辅助:newest_mtime(dir)(文件优先,目录 mtime 兜底)、dir_size(dir)(递归体积,用于日志/核算);
  • server.rsrecover_from_disk() 之后调用 spawn_gc_task()

4. 验证

cargo test --lib control_plane::jobs::
  test gc_helpers_report_last_activity_and_size ... ok
  test gc_removes_expired_finished_jobs_only ... ok
  test gc_disabled_keeps_everything ... ok
  test gc_removes_untracked_expired_dirs_and_orphan_entries ... ok
  test result: ok. 9 passed; 0 failed

新增 4 个用例覆盖:

  1. 过期终态 → 删;running → 保留;未过期 → 保留(并验证内存条目一起清);
  2. SMG_JOBS_RETENTION_HOURS=0(retention=None)→ 一个都不删;
  3. 未登记的历史目录被回收 + 内存注册表项被删;
  4. 辅助函数(最后活动时间 / 目录体积)。

5. 文档

docs/agent/training-jobs-api.md

  • 结果保留条目改为「48h + GC 语义」(只删终态、running 永不删、env 可调/可关);
  • §8 运维变量补 SMG_JOBS_RETENTION_HOURS / SMG_JOBS_GC_INTERVAL_SECS 与判据日志样例。

6. 上线方式(本 PR 不含)

需要一次 smg 重建 + 重启才生效(重启会打断在跑 job;磁盘结果会在启动时恢复)。待批准后再做

后续可选(未在本 PR):① job.json 去掉内嵌 result(现在结果同时写进 job.jsontask_*.json,且 job.json 每次状态变更重写 → 写放大,实测单文件 83 MB);② 结果落盘 gzip(实测 8.1MB → 1.1MB,13%);③ output_logprob_entriesoutput_token_logprobs+output_top_logprobs 去冗余(前者占结果文件 54%);④ 冷数据上传 OSS 后本地只留热数据。


CI States

Latest PR Test (Base): ✅ Run #34921492363
Latest PR Test (Extra): ❌ Run #34921492177

背景:job 结果(含逐 token logprob 三元组)写在 SMG_JOBS_DIR,此前**只有显式
DELETE /v1/control/jobs/{id}** 一条清理路径 —— 没有人删就永久堆积。实测 1021:
69 个 job / 954 MB,其中单个 job 目录 159 MB(job.json 83 MB、结果文件 8 MB),
近 24h 增量 923 MB;磁盘迟早被吃满。

本次加入基础 GC:

* `SMG_JOBS_RETENTION_HOURS`(默认 **48**,支持小数;**0 = 关闭 GC**)
  —— 超过保留期、且**已终态**的 job 目录被删除;
* `SMG_JOBS_GC_INTERVAL_SECS`(默认 1800)—— 后台扫描周期;启动时立即扫一次,
  回收停机期间积压的旧数据;
* 判定"最后活动时间"取 job 目录内文件的**最新 mtime**:job.json 每次状态变更都会
  重写、task_*.json 随任务完成落盘,因此该时间跟随真实进度;
* 安全性:**queued / running 的 job 永不删除**(内存中状态优先于文件年龄);
  未被内存登记的历史目录同样按年龄回收;删除会打日志(含释放体积),并把内存
  注册表项 + 每任务 live flag/progress/partial 一并清掉(复用 remove_job);
* 兼容:GC 只动本地文件与内存状态,不触碰引擎 / LoRA / 正在执行的请求。

实现:
* `JobManager.retention: Option<Duration>`;`new()` 从 env 取,新增私有
  `build(..., retention)` 便于测试注入;
* `gc_once()`(一次扫描,返回删除数)+ `spawn_gc_task()`(`tokio::time::interval`
  循环)+ 辅助 `newest_mtime()` / `dir_size()`;
* `server.rs`:`recover_from_disk()` 之后 `spawn_gc_task()`;GC 自己打一行配置日志
  (`jobs: gc enabled (retention 48.0h, idempotent scan every 1800s)`)。

单测 +4(`cargo test --lib control_plane::jobs::`):
  1. 过期终态删除、running 保留、未过期保留(同时验证内存条目一起清);
  2. `SMG_JOBS_RETENTION_HOURS=0`(retention=None)时一个都不删;
  3. 未登记的历史目录被回收 + 内存注册表项被删;
  4. 辅助函数(最后活动时间 / 目录体积)行为正确。

文档:`docs/agent/training-jobs-api.md`(保留期条目改为"48h + GC 语义",运维变量
补上两个 env 与判据日志样例)。

**未部署**:本 PR 只改代码,不重启、不动生产(避免影响在跑的训练)。
@github-actions github-actions Bot added documentation Improvements or additions to documentation model-gateway labels Sep 15, 2026
@Chronostasys
Chronostasys merged commit 0d53a21 into b300-glm52 Sep 16, 2026
53 of 58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation model-gateway

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants