失败处理
Megascale 的 bootstrap 有三种结构性失败模式,每一种都会产生独特的日志特征和不同的传播路径。下面的行为是二进制实际实现的行为,由反汇编和字符串锚点恢复;rendezvous RPC 本身没有重试策略。
失败 1:工作进程在 GetMultiSliceTopology 上超时
工作进程的 gRPC 截止时间来自 FLAGS_megascale_topology_discovery_timeout(位于 +0x34..+0x40 的 Options 字段,一个 absl::Duration)。当协调器的 TopologyCoordinator 积累到 quorum 之前截止时间触发时,工作进程的 stub 会向 DiscoverTopologyAndAddressBindings (xla::megascale::runtime::CommunicationBackend::DiscoverTopologyAndAddressBindings, 0x1ccacb80)返回 DEADLINE_EXCEEDED,随后它会:
- 通过在
0x1ccacea4构造的StatusOr<tuple<MultiSliceTopologyAndLocationProto, EndpointAddresses>>(DiscoverTopologyAndAddressBindings内部的absl::StatusOr<...>::StatusOr调用;参数设置在0x1ccacea1)将错误返回给 XLA Megascale 运行时。 工作进程侧不会发出"TopologyCoordinator: Unable to wait for all slices to connect..."日志;该字符串(0x96df1d4)由协调器的析构函数发出,见失败 2。 - 运行时调用
CommunicationBackend::ReportError(slice_id, host_id, MegaScaleRuntimeError{...})(xla::megascale::runtime::CommunicationBackend::ReportError(int, int, MegaScaleRuntimeError const&),0x1ccadbe0):这是MegaScaleTransport.ReportErrorgRPC (/xla.megascale.runtime.MegaScaleTransport/ReportError),会扇入协调器的ErrorReporter(见 错误聚合器)。MegaScaleRuntimeError携带error_message字段 (xla.megascale.runtime.MegaScaleRuntimeError.error_message)和一个ErrorType枚举 (MegaScaleRuntimeError.ErrorType);UNRECOVERABLE_ERROR成员是真实存在的,不过 bootstrap 超时消息文本是在运行时构造的,并不是固定的 rodata 字面量。 - 运行时向 PJRT 返回非 OK Status,PJRT 再将其暴露给用户代码。
如果设置了 --megascale_use_inplace_restart_for_error,运行时会尝试原地拆除并重新实例化 CommunicationBackend,而不是退出。否则进程退出,集群调度器重启它(Borg/GKE crash-loop),此时 bootstrap 从头重试。
失败 2:协调器一直没有看到足够的注册
协调器的 TopologyCoordinator 本身没有超时;Coordinator<> 基类不会调用 WaitForNotificationWithTimeout,也没有任何按实例计时器。bootstrap 时间的界限完全由每个工作进程附加的每 RPC 截止时间施加。因此,当注册不断迟到时会发生:
- 协调器位于
+0x88的 pending-callback vector 增长。 - 周期性的
ReportStatus()(xla::megascale::runtime::TopologyCoordinator::ReportStatus,0x213b7ba0)继续发出"MegaScale Topology Discovery in progress. Missing hosts (num_slices=<N>, num_hosts=<H>): [<list>]"(0x96df232),其中包含尚未登记的 slice/host 列表。 - 随着各个工作进程达到自己的截止时间,gRPC 框架取消其服务器端请求。当响应最终构建出来时,协调器 vector 中的 pending callback 仍会被调用,但调用现在落在已取消的
ServerCallbackUnaryImpl上,后者会静默丢弃响应。 - 当最慢的工作进程最终出现(或使用相同
(slice_id, host_id)的重启工作进程出现)时,协调器的IsComplete()转为 true,CreateResponse()运行,每个仍处于活动状态的 pending callback 都会收到响应。已取消的 callback 变成 no-op。
协调器不会中止。该进程继续运行,TopologyCoordinator 仍处于 state=1,准备接受缺失的注册。如果操作员判断 rendezvous 已无希望,必须从外部杀死协调器进程。
"TopologyCoordinator: Unable to wait for all slices to connect. The missing hosts (num_slices=<N>, num_hosts=<H>): [<list>]" 日志(0x96df1d4)只会在 TopologyCoordinator 未收敛就被销毁时发出,即由 xla::megascale::runtime::TopologyCoordinator::~TopologyCoordinator (0x1cf511a0)调用 GetMissingHosts() 并格式化剩余 slice/host 时发出。它不是由工作进程上的 DiscoverTopology... 发出,也不是由等待期间的 ReportStatus() 发出。
一条协调器特有的日志是 "Some workers didn't report an error after 5 minutes: ..." (rodata 0xa238672):它来自 ErrorReporter::ProcessErrorDigest() 路径 (xla::megascale::runtime::ErrorReporter::ProcessErrorDigest, 0x1ccb7140;见 错误聚合器),而不是来自 TopologyCoordinator 本身。它在错误聚合反过来超时时触发;拓扑聚合没有这样的日志。
失败 3:重新键控 / 漂移不匹配
当工作进程以不同的 topology_args、不同的 host_addresses 或不同的 incarnation_id 重新注册时,协调器会在两个位置捕获漂移:
所有三个漂移检查都位于同一个函数中: xla::megascale::runtime::TopologyCoordinator::ProcessRequest (0x1cf524c0),而不是拆分到单独的广播时路径中。两个 proto2::util::MessageDifferencer::Compare 调用(0x1cf5269d 和 0x1cf527a4,每个调用之前都有 set_message_field_comparison(1) + ReportDifferencesToString)驱动拓扑和主机地址比较;incarnation id 则内联比较。每个不匹配都是硬性 reject:下面的消息会被构建进 MakeErrorImpl<3> (INVALID_ARGUMENT) Status 并被返回(该函数在匹配位置 return Error),而不是记录日志后继续。三条错误消息是:
"Received topology that differs from previously registered topology at same sliceID. SliceID: $0 Previous HostId: $1 New HostId: $2 Addresses: $3 Diff: $4"(0x9b27486),当topology_args不同时触发(在0x1cf527a4处第二个Compare之后)。"Received host address mapping that differs from previous mapping SliceID: $0 HostId: $1 Prev Address: $2 New Addresses: $3"(0x9c14204),当host_addresses不同时触发(在0x1cf5269d处第一个Compare之后)。"Received incarnation ID that is different from previous incarnation ID. SliceID: $0 HostId: $1 Prev IncarnationId: $2 New IncarnationId: $3"(0x9c14456),当只有 incarnation id 发生变化时触发。这是静默工作进程重启的信号。
所有三条消息都由 absl::SubstituteAndAppendArray 组装(反编译中对应的字面长度分别为 153 / 122 / 139),进入返回的 INVALID_ARGUMENT Status。发生漂移的注册会被拒绝:违规工作进程的 GetMultiSliceTopology RPC 返回错误,而不是完成。协调器针对该 (slice_id, host_id) 的已接受状态保持不变;它不会采纳新的(漂移后的)值。因此,以相同 args 重新注册的工作进程仍会根据缓存响应解析,但发生漂移的工作进程会得到错误。
另有一个针对日志 unique id 的按 (slice,host) 单次去重机制: LogUniqueIds(内联到 xla::megascale::runtime::Communicator::Create,0x1cca9aa0)在 0x223717c0、0x223717c4、0x223717c8 处持有三个 int 槽位 last_ids.0/.1/.2,由 unique_id_mutex 保护。这套机制限制工作进程侧响应路径上的 unique-id 日志频率;它不会发出上面的三条协调器侧错误消息。
要强制真正重新 bootstrap,操作员必须杀死协调器进程;协调器重启后,新的 TopologyCoordinator 从空状态开始,每个工作进程都必须重新发出 GetMultiSliceTopology。
Barrier 失败
BarrierCoordinator 采用相同的 RPC 超时模型。barrier 超时作为 gRPC 客户端截止时间在带外携带,不是 proto 字段;BarrierRequest 字段 4 是 num_participants(会与协调器存储的数量校验),每次调用的截止时间来自 FLAGS_tf_tpu_preexecution_barrier_timeout(默认 30 s)。见 跨主机 Barrier §5。当参与者超时时:
- 工作进程侧记录本地 deadline-exceeded 错误。
- 当
BarrierCoordinator被销毁且并非所有参与者都已登记时,它的析构函数 (xla::megascale::runtime::BarrierCoordinator::~BarrierCoordinator,0x1cf55760)发出"BarrierCoordinator: Unable to wait for all slices to connect. Saw "(0xa1b93bb)`+ "<K>" + " of " + "<NumW>"- " expected participants. Seen hosts: " + GetSeenHosts()
。 三个 rodata 片段(Saw、of、expected participants. Seen hosts:)通过LogMessage::CopyToEncodedBuffer拼接,主机列表来自BarrierCoordinator::GetSeenHosts()。这与 topology coordinator 一样,是析构时未收敛模式,不是ReportStatus()` 发出。
- " expected participants. Seen hosts: " + GetSeenHosts()
- 运行时通过
ReportError将失败的 barrier 视为UNRECOVERABLE_ERROR。
如果 --xla_tpu_enable_megascale_barrier=false,整个 barrier 机制会被绕过: BarrierCoordinator 永远不会实例化,也不会发出 Barrier RPC。这是调试/测试开关;生产部署始终启用它。
HeartBeat 失败(bootstrap 后)
bootstrap 完成后,运行时会启动 StartHeartBeat() (0x1ccade60),定期 ping 每个 peer。心跳失败会沿以下路径级联:
"Failed to send heartbeat to <peer>"(0xa1ec1d8)"Failed to perform heartbeat check"(0x863656b)"Stop heartbeat check watchdog"(0x8655783)
三个由 flag 控制的中止策略:
| Flag | 心跳失败的影响 |
|---|---|
--megascale_use_inplace_restart_for_failed_heartbeat | "Aborting (w/ restart) due to failed heartbeat."(0x9ffa7d9)——运行时原地拆除并重建 CommunicationBackend |
--megascale_restart_on_failed_heartbeat (without inplace) | "Aborting (w/o restart) due to failed heartbeat."(0x9ffa7a9)——进程退出,由调度器重启 |
| Neither flag | "Terminating the process due to failed heartbeat."(0x9ffa778)——进程退出,不重启 |
心跳在逻辑上不是 bootstrap 的一部分;它只在 DiscoverTopologyAndAddressBindings 返回 OK 后运行。但它继承 bootstrap 期间构建的地址表,因此其失败模式会隐式验证该表。
传播到 ErrorReporter / RapidEye
每一种调用 ReportError 的 bootstrap 失败模式最终都会进入协调器的 MegascaleErrorAggregator(见 错误聚合器)。MegaScaleRuntimeError.ErrorType 携带 UNRECOVERABLE_ERROR 成员(这里未独立确认其数值序号)。聚合器的分类器会将其映射到 RapidEyeErrorDigestProto.Cause 枚举成员 UNRECOVERABLE_ERROR (与 BAD_TPU_CHIP、NETWORKING_ISSUE、DATA_INPUT_STALL 并列的真实 Cause 值),并从 xla::megascale::runtime::MegascaleErrorAggregator::LogErrorDigest (0x213b42c0)发出诊断模板 "Some workers have halted with an unrecoverable error: ..." (0xa23e476)。
如果设置了 --megascale_rapideye_error_digest_log_path,包含失败主机列表的 digest 会通过 CloudRapidEyeLogger 写入该路径。否则失败只会捕获在协调器上的 LOG(ERROR) 轨迹中。完整的保留和消费链见 错误聚合器。
不存在什么
- 没有 bootstrap 侧重试。工作进程不会在瞬时失败时内部重试
GetMultiSliceTopology。 - rendezvous 本身没有协调器侧超时。只要工作进程持续在各自每 RPC 截止时间前到达,rendezvous 就可以一直等待。
- 已组装拓扑没有磁盘持久化。协调器重启会丢失缓存。
- rendezvous payload 没有备用通道(UDS、共享内存)。所有内容都通过 TCP 上的 gRPC 传输。
- pending callback 没有有界队列。持有数万个 pending 工作进程的协调器进程会把它们全部保留在内存中。
交叉引用
- Bootstrap › 概览 — 本页枚举其失败路径的生命周期
- 收敛 — 这些失败从中分支出来的成功路径
- 工作进程注册 — 其校验会产生 INVALID_ARGUMENT 拒绝的请求
- 错误聚合器 — 每个
ReportError最终被分类进RapidEyeErrorDigestProto.Cause的位置 - ICI 交接 — 与 Megascale 路径并行运行的 tpunetd 侧失败表面