Skip to main content

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}