工程效率与质量 / 进阶
CI 缓存别靠运气:设计可解释的缓存键与恢复策略
区分依赖缓存与构建产物缓存,设计完整缓存键,并避免旧缓存制造难以复现的结果。
先分清缓存对象
包管理器下载缓存可以跨分支复用,构建产物缓存则依赖源码、配置和工具链。把两者塞进同一个键会导致命中率低,也很难判断某次命中为什么有效。node_modules 通常不适合作为长期缓存,优先缓存包管理器的内容寻址仓库。
依赖下载缓存:锁文件、操作系统、包管理器版本;TypeScript 构建缓存:源码、tsconfig、编译器版本;测试缓存:源码、测试配置、运行时版本;容器层缓存:Dockerfile、构建上下文和基础镜像。
缓存键必须解释结果
精确键未命中时,可以恢复较旧的下载缓存,因为安装命令仍会依据锁文件校验并补齐内容。构建产物不应使用过宽的 restore key,除非构建工具本身会验证每个任务输入。
EXAMPLE / 02CI/CD
key: >-
pnpm-${{ runner.os }}-
node20-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
pnpm-${{ runner.os }}-node20-监控缓存,不要只看流水线总时长
记录命中率、下载耗时、解压耗时和缓存体积。一个 4 GB 的缓存即使命中,也可能比重新构建更慢。定期从空缓存运行基线任务,确认项目没有偷偷依赖缓存中的陈旧文件。
需要长期保存、审计或用于发布的文件应上传为带版本的 artifact,不能依赖可能被自动驱逐的缓存。