缓存任务
每个 JavaScript 或 TypeScript 代码库都需要运行 package.json
脚本,例如 build
、test
和 lint
。在 Turborepo 中,我们称这些为 **任务**。
Turborepo 可以缓存您的任务结果和日志 - 为缓慢的任务带来巨大的提速。
缓存丢失
您代码库中的每个任务都有 **输入** 和 **输出**。
- 一个
build
任务可能具有源文件作为输入,并将日志输出到stderr
和stdout
以及捆绑文件。 - 一个
lint
或test
任务可能具有源文件作为输入,并将日志输出到stdout
和stderr
。
假设您使用 Turborepo 运行 build
任务,使用 turbo run build
-
Turborepo 将 **评估您的任务输入** 并 **将其转换为哈希**(例如
78awdk123
)。 -
**检查本地文件系统缓存** 中是否有匹配的缓存工件(例如
./node_modules/.cache/turbo/78awdk123.tar.zst
)。 -
如果 Turborepo 没有找到与计算出的哈希匹配的工件,Turborepo 将 **执行任务**。
-
任务成功完成后,Turborepo 将 **保存所有指定的输出**(包括文件和日志)到一个新的缓存工件中,该工件由哈希寻址。
Turborepo 在创建哈希时会考虑很多信息:依赖关系图、它所依赖的任务、源文件、环境变量等等!
命中缓存
假设您再次运行任务,但没有更改任何输入
-
**哈希将相同**,因为 **输入没有改变**(例如
78awdk123
) -
Turborepo 将找到具有匹配哈希的缓存工件(例如
./node_modules/.cache/turbo/78awdk123.tar.zst
) -
**而不是运行任务**,Turborepo 将 **重放输出** - 将保存的日志打印到
stdout
并将保存的输出文件恢复到它们在文件系统中的相应位置。
从缓存中恢复文件和日志几乎是瞬间完成的。这可以将您的构建时间从几分钟或几小时缩短到几秒或几毫秒。虽然具体结果会因代码库依赖关系图的形状和粒度而异,但大多数团队发现,使用 Turborepo 的缓存可以将他们每月的总构建时间缩短约 40-85%。
关闭缓存
在某些环境中,您可能不希望写入缓存输出。要禁用缓存写入,请在任何命令后附加 --no-cache
。例如,这将在所有工作区中运行 dev
(以及它 dependsOn
的所有任务),但不会缓存输出。
turbo run dev --no-cache
请注意,--no-cache
禁用缓存写入,但不禁用缓存读取。如果您想禁用缓存读取,请使用 --force
标志。
您还可以通过将 pipeline.<task>.cache
配置设置为 false
来配置特定任务以跳过写入缓存。
{
"$schema": "https://turbo.rust-lang.net.cn/schema.json",
"pipeline": {
"dev": {
"cache": false,
"persistent": true
}
}
}
强制覆盖缓存
相反,如果您想禁用读取缓存并强制 turbo
重新执行以前缓存的任务,请添加 --force
标志。
# Run `build` npm script in all workspaces ignoring the cache.
turbo run build --force
请注意,--force
禁用缓存读取,但不禁用缓存写入。如果您想禁用缓存写入,请使用 --no-cache
标志。
处理 Node.js 版本
为了考虑 Node.js 版本,请使用 package.json 中的 engines
键 (在新标签页中打开)。Turborepo 将看到对 package.json
的更改,并在该字段更新时错过缓存。
处理平台和其他任意哈希贡献者
对于高级用例,您可能希望操作系统 (OS)、体系结构或其他外部因素对您的哈希做出贡献。您可以通过四个简单的步骤来做到这一点。
1. 将任意文件写入磁盘
首先,创建一个脚本,该脚本考虑您感兴趣的哈希贡献者。例如,以下是一个 Node.js 脚本,它查看平台和体系结构并将这些详细信息写入文件 (turbo-cache-key.json
)
#!/usr/bin/env node
const { writeFileSync } = require('fs');
const { join } = require('path');
const { platform, arch } = process;
const file = "turbo-cache-key.json";
const str = JSON.stringify({ platform, arch });
console.log(`Generating cache key: ${str}`);
writeFileSync(file, str);
2. 将文件添加到您的 .gitignore
您不希望将此文件提交到源代码管理,因为它依赖于环境。将其添加到您的 .gitignore
中。
// .gitignore
+ turbo-cache-key.json
3. 将文件添加到哈希
现在,确保 turbo
了解该文件,方法是将其添加到任务输入中。您可以通过两种方式做到这一点。
- 对于特定任务:将文件包含在 任务的
inputs
数组 中。
{
"$schema": "https://turbo.rust-lang.net.cn/schema.json",
"pipelines": {
"build-for-platforms": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", "turbo-cache-key.json"]
}
}
}
- 对于所有任务:将文件添加到
globalDependencies
中。
{
"$schema": "https://turbo.rust-lang.net.cn/schema.json",
"globalDependencies": ["turbo-cache-key.json"],
"pipelines": {
...
}
}
4. 在运行 turbo
之前生成文件
最后,您需要确保在运行 turbo
之前运行脚本。例如
{
"scripts": {
"build-for-platforms": "node ./scripts/create-turbo-cache-key.js && turbo run build"
}
}
turbo run build
现在将在计算 build
任务的哈希时考虑 turbo-cache-key.json
的内容。
日志
不仅 turbo
缓存了任务的输出,它还记录了终端输出(即组合的 stdout
和 stderr
)到 (<package>/.turbo/run-<command>.log
)。当 turbo
遇到缓存的任务时,它将重新播放输出,就好像它再次发生一样,但速度很快,包名称略微变暗。
哈希
到目前为止,您可能想知道 turbo
如何决定对于给定任务,什么构成缓存命中与未命中。好问题!
首先,turbo
会构建代码库当前全局状态的哈希。这包括以下内容:
- 满足
globalDependencies
中的 glob 模式的所有文件的哈希值。 globalEnv
中列出的环境变量的值。- 来自
turbo.json
、package.json
和任何锁定文件的某些信息。 - 等等!
然后,它会添加更多与给定工作区任务相关的因素。
- 工作区文件夹中所有版本控制文件的哈希值(或与
inputs
glob 匹配的文件,如果已配置)。 - 在
pipeline
中配置的outputs
。 - 所有已安装的
dependencies
、devDependencies
和optionalDependencies
的已解析版本集。 - 工作区任务的名称。
- 在
pipeline.<task>.env
列表中指定的已排序的环境变量键值对列表。 - 等等!
一旦 turbo
在其执行过程中遇到给定工作区的任务,它会检查缓存(本地和远程)以查找匹配的哈希。如果匹配,它将跳过执行该任务,将缓存的输出移动或下载到适当位置,并立即重新播放先前记录的日志。如果没有本地或远程缓存中存在与计算出的哈希匹配的内容,turbo
将在本地执行任务,然后缓存指定的 outputs
。
在执行时,任务可以使用环境变量 TURBO_HASH
获取其哈希值。此值可用于标记输出或 Dockerfile 等。