raft_rust/raft/kv.rs
1//! 用于演示与测试的简单字符串键值状态机。
2//!
3//! 命令与响应均为 bincode 编码的 `Vec<u8>`,与 [`super::State`] 的不透明字节接口一致。
4
5// 有序 map:扫描结果确定、便于测试
6use std::{collections::BTreeMap, panic};
7// 命令/响应的可读显示
8use std::fmt::Display;
9
10// 命令与响应需可编解码
11use serde::{Deserialize, Serialize};
12
13// 实现 State 所需类型
14use super::{Entry, Index, State};
15// 解码失败映射为库错误
16use crate::error::Result;
17
18/// Bincode 标准配置,用于命令 / 响应编码。
19const BINCODE: bincode::config::Configuration = bincode::config::standard();
20
21/// 用 bincode 编码应用层值。
22pub fn encode<T: Serialize>(value: &T) -> Vec<u8> {
23 // 应用层类型应始终可序列化;失败直接 panic
24 bincode::serde::encode_to_vec(value, BINCODE).expect("value must be serializable")
25// 结束当前作用域
26}
27
28/// 用 bincode 解码应用层值。
29pub fn decode<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T> {
30 // 取解码结果的第一项(值),忽略消耗字节数
31 Ok(bincode::serde::borrow_decode_from_slice(bytes, BINCODE)?.0)
32// 结束当前作用域
33}
34
35/// 由 Raft 驱动的内存字符串键值存储。
36// Default:空库 applied_index=0
37#[derive(Default)]
38// 定义数据结构
39pub struct Kv {
40 // 最后成功 apply 的日志索引
41 applied_index: Index,
42 // 业务数据:字符串键值
43 data: BTreeMap<String, String>,
44// 结束当前作用域
45}
46
47// 为类型实现方法
48impl Kv {
49 /// 创建一个空的键值状态机。
50 pub fn new() -> Box<Self> {
51 // 装箱以便作为 dyn State 使用
52 Box::new(Self::default())
53 // 结束当前作用域
54 }
55
56 /// 返回当前数据的快照(便于检查 / 测试)。
57 pub fn data(&self) -> &BTreeMap<String, String> {
58 // 只读暴露内部 map
59 &self.data
60 // 结束当前作用域
61 }
62// 结束当前作用域
63}
64
65// 实现 Raft 状态机接口
66impl State for Kv {
67 // 汇报已应用索引
68 fn get_applied_index(&self) -> Index {
69 // 业务逻辑步骤
70 self.applied_index
71 // 结束当前作用域
72 }
73
74 // 将已提交日志应用到 KV;读类命令不应走写路径
75 fn apply(&mut self, entry: Entry) -> Result<Vec<u8>> {
76 // 空命令(noop/成员)→ command 为 None;有载荷则解码
77 let command = entry.command.as_deref().map(decode::<Command>).transpose()?;
78 // 按命令类型执行
79 let response = match command {
80 // Put:写入并返回已应用索引
81 Some(Command::Put { key, value }) => {
82 // 业务逻辑步骤
83 self.data.insert(key, value);
84 // 编码命令字节
85 encode(&Response::Put(entry.index))
86 // 结束当前作用域
87 }
88 // Get/Scan 误作为写提交:编程错误,直接 panic
89 Some(c @ (Command::Get { .. } | Command::Scan)) => {
90 // 失败或不可达路径
91 panic!("{c} submitted as write command")
92 // 结束当前作用域
93 }
94 // noop 或无命令:返回空结果
95 None => Vec::new(),
96 // 结束当前作用域
97 };
98 // 推进 applied 索引
99 self.applied_index = entry.index;
100 // 返回编码后的响应
101 Ok(response)
102 // 结束当前作用域
103 }
104
105 // 只读路径:Get/Scan
106 fn read(&self, command: Vec<u8>) -> Result<Vec<u8>> {
107 // 解码读命令
108 match decode::<Command>(&command)? {
109 // 查单键,可能不存在
110 Command::Get { key } => Ok(encode(&Response::Get(self.data.get(&key).cloned()))),
111 // 返回全表克隆
112 Command::Scan => Ok(encode(&Response::Scan(self.data.clone()))),
113 // 写命令误走读路径:panic
114 c @ Command::Put { .. } => panic!("{c} submitted as read command"),
115 // 结束当前作用域
116 }
117 // 结束当前作用域
118 }
119
120 // 导出 (applied_index, data) 作为快照
121 fn snapshot(&self) -> Result<Vec<u8>> {
122 // 成功返回
123 Ok(encode(&(self.applied_index, &self.data)))
124 // 结束当前作用域
125 }
126
127 // 从快照恢复数据与 applied 索引
128 fn restore(&mut self, snapshot: &[u8], index: Index) -> Result<()> {
129 // 解码快照中的索引与全表
130 let (applied, data): (Index, BTreeMap<String, String>) = decode(snapshot)?;
131 // 取快照索引与调用方 index 的较大者,避免回退
132 self.applied_index = index.max(applied);
133 // 替换内存数据
134 self.data = data;
135 // 成功返回
136 Ok(())
137 // 结束当前作用域
138 }
139// 结束当前作用域
140}
141
142/// 键值命令。先用 [`encode`] 编码,再包装进 [`super::Request::Read`] / [`super::Request::Write`]。
143#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
144// 定义枚举
145pub enum Command {
146 /// 获取给定键的值。
147 Get { key: String },
148 /// 存储键值对(写操作;返回已应用索引)。
149 Put { key: String, value: String },
150 /// 返回全部键值对。
151 Scan,
152// 结束当前作用域
153}
154
155// 便于日志与 panic 信息中打印命令
156impl Display for Command {
157 // 定义函数
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 // 人类可读的命令摘要
160 match self {
161 // 列表或字段续项
162 Self::Get { key } => write!(f, "get {key}"),
163 // 列表或字段续项
164 Self::Put { key, value } => write!(f, "put {key}={value}"),
165 // 列表或字段续项
166 Self::Scan => write!(f, "scan"),
167 // 结束当前作用域
168 }
169 // 结束当前作用域
170 }
171// 结束当前作用域
172}
173
174/// [`Command`] 的响应。
175#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
176// 定义枚举
177pub enum Response {
178 /// Get 的结果。
179 Get(Option<String>),
180 /// Put 的已应用索引。
181 Put(Index),
182 /// Scan 返回的全部键值对。
183 Scan(BTreeMap<String, String>),
184// 结束当前作用域
185}
186
187// 便于 CLI 打印响应
188impl Display for Response {
189 // 定义函数
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 // 按响应类型格式化输出
192 match self {
193 // 有值直接打印
194 Self::Get(Some(value)) => write!(f, "{value}"),
195 // 缺失键
196 Self::Get(None) => write!(f, "None"),
197 // 写成功返回 applied index
198 Self::Put(applied_index) => write!(f, "{applied_index}"),
199 // Scan:k=v 逗号分隔
200 Self::Scan(kvs) => {
201 // 控制首项不加逗号
202 let mut first = true;
203 // 按 BTreeMap 序输出
204 for (k, v) in kvs {
205 // 非首项加分隔符
206 if !first {
207 // 业务逻辑步骤
208 write!(f, ",")?;
209 // 结束当前作用域
210 }
211 // 写出一对
212 write!(f, "{k}={v}")?;
213 // 后续项需分隔
214 first = false;
215 // 结束当前作用域
216 }
217 // 成功返回
218 Ok(())
219 // 结束当前作用域
220 }
221 // 结束当前作用域
222 }
223 // 结束当前作用域
224 }
225// 结束当前作用域
226}