raft_rust/error.rs
1// 用于实现 Display,向客户端/日志输出可读错误信息
2use std::fmt::Display;
3
4// 错误需可序列化,以便经网络作为 ClientReply 返回
5use serde::{Deserialize, Serialize};
6
7/// Raft 库错误类型。
8// 可克隆/比较/序列化,便于在协议响应与测试断言中使用
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
10// 全库统一错误枚举:可经 ClientResponse 回传客户端
11pub enum Error {
12 /// 操作被中止,必须重试。常见于 Raft 领导者变更等场景。
13 /// 用此错误代替在 Raft 内实现复杂的重试与重放保护逻辑。
14 Abort,
15 /// 非法数据,通常是解码错误或意外的内部值。
16 InvalidData(String),
17 /// 非法用户输入。
18 InvalidInput(String),
19 /// IO 错误。
20 IO(String),
21// Error 枚举定义结束
22}
23
24// 接入标准 Error trait,便于与 ? 及错误链生态互操作
25impl std::error::Error for Error {}
26
27// 将错误渲染为人类可读字符串(日志、CLI、ClientReply)
28impl Display for Error {
29 // 按变体拼出固定英文前缀,便于脚本/人类统一识别
30 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
31 // 按错误类别输出固定英文前缀 + 细节
32 match self {
33 // 领导者变更等场景:提示操作被中止需重试
34 Error::Abort => write!(f, "operation aborted"),
35 // 协议/存储解码或内部不变量被破坏
36 Error::InvalidData(msg) => write!(f, "invalid data: {msg}"),
37 // 配置或客户端输入不合法
38 Error::InvalidInput(msg) => write!(f, "invalid input: {msg}"),
39 // 磁盘、网络、channel 等本地 IO 故障
40 Error::IO(msg) => write!(f, "io error: {msg}"),
41 // match 分支穷尽
42 }
43 // fmt 结束
44 }
45// Display impl 结束
46}
47
48// 状态机确定性分类等与 Error 相关的方法
49impl Error {
50 /// 判断错误是否为确定性错误。
51 ///
52 /// Raft 状态机应用需要知道命令失败是否由输入命令本身决定:
53 /// 若是,则该命令可视为已应用,错误可返回给客户端;
54 /// 否则状态机必须 panic,以免节点状态分叉。
55 pub fn is_deterministic(&self) -> bool {
56 // 分类判断:只有输入类错误可安全回传且不导致状态分叉
57 match self {
58 // Abort 不会在应用阶段发生,只在领导者变更时出现。
59 Error::Abort => false,
60 // 可能是本节点本地数据损坏。
61 Error::InvalidData(_) => false,
62 // 输入错误(大概率)是确定性的。
63 Error::InvalidInput(_) => true,
64 // IO 错误通常是节点本地问题(例如磁盘故障)。
65 Error::IO(_) => false,
66 // is_deterministic 的 match 结束
67 }
68 // is_deterministic 结束
69 }
70// Error 方法 impl 结束
71}
72
73/// 按格式字符串构造 `Error::InvalidData`。
74// 导出宏:在协议解码/日志损坏等路径快速构造 InvalidData
75#[macro_export]
76// 解码/内部不变量失败时的便捷构造入口
77macro_rules! errdata {
78 // 将 format! 参数包装为 InvalidData,并 .into() 成 Result
79 ($($args:tt)*) => { $crate::error::Error::InvalidData(format!($($args)*)).into() };
80// errdata 宏定义结束
81}
82
83/// 按格式字符串构造 `Error::InvalidInput`。
84// 导出宏:在配置/客户端参数校验路径快速构造 InvalidInput
85#[macro_export]
86// 用户输入/配置校验失败时的便捷构造入口
87macro_rules! errinput {
88 // 将 format! 参数包装为 InvalidInput,并 .into() 成 Result
89 ($($args:tt)*) => { $crate::error::Error::InvalidInput(format!($($args)*)).into() };
90// errinput 宏定义结束
91}
92
93/// 返回 [`Error`] 的 Result 别名。
94// 全库统一的 Result 类型,减少重复书写
95pub type Result<T> = std::result::Result<T, Error>;
96
97// 允许 `return error.into()` 直接得到 Err(error)
98impl<T> From<Error> for Result<T> {
99 // 配合 errdata!/errinput! 的 `.into()` 直接变成 Err
100 fn from(error: Error) -> Self {
101 // 将 Error 包装为 Result::Err,配合 errdata!/errinput! 宏使用
102 Err(error)
103 // From<Error> for Result 结束
104 }
105// Result 的 From impl 结束
106}
107
108// bincode 解码失败映射为 InvalidData(线路/日志载荷损坏)
109impl From<bincode::error::DecodeError> for Error {
110 // 线路或持久化载荷无法按约定格式解码
111 fn from(err: bincode::error::DecodeError) -> Self {
112 // 序列化格式错误视为数据不合法,而非本地 IO
113 Error::InvalidData(err.to_string())
114 // DecodeError 转换结束
115 }
116// From DecodeError 结束
117}
118
119// bincode 编码失败映射为 InvalidData(理论上少见,多为类型不兼容)
120impl From<bincode::error::EncodeError> for Error {
121 // 编码路径异常(类型变更/不兼容)同样归数据问题
122 fn from(err: bincode::error::EncodeError) -> Self {
123 // 编码失败同样归类为数据问题
124 Error::InvalidData(err.to_string())
125 // EncodeError 转换结束
126 }
127// From EncodeError 结束
128}
129
130// 阻塞接收 channel 断开 → IO(对端线程退出)
131impl From<crossbeam::channel::RecvError> for Error {
132 // 阻塞 recv 时对端已关闭通道
133 fn from(err: crossbeam::channel::RecvError) -> Self {
134 // 通道关闭视作节点内通信 IO 故障
135 Error::IO(err.to_string())
136 // RecvError 转换结束
137 }
138// From RecvError 结束
139}
140
141// 发送 channel 失败 → IO(接收端已丢弃)
142impl<T> From<crossbeam::channel::SendError<T>> for Error {
143 // 发送时接收端已 drop,消息无法投递
144 fn from(err: crossbeam::channel::SendError<T>) -> Self {
145 // 无法投递出站消息(如 Node 驱动线程退出)
146 Error::IO(err.to_string())
147 // SendError 转换结束
148 }
149// From SendError 结束
150}
151
152// 非阻塞接收错误(空或断开)→ IO
153impl From<crossbeam::channel::TryRecvError> for Error {
154 // try_recv 在空队列或断开时都会落到此映射
155 fn from(err: crossbeam::channel::TryRecvError) -> Self {
156 // try_recv 失败统一映射,调用方再按需区分
157 Error::IO(err.to_string())
158 // TryRecvError 转换结束
159 }
160// From TryRecvError 结束
161}
162
163// 非阻塞发送错误 → IO
164impl<T> From<crossbeam::channel::TrySendError<T>> for Error {
165 // 有界通道满或已关闭时的非阻塞发送失败
166 fn from(err: crossbeam::channel::TrySendError<T>) -> Self {
167 // 有界通道满或已关闭
168 Error::IO(err.to_string())
169 // TrySendError 转换结束
170 }
171// From TrySendError 结束
172}
173
174// 标准库 IO 错误(磁盘/网络)直接映射
175impl From<std::io::Error> for Error {
176 // 文件、套接字等系统调用失败统一为 IO 变体
177 fn from(err: std::io::Error) -> Self {
178 // 文件、socket 等系统调用失败
179 Error::IO(err.to_string())
180 // std::io::Error 转换结束
181 }
182// From std::io::Error 结束
183}
184
185// 切片转固定数组失败(长度不符)→ 数据损坏/格式错误
186impl From<std::array::TryFromSliceError> for Error {
187 // 长度前缀/定长字段截取时字节数不够
188 fn from(err: std::array::TryFromSliceError) -> Self {
189 // 例如长度前缀解析时字节数不足
190 Error::InvalidData(err.to_string())
191 // TryFromSliceError 转换结束
192 }
193// From TryFromSliceError 结束
194}
195
196// UTF-8 校验失败 → 数据不合法
197impl From<std::string::FromUtf8Error> for Error {
198 // 期望文本的二进制字段不是合法 UTF-8
199 fn from(err: std::string::FromUtf8Error) -> Self {
200 // 期望字符串的二进制字段非法
201 Error::InvalidData(err.to_string())
202 // FromUtf8Error 转换结束
203 }
204// From FromUtf8Error 结束
205}
206
207// 整数范围转换失败 → 数据/协议字段越界
208impl From<std::num::TryFromIntError> for Error {
209 // 协议索引/长度等字段无法安全窄化
210 fn from(err: std::num::TryFromIntError) -> Self {
211 // 索引/长度等字段无法安全转换
212 Error::InvalidData(err.to_string())
213 // TryFromIntError 转换结束
214 }
215// From TryFromIntError 结束
216}
217
218// 互斥锁中毒:另一线程在持锁时 panic,本节点状态已不可信
219impl<T> From<std::sync::PoisonError<T>> for Error {
220 // 锁中毒说明节点内不变量可能已破坏,直接致命退出
221 fn from(err: std::sync::PoisonError<T>) -> Self {
222 // 只有在其他线程持有互斥锁时 panic 才会发生。
223 // 这种情况应视为致命错误,因此这里同样 panic。
224 panic!("{err}")
225 // PoisonError 处理结束
226 }
227// From PoisonError 结束
228}