来自 #117 的架构级评审建议。不阻塞合入,仅供参考是否有更好的架构解法。
💡 [建议 · 错误处理] AppendLogs 失败后未停止后续处理 Raft/rpc.go:183
问题根因:follower 端 AppendEntries 中,AppendLogs 失败时仅 slog.Error 记录日志后继续执行 persistStateLocked、更新 commitIndex 和 applyCommittedLogs——WAL 写入失败意味着数据未持久化,继续应用日志会提交尚未持久化的 entry,崩溃重启后这些 entry 丢失,造成已提交日志的回滚(违背 Raft 持久化保证)。
为什么低级解法不够:简单的修复是 return err——但这会让 follower 返回 RPC error,leader 会认为 follower 失败然后重试,行为基本正确但 error propagation 不优雅。更深层的问题是:WAL 写入失败是致命的,follower 应该 panic 或转为 passive 状态(至少不应继续参与 consensus),而不是安静地吃掉错误。
架构级方案:将 WAL 写入定义为错误不可恢复路径:AppendLogs 失败时,follower 应 (a) 立即返回 RPC error 拒绝本次 AppendEntries(让 leader 重试),(b) 将该节点标记为"WAL 故障"状态,停止接受后续日志直到故障恢复。更激进的做法是直接 slog.Error + panic (类似 etcd 的做法),因为 Raft 的正确性依赖于持久化存储的正确性。本 PR 可先完成最小修复——让 AppendLogs 失败后返回 error,不做后续持久化。
代价/收益:直接 panic 适合 etcd 这样的生产级实现(fail-fast),代价是进程重启成本高;返回 RPC error 让 leader 重试更宽容,代价是错误路径的编程复杂度。建议本 PR 至少做到返回 error 并中止后续流程。
💡 [建议 · 错误处理] AppendLogs 失败后未停止后续处理
Raft/rpc.go:183问题根因:follower 端
AppendEntries中,AppendLogs失败时仅slog.Error记录日志后继续执行persistStateLocked、更新commitIndex和applyCommittedLogs——WAL 写入失败意味着数据未持久化,继续应用日志会提交尚未持久化的 entry,崩溃重启后这些 entry 丢失,造成已提交日志的回滚(违背 Raft 持久化保证)。为什么低级解法不够:简单的修复是 return err——但这会让 follower 返回 RPC error,leader 会认为 follower 失败然后重试,行为基本正确但 error propagation 不优雅。更深层的问题是:WAL 写入失败是致命的,follower 应该 panic 或转为 passive 状态(至少不应继续参与 consensus),而不是安静地吃掉错误。
架构级方案:将 WAL 写入定义为错误不可恢复路径:
AppendLogs失败时,follower 应 (a) 立即返回 RPC error 拒绝本次 AppendEntries(让 leader 重试),(b) 将该节点标记为"WAL 故障"状态,停止接受后续日志直到故障恢复。更激进的做法是直接slog.Error+panic(类似 etcd 的做法),因为 Raft 的正确性依赖于持久化存储的正确性。本 PR 可先完成最小修复——让AppendLogs失败后返回 error,不做后续持久化。代价/收益:直接 panic 适合 etcd 这样的生产级实现(fail-fast),代价是进程重启成本高;返回 RPC error 让 leader 重试更宽容,代价是错误路径的编程复杂度。建议本 PR 至少做到返回 error 并中止后续流程。