raft_rust/storage/engine.rs
1// Bound 用于前缀扫描的开闭区间;RangeBounds 为 scan 入参
2use std::ops::{Bound, RangeBounds};
3
4// Status 可序列化,便于经 Status 客户端请求返回
5use serde::{Deserialize, Serialize};
6
7// 引擎方法统一返回库 Result
8use crate::error::Result;
9
10/// 为键前缀生成扫描范围。
11///
12/// 排他上界通过对最后一个非 `0xff` 字节加 1 得到;若前缀全是 `0xff`,
13/// 则上界无界(其后不可能再有其它前缀)。
14pub fn prefix_range(prefix: &[u8]) -> (Bound<Vec<u8>>, Bound<Vec<u8>>) {
15 // 下界:包含该前缀本身
16 let start = Bound::Included(prefix.to_vec());
17 // 上界:找到最后一个可进位的字节,构造「下一前缀」作为排他端
18 let end = match prefix.iter().rposition(|&b| b != 0xff) {
19 // 将该字节 +1,前面字节保持,形成字典序上紧邻的下一区间起点
20 Some(i) => Bound::Excluded(
21 // 前缀前缀字节 + 进位字节拼出排他上界键
22 prefix.iter().take(i).copied().chain(std::iter::once(prefix[i] + 1)).collect(),
23 // Excluded 构造结束
24 ),
25 // 全 0xff:无法构造更大前缀,上界开放
26 None => Bound::Unbounded,
27 // match 结束,得到 end
28 };
29 // 返回半开/开闭组合范围,供 BTreeMap 等有序扫描使用
30 (start, end)
31// prefix_range 函数结束
32}
33
34/// 键值存储引擎,保存任意字节串。键按字典序维护,因此支持范围扫描。
35/// 例如:扫描某张表的全部行(共用键前缀),或扫描 Raft 日志尾部(给定索引之后)。
36///
37/// 只有在调用 [`Engine::flush()`] 之后,写入才保证落盘。
38///
39/// 为简单起见,同一时刻只支持单个使用者,因此所有方法(含读)都需要可变引用。
40/// 由于 Raft 本身是串行执行的,这一点通常不成问题。
41pub trait Engine: Send {
42 /// [`Engine::scan`] 返回的迭代器类型。
43 // 关联类型:具体引擎可返回零分配的专用迭代器
44 type ScanIterator<'a>: ScanIterator + 'a
45 // 生命周期与 Self 绑定,避免迭代器悬垂
46 where
47 // Sized 约束仅用于静态分发路径;dyn 时用 scan_dyn
48 Self: Sized + 'a; // 在 trait 对象中省略,以保持 dyn 兼容
49
50 /// 删除一个键;键不存在时无操作。
51 fn delete(&mut self, key: &[u8]) -> Result<()>;
52
53 /// 将缓冲数据刷入磁盘。
54 fn flush(&mut self) -> Result<()>;
55
56 /// 获取键对应的值(若存在)。
57 fn get(&mut self, key: &[u8]) -> Result<Option<Vec<u8>>>;
58
59 /// 按有序范围迭代键值对。
60 fn scan(&mut self, range: impl RangeBounds<Vec<u8>>) -> Self::ScanIterator<'_>
61 // 仅静态类型可返回关联迭代器
62 where
63 // 静态分发版本;trait 对象请用 scan_dyn
64 Self: Sized; // 在 trait 对象中省略,以保持 dyn 兼容
65
66 /// 与 scan 类似,但可用于 trait 对象(动态分发)。
67 // 显式 Bound 元组 + Box 迭代器,满足 dyn Engine 调用
68 fn scan_dyn(&mut self, range: (Bound<Vec<u8>>, Bound<Vec<u8>>)) -> Box<dyn ScanIterator + '_>;
69
70 /// 迭代所有以给定前缀开头的键值对。
71 fn scan_prefix(&mut self, prefix: &[u8]) -> Self::ScanIterator<'_>
72 // 默认方法同样要求 Sized
73 where
74 // 默认实现走 prefix_range + scan
75 Self: Sized, // 在 trait 对象中省略,以保持 dyn 兼容
76 // 默认方法体:前缀 → 范围 → 扫描
77 {
78 // 将前缀转为有序范围再扫描
79 self.scan(prefix_range(prefix))
80 // scan_prefix 默认实现结束
81 }
82
83 /// 设置键的值;若已存在则覆盖。
84 fn set(&mut self, key: &[u8], value: Vec<u8>) -> Result<()>;
85
86 /// 返回引擎状态信息。
87 fn status(&mut self) -> Result<Status>;
88// Engine trait 定义结束
89}
90
91/// 由 [`Engine::scan()`] 返回的键值对扫描迭代器。
92// 双向迭代 + 每项可能 IO 失败
93pub trait ScanIterator: DoubleEndedIterator<Item = Result<(Vec<u8>, Vec<u8>)>> {}
94
95/// 所有可作为扫描迭代器的迭代器的 blanket 实现。
96// 任何满足签名的双端迭代器自动成为 ScanIterator
97impl<I: DoubleEndedIterator<Item = Result<(Vec<u8>, Vec<u8>)>>> ScanIterator for I {}
98
99/// 引擎状态信息。
100// 可返回给客户端 Status 请求,也用于压缩决策
101#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
102// 对外暴露的引擎度量快照
103pub struct Status {
104 /// 存储引擎名称。
105 pub name: String,
106 /// 存活键的数量。
107 pub keys: u64,
108 /// 存活键值对的逻辑大小。
109 pub size: u64,
110 /// 磁盘上全部数据大小(含垃圾)。
111 pub disk_size: u64,
112 /// 磁盘上存活数据大小(不含垃圾)。
113 pub live_disk_size: u64,
114// Status 字段定义结束
115}
116
117// 垃圾度量的便捷方法
118impl Status {
119 /// 磁盘上垃圾数据的大小。
120 pub fn garbage_disk_size(&self) -> u64 {
121 // 总盘占用减去存活部分
122 self.disk_size - self.live_disk_size
123 // garbage_disk_size 结束
124 }
125
126 /// 磁盘垃圾占总大小的比例。
127 pub fn garbage_disk_percent(&self) -> f64 {
128 // 空库避免除零
129 if self.disk_size == 0 {
130 // 无数据时垃圾占比记 0
131 return 0.0;
132 // 空库分支结束
133 }
134 // 百分比形式,便于日志与阈值比较
135 self.garbage_disk_size() as f64 / self.disk_size as f64 * 100.0
136 // garbage_disk_percent 结束
137 }
138// Status impl 结束
139}