调试远程缓存命中以进行远程执行

报告问题 查看源代码 每夜版 · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

本页面介绍了如何检查缓存命中率,以及如何在远程执行环境中调查缓存未命中问题。

本页面假设您已成功利用远程执行完成构建和/或测试,并且希望确保有效利用远程缓存。

查看缓存命中率

在 Bazel 运行的标准输出中,查看列出进程(大致对应于 Bazel 操作)的 INFO 行。该行详细说明了操作的运行位置。查找 remote 标签(表示远程执行的操作)、linux-sandbox(表示在本地沙盒中执行的操作)以及其他值(表示其他执行策略)。结果来自远程缓存的操作显示为 remote cache hit

例如:

INFO: 11 processes: 6 remote cache hit, 3 internal, 2 remote.

在此示例中,有 6 次远程缓存命中,有 2 个操作未命中缓存并已在远程执行。可以忽略内部的 3 部分。 它通常是微小的内部操作,例如创建符号链接。此摘要中不包含本地缓存命中。如果您获得的进程数为 0(或低于预期),请运行 bazel clean,然后运行 build/测试命令。

排查缓存命中问题

如果您未获得预期的缓存命中率,请执行以下操作:

确保重新运行相同的 build/test 命令会产生缓存命中

  1. 运行您希望填充缓存的构建和/或测试。首次在特定堆栈上运行新 build 时,您预计不会有任何远程缓存命中。在远程执行过程中,操作结果会存储在缓存中,后续运行应会提取这些结果。

  2. 运行 bazel clean。此命令会清除您的本地缓存,这样一来,您便可以在结果不被本地缓存命中掩盖的情况下调查远程缓存命中。

  3. 再次运行要调查的 build 和测试(在同一台机器上)。

  4. 检查 INFO 行的缓存命中率。如果您除了 remote cache hitinternal 之外没有看到任何进程,则说明您的缓存正在被正确填充和访问。在这种情况下,请跳到下一部分。

  5. 差异的可能来源是 build 中的某些非密封内容,导致操作在两次运行中收到不同的操作键。如需查找这些操作,请执行以下操作:

    a. 重新运行有问题的 build 或测试,以获取执行日志:

      bazel clean
      bazel --optional-flags build //your:target --execution_log_compact_file=/tmp/exec1.log

    b. 比较两次运行之间的执行日志。确保两个日志文件中的操作完全相同。 差异可提供有关两次运行之间发生的更改的线索。请更新您的 build 以消除这些差异。

    如果您能够解决缓存问题,并且现在重复运行会产生所有缓存命中,请跳到下一部分。

    如果操作 ID 完全相同,但没有缓存命中,则说明您的配置中存在阻止缓存的因素。请继续阅读本部分,以检查是否存在常见问题。

  6. 检查执行日志中的所有操作是否都将 cacheable 设置为 true。如果执行日志中未显示 cacheable,则表示相应规则可能在 BUILD 文件中的定义中包含 no-cache 标记。查看执行日志中的 mnemonictarget_label 字段,以帮助确定操作的来源。

  7. 如果操作相同且为 cacheable,但没有缓存命中,则可能是您的命令行包含 --noremote_accept_cached,这会针对 build 停用缓存查找。

    如果难以确定实际的命令行,请使用 Build Event Protocol 中的规范命令行,如下所示:

    a. 将 --build_event_text_file=/tmp/bep.txt 添加到 Bazel 命令中,以获取日志的文本版本。

    b. 打开日志的文本版本,然后搜索包含 command_line_label: "canonical"structured_command_line 消息。它将列出展开后的所有选项。

    c. 搜索 remote_accept_cached,并检查其是否设置为 false

    d. 如果 remote_accept_cachedfalse,请确定它是在命令行还是在 bazelrc 文件中设置为 false 的。

确保在所有机器上进行缓存

在同一台机器上按预期发生缓存命中后,在另一台机器上运行相同的 build/测试。如果您怀疑缓存未在多台机器上进行,请执行以下操作:

  1. 对 build 进行小幅修改,以避免命中现有缓存。

  2. 在第一台机器上运行 build:

     bazel clean
     bazel ... build ... --execution_log_compact_file=/tmp/exec1.log
  3. 在第二台机器上运行 build,确保包含第 1 步中的修改:

     bazel clean
     bazel ... build ... --execution_log_compact_file=/tmp/exec2.log
  4. 比较两次运行的执行日志。如果日志不完全相同,请调查 build 配置是否存在差异,以及主机环境中的属性是否泄漏到任一 build 中。

比较执行日志

执行日志包含构建期间执行的操作的记录。每条记录都描述了操作的输入(不仅包括文件,还包括命令行实参、环境变量等)和输出。因此,检查日志可以揭示操作重新执行的原因。

执行日志可以采用以下三种格式之一生成:紧凑 (--execution_log_compact_file)、二进制 (--execution_log_binary_file) 或 JSON (--execution_log_json_file)。建议使用紧凑格式,因为这种格式生成的文件小得多,运行时开销也很小。以下说明适用于任何格式。您还可以使用 //src/tools/execlog:converter 工具在它们之间进行转换。

如需比较未按预期共享缓存命中的两个 build 的日志,请执行以下操作:

  1. 从每个 build 中获取执行日志,并将其存储为 /tmp/exec1.log/tmp/exec2.log

  2. 下载 Bazel 源代码并构建 //src/tools/execlog:parser 工具:

    git clone https://github.com/bazelbuild/bazel.git cd bazel bazel build //src/tools/execlog:parser

  3. 使用 //src/tools/execlog:parser 工具将日志转换为人类可读的文本格式。在这种格式中,第二个日志中的操作会按第一个日志中的顺序排序,从而更便于比较。

    bazel-bin/src/tools/execlog/parser \
      --log_path=/tmp/exec1.log \
      --log_path=/tmp/exec2.log \
      --output_path=/tmp/exec1.log.txt \
      --output_path=/tmp/exec2.log.txt
    
  4. 使用您喜欢的文本差异比较工具来比较 /tmp/exec1.log.txt/tmp/exec2.log.txt