缓存架构
Note
本页提供 Tuist 缓存服务架构的技术概览。主要面向需要理解服务内部工作原理的自托管用户和贡献者。仅希望使用缓存的普通用户无需阅读此内容。
Tuist 缓存服务是一项独立服务,为构建产物提供内容寻址存储(CAS),并为缓存元数据提供键值存储。
概览#
该服务采用两层存储架构,并结合本地 SQLite 元数据:
- 本地磁盘:用于低延迟缓存命中的主存储
- S3:持久化存储,可保留产物并在驱逐后恢复
- SQLite:本地元数据,用于跟踪产物访问、清理孤儿文件、执行后台作业以及存储键值缓存数据
flowchart LR
CLI[Tuist CLI] --> NGINX[Nginx]
NGINX --> APP[Cache service]
NGINX -->|X-Accel-Redirect| DISK[(Local Disk)]
APP --> S3[(S3)]
APP -->|auth| SERVER[Tuist Server]
组件#
Nginx#
Nginx 作为入口点,并使用 X-Accel-Redirect 高效交付文件:
- 下载:缓存服务验证身份认证,然后返回
X-Accel-Redirect标头。Nginx 直接从磁盘提供文件或通过 S3 代理。 - 上传:Nginx 将请求代理到缓存服务,后者将数据流式写入磁盘。
内容寻址存储#
产物以分片目录结构存储在本地磁盘上:
- 路径:
{account}/{project}/cas/{shard1}/{shard2}/{artifact_id} - 分片:取产物 ID 的前四个字符创建两级分片(例如,
ABCD1234→AB/CD/ABCD1234)
SQLite 元数据#
缓存服务使用两个 SQLite 数据库:
- 主元数据数据库:存储
cache_artifacts、孤儿扫描游标、Oban 作业以及其他服务元数据。 - 键值数据库:在专用的 SQLite 文件中存储
key_value_entries和key_value_entry_hashes。
键值存储被拆分到独立的数据库中,以便可以使用 SQLite 增量自动清理,而不影响产物元数据和孤儿清理状态。
S3 集成#
S3 提供持久化存储:
- 后台上传:写入磁盘后,产物会通过每分钟运行一次的后台工作器排队上传到 S3
- 按需回填:当本地缺少某个产物时,请求会通过预签名的 S3 URL 立即得到响应,同时该产物会排队在后台下载到本地磁盘
磁盘驱逐#
该服务通过多个后台进程管理磁盘空间:
- CAS 磁盘驱逐使用由
cache_artifacts支持的 LRU 策略 - 当磁盘使用率超过 85% 时,将删除最旧的产物,直到使用率降至 70%
- 产物在本地驱逐后仍保留在 S3 中
- KV 驱逐会根据保留策略移除旧的键值条目,并在专用 KV 数据库超出其配置的大小预算时对其进行收缩
孤儿清理#
该服务还会运行一个用于磁盘产物的孤儿清理工作器:
- 它会扫描存储树,查找存在于磁盘但没有对应
cache_artifacts记录的文件。 - 如果文件已写入磁盘,但在 SQLite 缓冲区刷新完成之前元数据写入丢失,就可能出现这种情况。
- 为避免与进行中的上传发生竞争,会忽略早于安全时间窗口的文件。
- 如果某个孤儿文件被删除后又再次被请求,下一次缓存未命中会使其重新上传,因此系统能够自我修复。
身份认证#
缓存通过调用 /api/projects 端点并将结果缓存(成功时缓存 10 分钟,失败时缓存 3 秒),将身份认证委托给 Tuist 服务器。
请求流程#
下载#
sequenceDiagram
participant CLI as Tuist CLI
participant N as Nginx
participant A as Cache service
participant D as Disk
participant S as S3
CLI->>N: GET /api/cache/cas/:id
N->>A: Proxy for auth
A-->>N: X-Accel-Redirect
alt On disk
N->>D: Serve file
else Not on disk
N->>S: Proxy from S3
end
N-->>CLI: File bytes
上传#
sequenceDiagram
participant CLI as Tuist CLI
participant N as Nginx
participant A as Cache service
participant D as Disk
participant S as S3
CLI->>N: POST /api/cache/cas/:id
N->>A: Proxy upload
A->>D: Stream to disk
A-->>CLI: 201 Created
A->>S: Background upload
API 端点#
| 端点 | 方法 | 描述 |
|---|---|---|
/up | GET | 健康检查 |
/metrics | GET | Prometheus 指标 |
/api/cache/cas/:id | GET | 下载 CAS 产物 |
/api/cache/cas/:id | POST | 上传 CAS 产物 |
/api/cache/keyvalue/:cas_id | GET | 获取键值条目 |
/api/cache/keyvalue | PUT | 存储键值条目 |
/api/cache/module/:id | HEAD | 检查模块产物是否存在 |
/api/cache/module/:id | GET | 下载模块产物 |
/api/cache/module/start | POST | 启动分片上传 |
/api/cache/module/part | POST | 上传分片 |
/api/cache/module/complete | POST | 完成分片上传 |