共享 Vite Task 缓存
Alchemy 管理 T3TaskCache 栈:t3-task-cache Worker、它的 D1 索引、私有 R2 存储桶、速率限制,以及清理计划。生产栈接管现有资源,并保留它们的数据和仓库授权策略。D1 与 R2 采用 retain 移除策略。
Worker 使用 Effect 服务来处理请求、开发者认证、有界并发、截止时间,以及清理。版本已固定的上游协议包提供实验性的线路格式、流式上传、GitHub OIDC 校验,以及原子化的 D1 发布。保留这个适配器(adapter),现有缓存条目就能继续兼容。
Vite+ 1.3 带有实验性的远程客户端(client),尽管它的 TypeScript 配置和 CLI 帮助里并没有写出这项能力。原生客户端会读取 run.cache.remote.url、VP_REMOTE_CACHE_URL 和 VP_REMOTE_CACHE。一份按需启用的 Node 预加载会在内存中修补 Vite+ 的 JavaScript 入口,并在原生任务运行器运行之前和之后调用一个可选的仓库钩子。全局可执行文件和 PATH 保持不变。Vite 配置把已保存的凭据载入内存中的远程 URL,钩子则选择原生缓存模式,并导出任务摘要。已保存的凭据绝不会被导出到任务环境(environment)里。普通的依赖安装不会修补 Vite+。钩子只在维护者显式安装时,或由我们自己的 CI 安装;没有凭据时,它既不采集遥测,也不把遥测加入队列。
部署
先用 vp install 安装依赖,再用 Alchemy 的 Cloudflare 配置档完成认证。也可以改为在环境中提供 CLOUDFLARE_ACCOUNT_ID 和 CLOUDFLARE_API_TOKEN。Alchemy 通过 Cloudflare.state() 远程保存部署状态。
cd infra/task-cache
alchemy deploy --stage prod该栈从 ~/.config/t3-task-cache/readers.json(或 READER_KEYS)读取只读凭据摘要,从 ~/.config/t3-task-cache/writers.json 读取写入方摘要,并把它们作为已脱敏的 Worker 密钥提供。请保留该文件,以便管理现有的写入方。部署使用 prod 阶段,以及 .task-cache.json 中的现有端点。上游协议只在 Worker 运行时内部加载,并由 Alchemy 打包,因此部署在 Node 上运行,不需要额外的 TypeScript 加载器。显式设置的环境变量 DEVELOPER_KEYS 会覆盖本地注册表,在没有该文件的部署主机上同样如此。两个来源都不存在时部署会失败,因此缺少本地文件不会悄无声息地撤销现有写入方。
生产数据库中的 scopes 和 deployment 记录控制着仓库身份、推送分支、配额和保留期限。pull_requests 显式启用针对已配置的 branch 的同仓库拉取请求(pull request)上传;writes_enabled 则禁用全部产物上传,维护者的上传也包括在内。遥测接受来自其他 PR 基分支的报告,但不会授予产物写入权限。单独置备一套缓存需要它自己的仓库策略;一个新建的空数据库并不会授予上传权限。
CI 的只读身份名为 ci-reader。它的原始令牌应放在 GitHub 仓库密钥 T3_TASK_CACHE_READ_TOKEN 中;readers.json 把该名称映射到令牌的 SHA-256 十六进制摘要。轮换时,先替换该摘要,再部署,然后替换 GitHub 密钥。请把可选的私有文件 ci-reader.json 保持为 { "name": "ci-reader", "token": "…" },供冒烟测试验证只读恢复;没有该文件时,冒烟测试使用操作者已保存的凭据。切勿提交这些文件。只读凭据不能上传产物或遥测。发布作业没有 OIDC,它们的遥测不会被记录。
添加贡献者或机器
node infra/task-cache/scripts/manage.ts add-writer maria这会更新本地写入方注册表,并写出 ~/.config/t3-task-cache/maria.json。运行 alchemy deploy --stage prod,从 infra/task-cache 执行,以应用该注册表,然后通过私密渠道传送该文件。接收者运行:
bash infra/task-cache/install.sh安装程序会提示输入端点、开发者名称和令牌(输入时隐藏),用一份空的遥测报告验证认证,并以 0600 模式保存凭据。它在主目录中一次性安装钩子和凭据,并通过 shell 启动文件中的 NODE_OPTIONS 启用预加载。安装完成后请打开一个新的 shell。新建的工作树(worktree)以及重新安装依赖,都会自动使用同一个钩子;不会改动任何检出或可执行文件。已经长时间运行的智能体需要重启,或者继承更新后的环境(environment)。再次运行安装程序会更新共享钩子,并会从旧的磁盘补丁里恢复相匹配的备份。
使用 bash infra/task-cache/install.sh --uninstall 卸载,然后重启现有的 shell 和智能体。这会移除 shell 集成、已保存的客户端凭据,以及队列中的遥测。对于仍继承其路径、并且已经在运行的 shell,会留下一个空操作预加载;不会有任何缓存代码经由它运行。用于管理的写入方摘要和已经签发的凭据文件会保留下来。
显式的环境设置优先:VP_REMOTE_CACHE_URL 可以包含经过 URL 编码的开发者名称和令牌,作为 HTTP Basic 凭据;VP_REMOTE_CACHE=read-write 启用上传。这些值可以由密钥管理器提供,而不使用已保存的凭据。不要把凭据放进 shell 的命令历史,也不要提交它们。不同的人使用不同的写入方名称。再次执行 add-writer 会轮换令牌;revoke-writer <name> 会移除它。再次部署,以应用其中任一更改。
我们仓库的 CI 会显式安装 Vite+ 钩子。目标为 main 的同仓库 PR,以及默认分支上的推送作业,都可以读写。目标为其他分支的 PR 只读;fork PR 不会获得缓存凭据,并在没有远程缓存的情况下照常运行。每一次产物读取都需要认证,包括按 blob ID 下载。受信任的维护者会自动上传产物,供其他已通过认证的维护者和 CI 取用。贡献者的机器和 CI 运行器上从不需要部署凭据。
验证
node infra/task-cache/scripts/smoke.ts
vp run --filter @t3tools/task-cache test
vp run --filter @t3tools/task-cache typecheck冒烟检查会上传一份唯一的夹具,把其输出恢复到一个冷的第二目录,更改其输入,并确认发生了重新计算。它还会检查匿名读取和上传都会被拒绝。可缓存的命令(command)必须通过 vp run 运行;直接调用 vp test 或 vp check 不会使用这个缓存。
原生客户端
/projects/t3code/native-clients/<platform>/<variant>/<expo-fingerprint> 把供 scripts/mobile-native-client.ts 使用的模拟器与仿真器开发客户端(client)存放在单独的 -native-clients R2 存储桶中,保留 30 天。与 Vite Task 条目不同,Expo 指纹可以跨机器移植,因此在任意一台 Mac 或 Linux 机器上构建的客户端,都能供其余每一台机器使用。Mobile Dev Client 工作流在 Blacksmith 上构建它们,并以写入方 ci-native-clients 的身份上传(先执行 node infra/task-cache/scripts/manage.ts add-writer ci-native-clients,再部署,然后把该文件写出的令牌放入仓库密钥 T3_NATIVE_CLIENT_WRITE_TOKEN);没有该密钥时,这个工作流什么也不会构建。读取方下载;写入方以 64 MB 为一片,通过 R2 分片上传,因为 Workers 限制了请求体的大小。上传器的包摘要会作为对象元数据保存,客户端在安装任何内容之前都会检查它。
当前限制
- Vite Task 会把不同的操作系统和 CPU 架构分开。macOS 上的结果不能满足 Linux CI;Linux 开发机可以填入与之兼容的 CI 结果。
- 当前协议为每个执行键保留一份结果。相互竞争的源版本可能会覆盖彼此的条目。复用之前会校验输入,因此这只会造成未命中,而不会命中过期结果。合并到 main 既不会提升缓存,也不会重置缓存。
- 本地命中不会发起远程请求,也不会事后补传。在测试上传一项已经被缓存的任务时,只清除某个隔离工作树(worktree)的任务缓存。
- 生产环境在 64 GB 预算内把结果保留七天。远程出错会回退为直接执行。
VP_REMOTE_CACHE=off vp run ...会在这一次调用中禁用远程层。
节省与追踪
Alchemy 还拥有 t3-task-cache-traces Axiom 数据集、该数据集只允许摄入的 Worker 令牌、一块节省仪表板,以及一个单独予以保留的 D1 数据库,用来保存终身计数器。部署时,除了 Cloudflare 凭据,还需要 Axiom 的 Alchemy 配置档,或者 AXIOM_TOKEN/AXIOM_ORG_ID。贡献者的机器永远不会拿到 Axiom 令牌。
vp run 和它的快捷方式 vpr 都会调用这个钩子。钩子会观察 Vite+ 以原子方式写入的 last-summary.json 文件,包括嵌套运行。它报告包名/任务名、已确认的本地命中和远程命中、省下的任务运行时间、开发者身份、主机、操作系统/架构,以及 CI 运行 ID。它不会发送命令、参数、源码路径、环境变量或任务输出。开发者身份来自已认证的缓存写入方;CI 使用 GitHub 已验证的 OIDC 行动者(actor)。同仓库的 PR 既可以上传产物,也可以上报遥测。fork 作业不会安装钩子,也不会上报遥测。
报告先暂存在本地,并在后续命令中重试。完成钩子会把最终摘要加入队列,失败的运行也包括在内。本地运行会启动一个分离出去的短时 Node 进程;CI 会等它在限定范围内完成刷新,然后运行器才可以终止它。采集或导出失败绝不会改变任务的退出状态。不会安装后台服务(background service)。队列最多保留 2,000 份报告,每份发送的超时为三秒,每次刷新的时间预算为 15 秒。稳定的报告 ID 和事务性的 D1 回执可以防止终身计数被重复累加。回执在 31 天后过期;被接受的报告必须是 30 天以内的。终身总计在原始追踪过期之后仍然保留,并且每小时重新导出,不活跃的开发者和主机也包括在内。采集从钩子安装之时开始;已有的缓存历史不会回填。
节省的时间,指的是被避免掉的历史任务执行时间。 它可以当作计算量的代理指标,但并不是测量得到的 CPU 时间,不是省下的钱,也不是开发者实际等待所流逝的时间。并行任务可能在时间上重叠,缓存查找和恢复的开销也不会被扣掉。恢复失败不计入节省。较旧的 Vite+ 版本不会给出命中来源;它们已确认的节省会记在一个明确标出的「未指定」层级下。原生摘要的采集是尽力而为:运行若被中断,或者摘要在被观察到之前就被覆盖,都可能出现缺失。这里对实验性的 Vite+ 摘要结构做了测试,升级 Vite+ 时必须再核对一次。
主机标签在存在 ~/.agents/MACHINE 时使用它,否则使用主机名。GitHub 托管的运行器会使用自己的操作系统/架构类别,以免每台临时虚拟机都新开一条终身序列。设置 T3_TASK_CACHE_HOST 可以覆盖该标签;设置 T3_TASK_CACHE_TELEMETRY=off 可以退出遥测,同时保留远程缓存。重新运行安装程序,即可更新现有机器上的钩子。
管理写入方时,实施管理的机器上必须备有完整的私有注册表。要在另一台机器上添加写入方,请先从其管理员那里安全地复制这份注册表。add-writer --new-registry 会显式初始化一个新缓存;切勿用它来恢复已有的注册表。
以文件系统权限运行的 Node 进程,必须放行已安装的预加载和钩子目录。如果某个受限进程无法放行这些文件,请从 NODE_OPTIONS 中去掉 task-cache 的导入。
本地遥测会在命令退出之前写入磁盘队列,再由一个短时的分离 Node 进程上传。端点缓慢或离线时,不会拖延 vp run;尚未发送的报告会在之后的一次运行中重试。