> ## Documentation Index
> Fetch the complete documentation index at: https://starforge.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 存储布局

> 一个根目录，一切由它派生，以及哪些部分可以安全回收。

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
FORGE_STORAGE_ROOT=/mnt/shared/starforge
```

平台写的每一个路径都由这一项派生。没有别的要配，也不会有任何东西写到它之外。

## 布局

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
<root>/
├── cache/
│   ├── hf/            HF_HOME —— 模型权重，所有 run 共用
│   ├── datasets/      为作业拉取的平台数据集
│   └── corpora/       只读挂载的受治理语料
├── runs/
│   └── <user>/<experiment>/<run_id>/
│       ├── work/      作业的工作副本
│       ├── out/       checkpoint、导出、评测报告
│       └── logs/      归档日志
├── packages/          未启用对象存储时的作业包
├── reflow/            按部署的流量缓冲区
└── state/             控制平面的台账
```

## 那条关键的约束

这个根目录在控制台、每个节点、每个容器里必须解析成**同一个绝对路径**。
共享文件系统、RWX PVC 或同路径 bind mount 都能满足。

<Warning>
  路径会做 `~` 展开，但**有意不**沿符号链接解析。这个根经常是一个符号链接，
  或者是一个真实路径在控制台和节点上并不相同的 NFS 挂载点，
  而整个设计成立的前提就是这个字符串在两边完全一致。解析它恰好会搞坏它本来要支持的那些部署。
</Warning>

相对路径会在启动时被拒绝，而不是拿进程恰好启动的那个目录去解析。

## 什么可以安全回收

| 目录               | 可回收       | 原因                     |
| ---------------- | --------- | ---------------------- |
| `cache/hf`       | 可以        | 能从 hub 或镜像重建。代价是重新下载一次 |
| `cache/datasets` | 可以        | 能从对象存储重建               |
| `cache/corpora`  | 可以        | 同上                     |
| `runs/**/work`   | run 结束后可以 | 工作副本；它来自的那个包还在对象存储里    |
| `runs/**/out`    | **不可以**   | checkpoint 和导出。删了就没了   |
| `runs/**/logs`   | 谨慎        | 作业进入终态后，这是唯一一份         |
| `packages/`      | 启用对象存储后可以 |                        |
| `state/`         | **不可以**   | 控制平面台账                 |

缓存跨 run 共用是有意的。没有共享的 HF 缓存，每个作业都要重新下载几十 GB 权重——
在内网链路上这比训练本身还贵。

## 数据库为什么不在里面

`FORGE_DB_PATH` 有意**不放**在存储根下。那个根通常是共享文件系统，
而 SQLite 跑在 NFS 上有众所周知的锁问题——台账会有损坏风险，而且每个控制台副本都会争抢同一个文件。
数据库是控制平面状态，不是作业存储，所以它留在进程本地。

团队部署把 `FORGE_DB_URL` 指向 Postgres，这个问题就不存在了。

## 语料在外面

`FORGE_CORPUS_ROOT` **必须**位于存储根之外，配错时启动会被拒绝。
语料是受治理的只读材料，访问规则和平台自己写的东西不同；
嵌套进去会让同一套回收策略同时作用在两者身上。

## 记账与压力

| 设置                              | 作用                               |
| ------------------------------- | -------------------------------- |
| `FORGE_STORAGE_SCAN_INTERVAL_S` | 多久遍历一次根目录统计每用户用量。0 表示存储配额显示「未统计」 |
| `FORGE_DISK_WATERMARK_PCT`      | 用量超过它就停止出队，保护已经在跑的作业写 checkpoint |

这个水位值得配。磁盘写满会一次性搞垮所有运行中的作业，不只是下一个要启动的——
而它搞垮的恰恰是跑得最久、损失最大的那些。

<Info>
  存储配额是「测量」而不是「预留」，所以它是最终一致的——用户可能在两次扫描之间短暂超出。
  GPU 那道闸是精确的，因为分配是平台自己做的决定；存储是它观察到的事实。
</Info>
