1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Persistence: the [`EzStorage`] contract, the values it exchanges, and one implementation
//!
//! [`FileStorage`] is here to get a first cluster running, not to be the storage a deployment
//! keeps. Read its caveats before reaching for it, and implement [`EzStorage`] yourself once
//! they matter.
use io;
use async_trait;
pub use FileStorage;
pub use Loaded;
pub use Persist;
use crateEzApp;
use crateEzEntry;
/// Storage persistence trait
///
/// Implement this to handle how Raft state is persisted to disk.
/// The framework handles all Raft logic - you only handle serialization and I/O.
///
/// # What the framework assumes
///
/// Raft's safety guarantees rest on these, so an implementation that breaks one can lose
/// acknowledged writes or elect two leaders for the same term:
///
/// - **Durability.** [`persist`] must return only once the data would survive a crash of the
/// machine, not just of the process. Anything weaker means a node can forget a vote it cast or a
/// log entry it acknowledged. The bundled [`FileStorage`] writes files without `fsync`, which is
/// fine for a demo and not enough for a real deployment.
/// - **Ordering.** Operations are applied in the order [`persist`] receives them. A later one must
/// not become durable before an earlier one.
/// - **Read-your-writes.** [`read_logs`] returns what [`persist`] last wrote, including the
/// deletions requested by [`Persist::DeleteLogs`].
///
/// [`persist`]: Self::persist
/// [`read_logs`]: Self::read_logs
///
/// # Example (file-based storage)
///
/// [`FileStorage`] is this sketch filled in, and is worth reading before writing your own.
///
/// ```ignore
/// struct MyStorage { base_dir: PathBuf }
///
/// #[async_trait]
/// impl EzStorage<KvApp> for MyStorage {
/// async fn load(&mut self) -> Result<Loaded, io::Error> {
/// // 1. Load meta from base_dir/meta.json (use default if first run)
/// // 2. Optionally load snapshot from base_dir/snapshot.meta + snapshot.data
/// // Log entries are read separately via read_logs()
/// Ok(Loaded { meta, snapshot })
/// }
///
/// async fn persist(&mut self, op: Persist<KvApp>) -> Result<(), io::Error> {
/// match op {
/// Persist::Meta(meta) => { /* write meta */ }
/// Persist::LogEntry(entry) => { /* write log entry */ }
/// Persist::Snapshot(snapshot) => { /* write snapshot.meta and snapshot.snapshot */ }
/// Persist::DeleteLogs { from, to } => { /* delete entries with from <= index < to */ }
/// }
/// }
///
/// async fn read_logs(&mut self, start: u64, end: u64) -> Result<Vec<EzEntry<KvApp>>, io::Error> {
/// // Read log entries in range [start, end)
/// }
/// }
/// ```