Skip to main content

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}