Skip to main content

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}