monorepo + Turborepo 缓存

发布于:2025-07-06 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

命中你的第一个 Turborepo 缓存

远程缓存

一个共享的缓存

Vercel

Vercel 上的远程缓存

工件完整性和真实性验证

远程缓存 API

Vercel 托管的远程缓存

自托管

什么会被缓存?

任务输出

日志

任务输入

问题排查

使用模拟运行

使用运行摘要

关闭缓存

覆盖缓存

缓存任务比执行任务慢


Turborepo 使用缓存来加速构建,确保你永远不会重复相同的工作。当你的任务可缓存时,Turborepo 将使用首次运行任务时的指纹从缓存中恢复任务结果。

当在本地工作时,Turborepo 的缓存可以显著节省时间 - 并且当启用远程缓存时会更加强大,可以在你的整个团队和 CI 之间共享缓存。

  • 如何命中你的第一个 Turborepo 缓存
  • 如何启用远程缓存
  • Turborepo 使用什么作为哈希的输入和输出
  • 如何解决缓存问题

须知:Turborepo 假设你的任务是确定性的。如果一个任务在 Turborepo 已知的输入集下能够产生不同的输出,则缓存可能无法按预期工作。

命中你的第一个 Turborepo 缓存

你可以通过三个步骤尝试 Turborepo 的缓存行为

创建一个新的 Turborepo 项目

使用 npx create-turbo@latest 并按照提示创建一个新的 Turborepo。

npx create-turbo@latest

首次运行构建

如果你已全局安装了 turbo,请在你的仓库中运行 turbo build。

或者,你可以使用你的包管理器在 package.json 中运行 build 脚本。

pnpm run build

 这将导致缓存未命中,因为你以前从未在这个仓库中使用这组输入运行过 turbo。输入被转换为哈希,以便在你的本地文件系统缓存或远程缓存中进行检查。

命中缓存

再次运行 turbo build。你将看到类似这样的消息

 

由于输入的指纹已在缓存中,因此无需再次从头开始重新构建你的应用程序。你可以从缓存中恢复先前构建的结果,从而节省资源和时间。

远程缓存

Turborepo 将任务结果存储在你机器上的 .turbo/cache 目录中。但是,你可以通过与你的团队成员和 CI 共享此缓存,使你的整个组织运行得更快。

Turborepo 的 任务缓存 通过避免重复工作来节省时间。

但有一个问题:缓存是本地的,仅限于你的机器。当你在使用持续集成系统时,这可能会导致大量重复工作

由于 Turborepo 默认只缓存到本地文件系统,即使所有任务输入都相同,相同的任务 (turbo run build) 也必须在每台机器上重新执行(你、你的队友、你的 CI、你的 PaaS 等),这会 浪费时间和资源

须知: 

你不需要使用远程缓存也能使用 Turborepo。虽然远程缓存会带来最显著的加速,但即使不使用远程缓存,你也可以加快现有工作流程。

一个共享的缓存

如果可以在你的整个团队(甚至你的 CI)中共享一个 Turborepo 缓存会怎么样?

Turborepo 可以安全地与远程缓存(一个存储你的任务结果的云服务器)通信。这可以通过避免在整个组织内重复工作来节省大量时间。

远程缓存是免费的,可以与 托管提供商 一起使用,也可以作为 自托管缓存 使用。

远程缓存是 Turborepo 的一项强大功能,但能力越大,责任越大。请首先确保你正确地进行了缓存,并仔细检查 环境变量的处理。另请记住,Turborepo 将日志视为工件,因此请注意你打印到控制台的内容。

Vercel

Vercel 远程缓存 在所有计划中均可免费使用,即使你没有在 Vercel 上托管你的应用程序。按照以下步骤为你的仓库启用远程缓存。

本地开发

要将你的本地 Turborepo 链接到你的远程缓存,请使用你的 Vercel 帐户验证 Turborepo CLI。

turbo login

如果你没有全局安装 turbo,你也可以使用你的包管理器

pnpm dlx turbo login

 如果你的远程缓存配置为使用单点登录,你需要运行 npx turbo login --sso-team=team-name 以获取具有正确权限的缓存令牌。

现在,将你的 Turborepo 链接到你的远程缓存

turbo link

启用后,对你当前正在缓存的包进行一些更改,并使用 turbo run 对其运行任务。你的缓存工件现在将存储在本地和你的远程缓存中。

要验证,请使用以下命令删除你的本地 Turborepo 缓存

rm -rf ./.turbo/cache

然后,再次运行相同的构建。如果一切正常,turbo 应该不会在本地执行任务。相反,它将从你的远程缓存下载日志和工件,并将它们回放给你。、

Vercel 上的远程缓存

如果你正在 Vercel 上构建和托管你的应用程序,一旦你使用 turbo,远程缓存将代表你自动设置。有关更多信息,请参阅 Vercel 文档。

工件完整性和真实性验证

Turborepo 可以在将工件上传到远程缓存之前使用密钥对其进行签名。Turborepo 在工件上使用 HMAC-SHA256 签名,密钥由你提供。Turborepo 将在下载远程缓存工件时验证其完整性和真实性。任何未能验证的工件都将被忽略,并被 Turborepo 视为缓存未命中。

要启用此功能,请在你的 turbo.json 配置文件中设置 remoteCache 选项,以包含 signature: true。然后通过声明 TURBO_REMOTE_CACHE_SIGNATURE_KEY 环境变量来指定你的密钥。
 

{
  "remoteCache": {
    "signature": true
  }
}

远程缓存 API

任何符合 Turborepo 远程缓存 API 规范的 HTTP 服务器都可以实现远程缓存。

Vercel 托管的远程缓存

Vercel,Turborepo 的创建者和维护者,提供了一个与 Turborepo 完全兼容的托管远程缓存。

使用 Vercel 远程缓存 是零配置的,并通过开源 Vercel 远程缓存 SDK 自动与 Vercel 部署 集成。

了解更多关于 Vercel 上的 Turborepo 的信息,或 免费部署一个模板 来试用。

自托管

你也可以自托管你自己的远程缓存,并使用 --manual 标志登录以提供 API URL、团队和令牌信息。

turbo login --manual

现在,当你运行任务时,Turborepo 将自动将任务的输出发送到远程缓存。如果你在另一台也已通过身份验证连接到你的远程缓存的机器上运行相同的任务,它将在首次运行该任务时命中缓存。

默认情况下,Turborepo 使用Vercel 远程缓存,无需任何配置。如果你想使用不同的远程缓存,请访问远程缓存 API 文档

什么会被缓存?

Turborepo 缓存两种类型的输出:任务输出和日志。

任务输出

Turborepo 缓存任务的文件输出,这些文件输出在 turbo.json 的 outputs 键中定义。当缓存命中时,Turborepo 将从缓存中恢复文件。

outputs 键是可选的,有关 Turborepo 在这种情况下如何表现,

  "tasks": {
    "build": {
      // Cache all files emitted to the packages's `dist` directory
      "outputs": ["dist/**"]
    }
  }
}

相对于包的 package.json 的文件 glob 模式列表,用于在任务成功完成后缓存。

以 $TURBO_ROOT$ 开头的文件 glob 将更改 glob,使其相对于仓库的根目录而不是包目录。

{
  "tasks": {
    "check-types": {
      // Consider all Typescript files in `src/` and the root tsconfig.json as inputs
      "inputs": ["$TURBO_ROOT$/tsconfig.json", "src/**/*.ts"]
    }
  }
}

默认值:true

省略此键或传递空数组会告诉 turbo 不缓存任何内容(除了日志,在启用缓存时始终缓存日志)。

{
  "tasks": {
    "build": {
      "outputs": [".svelte-kit/**", "dist/**"] // File outputs will be cached
    },
    "dev": {
      "cache": false, // No outputs will be cached
      "persistent": true
    }
  }
}

提供文件输出

如果你没有为任务声明文件输出,Turborepo 将不会缓存它们。对于某些任务(如 linters)来说,这可能没问题 - 但许多任务会生成你希望被缓存的文件。如果你在缓存命中时遇到文件不可用的错误,请确保你已为你的任务定义了输出。

日志

Turborepo 始终捕获你的任务的终端输出,并将这些日志从任务首次运行时恢复到你的终端。

你可以使用--output-logs 标志或outputLogs 配置选项来配置重放日志的详细程度。

{
  "tasks": {
    "build": {
      "outputLogs": "new-only"
    }
  }
}

 

任务输入

输入由 Turborepo 哈希处理,为任务运行创建一个“指纹”。当“指纹”匹配时,运行任务将命中缓存。

在底层,Turborepo 创建两个哈希:全局哈希和任务哈希。如果任何一个哈希发生更改,任务将缓存未命中。

全局哈希输入

包哈希输入 

 

问题排查

使用模拟运行

Turborepo 有一个 --dry 标志,可用于查看如果你运行一个任务但不实际运行它会发生什么。当你无法确定你正在运行哪些任务时,这对于调试缓存问题非常有用。

而不是执行任务,显示有关将要运行的软件包和任务的详细信息。

指定 --dry=json 以 JSON 格式获取输出。

使用运行摘要

Turborepo 有一个 --summarize 标志,可用于获取任务的所有输入、输出等的概述。比较两个摘要将显示为什么两个任务的哈希不同。这对于以下方面非常有用

  • 调试输入:Turborepo 中的任务有很多输入。如果任务在你期望命中的时候缓存未命中,你可以使用运行摘要来检查哪些输入与你预期的不同。
  • 调试输出:如果缓存命中没有恢复你期望的文件,运行摘要可以帮助你了解哪些输出正在从缓存中恢复。

在 .turbo/runs 中生成一个 JSON 文件,其中包含有关运行的元数据,包括

  • 受影响的软件包
  • 已执行的任务(包括其计时和哈希值)
  • 缓存工件中包含的所有文件

此标志有助于调试以确定诸如以下内容:

  • turbo 如何解释你的 inputs 和 outputs 的 glob 语法
  • 两次任务运行之间更改了哪些输入以产生缓存未命中
  • 任务计时如何随时间变化
turbo run build --summarize

    关闭缓存

    有时,你可能不想将任务的输出写入缓存。可以使用 "cache": false 为任务永久设置此项,或使用 --no-cache 标志为整个运行设置此项。

    覆盖缓存

    如果你想强制 turbo 重新执行已缓存的任务,请使用 --force 标志。请注意,这会禁用读取缓存,而不是写入。

    缓存任务比执行任务慢

    可能会创建一些场景,在这些场景中,缓存最终比不缓存更慢。这些情况很少见,但一些示例包括

    • 执行速度极快的任务:如果任务的执行速度比到远程缓存的网络往返时间还快,你应该考虑不缓存该任务。
    • 输出资产巨大的任务:可能会创建一个非常大的工件,以至于上传或下载它的时间超过了重新生成它的时间,例如完整的 Docker 容器。在这些情况下,你应该考虑不缓存该任务。
    • 具有自己缓存的脚本:某些任务具有自己的内部缓存行为。在这些情况下,配置可能会很快变得复杂,以使 Turborepo 的缓存和应用程序缓存协同工作。

    虽然这些情况很少见,但请务必测试你的项目的行为,以确定在特定位置禁用缓存是否能提供性能优势。