rabia_core/smr.rs
1//! # State Machine Replication (SMR) Interface
2//!
3//! This module defines the core State Machine Replication trait that enables
4//! the Rabia consensus protocol to work with any deterministic state machine.
5//!
6//! The SMR trait provides a clean abstraction for implementing custom state machines
7//! that can be replicated across a distributed cluster using Rabia consensus.
8
9use async_trait::async_trait;
10
11/// Core trait for State Machine Replication in Rabia consensus.
12///
13/// This trait defines the interface that any state machine must implement
14/// to be used with the Rabia consensus protocol. The trait ensures that
15/// state machines are deterministic and can be safely replicated across
16/// multiple nodes.
17///
18/// # Example Implementation
19///
20/// ```rust
21/// use rabia_core::smr::StateMachine;
22/// use async_trait::async_trait;
23/// use serde::{Deserialize, Serialize};
24///
25/// #[derive(Clone, Serialize, Deserialize)]
26/// pub struct CounterCommand {
27/// pub operation: String, // "increment", "decrement", "get"
28/// pub value: i64,
29/// }
30///
31/// #[derive(Clone, Serialize, Deserialize)]
32/// pub struct CounterResponse {
33/// pub value: i64,
34/// pub success: bool,
35/// }
36///
37/// #[derive(Clone, Serialize, Deserialize)]
38/// pub struct CounterState {
39/// pub value: i64,
40/// }
41///
42/// #[derive(Clone)]
43/// pub struct CounterStateMachine {
44/// state: CounterState,
45/// }
46///
47/// #[async_trait]
48/// impl StateMachine for CounterStateMachine {
49/// type Command = CounterCommand;
50/// type Response = CounterResponse;
51/// type State = CounterState;
52///
53/// async fn apply_command(&mut self, command: Self::Command) -> Self::Response {
54/// match command.operation.as_str() {
55/// "increment" => {
56/// self.state.value += command.value;
57/// CounterResponse { value: self.state.value, success: true }
58/// }
59/// "decrement" => {
60/// self.state.value -= command.value;
61/// CounterResponse { value: self.state.value, success: true }
62/// }
63/// "get" => {
64/// CounterResponse { value: self.state.value, success: true }
65/// }
66/// _ => CounterResponse { value: self.state.value, success: false }
67/// }
68/// }
69///
70/// fn get_state(&self) -> Self::State {
71/// self.state.clone()
72/// }
73///
74/// fn set_state(&mut self, state: Self::State) {
75/// self.state = state;
76/// }
77///
78/// fn serialize_state(&self) -> Vec<u8> {
79/// bincode::serialize(&self.state).unwrap_or_default()
80/// }
81///
82/// fn deserialize_state(&mut self, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
83/// self.state = bincode::deserialize(data)?;
84/// Ok(())
85/// }
86/// }
87/// ```
88#[async_trait]
89pub trait StateMachine: Send + Sync + Clone {
90 /// The command type that this state machine can process
91 type Command: Send + Sync + Clone;
92
93 /// The response type returned after applying commands
94 type Response: Send + Sync + Clone;
95
96 /// The state type representing the current state of the machine
97 type State: Send + Sync + Clone;
98
99 /// Apply a single command to the state machine.
100 ///
101 /// This method must be deterministic - given the same state and command,
102 /// it must always produce the same response and resulting state.
103 ///
104 /// # Arguments
105 /// * `command` - The command to apply to the state machine
106 ///
107 /// # Returns
108 /// The response generated by applying the command
109 async fn apply_command(&mut self, command: Self::Command) -> Self::Response;
110
111 /// Get the current state of the state machine.
112 ///
113 /// This method should return a snapshot of the current state that can
114 /// be used for state transfer or debugging.
115 fn get_state(&self) -> Self::State;
116
117 /// Set the state of the state machine.
118 ///
119 /// This method is used during state transfer operations to restore
120 /// the state machine to a specific state.
121 ///
122 /// # Arguments
123 /// * `state` - The state to restore
124 fn set_state(&mut self, state: Self::State);
125
126 /// Serialize the current state to bytes.
127 ///
128 /// This method is used for persistence and state transfer operations.
129 /// The serialized data should contain all information necessary to
130 /// restore the state machine to its current state.
131 ///
132 /// # Returns
133 /// A byte vector containing the serialized state
134 fn serialize_state(&self) -> Vec<u8>;
135
136 /// Deserialize state from bytes and restore it.
137 ///
138 /// This method is used during recovery and state transfer operations
139 /// to restore the state machine from serialized data.
140 ///
141 /// # Arguments
142 /// * `data` - The serialized state data
143 ///
144 /// # Returns
145 /// Result indicating success or failure of deserialization
146 fn deserialize_state(&mut self, data: &[u8]) -> Result<(), Box<dyn std::error::Error>>;
147
148 /// Apply multiple commands in sequence.
149 ///
150 /// Default implementation applies commands one by one. State machines
151 /// can override this for batch optimization if needed.
152 ///
153 /// # Arguments
154 /// * `commands` - Vector of commands to apply
155 ///
156 /// # Returns
157 /// Vector of responses corresponding to each command
158 async fn apply_commands(&mut self, commands: Vec<Self::Command>) -> Vec<Self::Response> {
159 let mut responses = Vec::with_capacity(commands.len());
160 for command in commands {
161 responses.push(self.apply_command(command).await);
162 }
163 responses
164 }
165
166 /// Check if this state machine is deterministic.
167 ///
168 /// All state machines used with Rabia consensus MUST be deterministic.
169 /// This method is provided for validation and debugging purposes.
170 ///
171 /// # Returns
172 /// true if the state machine is deterministic (should always return true)
173 fn is_deterministic(&self) -> bool {
174 true
175 }
176}