Skip to content

feat: add runtime storage cleanup with per-category retention - #313

Open
Sycun wants to merge 1 commit into
AIPentest:mainfrom
Sycun:feat/runtime-storage-cleanup
Open

Sycun wants to merge 1 commit into
AIPentest:mainfrom
Sycun:feat/runtime-storage-cleanup

Conversation

@Sycun

@Sycun Sycun commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

关联

Closes #310(增加清理运行空间垃圾的功能,可以释放无用的存储空间)

背景

运行期间产生的磁盘产物此前基本无界增长:多数只在删除会话/项目时才清理,tmp/c2(回传截图、上传件、下发文件、payload)与 data/workflow-checkpoints从未被删除。本 PR 将其纳入统一的保留策略与清理入口。

改动

新增 internal/storage(Gitea 式具名清理任务 + Prometheus 式标记后原子清扫):

类别 默认保留 目标目录
workspace 30 天 Agent 工作区 tmp/workspace
reduction 7 天 超长工具输出落盘 tmp/reduction
conversation_artifacts 30 天 data/conversation_artifacts
plantask 30 天 skills/.eino/plantask
c2_artifacts 30 天 tmp/c2
chat_uploads 90 天 chat_uploads
workflow_checkpoints 7 天 data/workflow-checkpoints
diagnostic_logs 14 天 log/diagnostic-*.log
  • 配置段 storage:(沿用本项目 *int + Effective() 惯例);auto_clean 默认关闭,与 Argo ttlStrategy / K8s ttlSecondsAfterFinished 的 unset 语义一致,升级不会在管理员不知情时删除既有数据
  • API:GET /api/storage/metaGET /api/storage/statusstatfsf_bavail 并暴露 inode)、POST /api/storage/cleanup
  • 后台清扫循环(默认 60 分钟,1 分钟粒度唤醒,改间隔无需重启)
  • 设置页新增「存储清理」tab:docker-system-df 式表格 + 预览 + 立即清理 + 策略表单,中英双语 i18n
  • 新增 storage:read / storage:write 权限,仅授予 admin

安全约束(误删活跃任务数据的代价远高于省下磁盘)

  • dry-run 为默认:省略 dry_run 按预览处理;真实删除必须同时传 dry_run=falseconfirm=true,确认是 API 层显式动作而非仅前端弹窗
  • 活跃会话保护active_grace_hours 内有活动的会话一律跳过;活跃状态查询失败时保守跳过(fail closed)
  • 孤儿回收:会话/项目已删除但目录残留的,超过 orphan_grace_days 后回收
  • scanner 不跟随符号链接,所有候选路径都收敛校验在类别根内;删除先原子改名为 .tmp-for-deletionRemoveAll,崩溃只留下可补删的残骸
  • 同一时刻只允许一轮清理(409);清理写入平台审计日志

顺带修复两个确定的泄漏

  1. DeleteConversation 漏删 chat_uploads/:其 chat_upload_artifacts 行已由 ON DELETE CASCADE 清除,磁盘文件却永久残留(该目录比其他产物多一层日期目录,无法复用既有清理函数)
  2. data/workflow-checkpoints/*.ckpt 此前无任何删除路径;checkpoint 生命周期由 Eino 持有、仓库内无干净终态钩子,故采用按龄保留(7 天)而非即时删除,避免破坏 resume 能力

验证

  • 新增 20 个单测(过期/未过期、dry-run、孤儿、活跃保护、fail-closed、符号链接不跟随、崩溃残骸补删、并发 409、路径逃逸、空日期目录回收、缓存失效等);go test ./internal/...go vet ./... 全绿
  • 真实起服务验证:登录后构造 44 天老化垃圾,确认孤儿回收、活跃保护(skipped_active 与新造文件精确对应)、confirm 缺失 400、未知类别(含 ../ 穿越尝试)400、审计落库、config.yaml 注释保留且未注册类别键被拒写
  • 真实浏览器(chromium/CDP)走完登录 → 设置 → 存储清理页:表格、概览卡片、预览面板渲染正确,无控制台报错;中英两种 locale 均验证
  • 后台循环实测:第二轮定时清扫与首轮精确间隔配置的 5 分钟
  • RBAC 直查 SQLite:operator / auditor / viewer 均无 storage:*

说明

  • workflow_checkpoints 未做即时删除(见上),如维护者希望运行结束即删,可在后续 PR 中于 Eino 终态钩子处调用
  • .upgrade-backup/ 未纳入清理:属回滚归档而非运行垃圾,误删影响升级回退

…est#310)

Runtime artifacts (agent workspaces, tool-output spill, C2 payloads, chat
uploads, workflow checkpoints, diagnostic logs) previously accumulated
without bound: most were only removed when a conversation or project was
deleted, and tmp/c2 plus workflow checkpoints were never removed at all.

Add a storage cleaner with named per-category tasks (Gitea-style), a
settings page tab, and a background sweep that is off by default so
upgrading never deletes existing data.

Safety properties, since mis-deleting live task data costs far more than
the disk saved:
- dry-run is the default; a real cleanup requires dry_run=false together
  with confirm=true at the API layer, not just a frontend dialog
- sessions active within active_grace_hours are always skipped, and a
  failed activity lookup skips conservatively (fail closed)
- directories whose conversation/project no longer exists are reclaimed
  as orphans after orphan_grace_days
- scanners never follow symlinks and every candidate path is confined to
  its category root; deletion renames to a .tmp-for-deletion marker first
  so a crash leaves recoverable residue instead of a half-deleted dir
- storage:* permissions are admin-only; without the grantSystemRolePermissions
  skip the default branch would have given operators an irreversible
  file-deletion right

Also fix two confirmed leaks: DeleteConversation left chat_uploads files
on disk (their rows already vanished via ON DELETE CASCADE), and workflow
checkpoints had no deletion path at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

增加清理运行空间垃圾的功能,可以释放无用的存储空间

1 participant