Skip to main content

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}