@@ -12,9 +12,16 @@ pub trait State: Send {
12
12
/// Returns the last applied index from the state machine.
13
13
fn get_applied_index ( & self ) -> Index ;
14
14
15
- /// Mutates the state machine. If the state machine returns Error::Internal, the Raft node
16
- /// halts. For any other error, the state is applied and the error propagated to the caller.
17
- fn mutate ( & mut self , index : Index , command : Vec < u8 > ) -> Result < Vec < u8 > > ;
15
+ /// Applies a log entry to the state machine. If it returns Error::Internal,
16
+ /// the Raft node halts. Any other error is considered applied and returned
17
+ /// to the caller.
18
+ ///
19
+ /// The entry may contain a noop command, which is committed by Raft during
20
+ /// leader changes. This still needs to be applied to the state machine to
21
+ /// properly track the applied index, and returns an empty result.
22
+ ///
23
+ /// TODO: consider using runtime assertions instead of Error::Internal.
24
+ fn apply ( & mut self , entry : Entry ) -> Result < Vec < u8 > > ;
18
25
19
26
/// Queries the state machine. All errors are propagated to the caller.
20
27
fn query ( & self , command : Vec < u8 > ) -> Result < Vec < u8 > > ;
@@ -51,7 +58,6 @@ struct Query {
51
58
pub struct Driver {
52
59
state_rx : UnboundedReceiverStream < Instruction > ,
53
60
node_tx : mpsc:: UnboundedSender < Message > ,
54
- applied_index : Index ,
55
61
/// Notify clients when their mutation is applied. <index, (client, id)>
56
62
notify : HashMap < Index , ( Address , Vec < u8 > ) > ,
57
63
/// Execute client queries when they receive a quorum. <index, <id, query>>
@@ -67,16 +73,14 @@ impl Driver {
67
73
Self {
68
74
state_rx : UnboundedReceiverStream :: new ( state_rx) ,
69
75
node_tx,
70
- applied_index : 0 ,
71
76
notify : HashMap :: new ( ) ,
72
77
queries : BTreeMap :: new ( ) ,
73
78
}
74
79
}
75
80
76
81
/// Drives a state machine.
77
82
pub async fn drive ( mut self , mut state : Box < dyn State > ) -> Result < ( ) > {
78
- self . applied_index = state. get_applied_index ( ) ;
79
- debug ! ( "Starting state machine driver at applied index {}" , self . applied_index) ;
83
+ debug ! ( "Starting state machine driver at applied index {}" , state. get_applied_index( ) ) ;
80
84
while let Some ( instruction) = self . state_rx . next ( ) . await {
81
85
if let Err ( error) = self . execute ( instruction, & mut * state) . await {
82
86
error ! ( "Halting state machine due to error: {}" , error) ;
@@ -91,40 +95,29 @@ impl Driver {
91
95
pub fn apply_log ( & mut self , state : & mut dyn State , log : & mut Log ) -> Result < Index > {
92
96
let applied_index = state. get_applied_index ( ) ;
93
97
let ( commit_index, _) = log. get_commit_index ( ) ;
94
- if applied_index > commit_index {
95
- return Err ( Error :: Internal ( format ! (
96
- "State machine applied index {} greater than log commit index {}" ,
97
- applied_index, commit_index
98
- ) ) ) ;
99
- }
98
+ assert ! ( applied_index <= commit_index, "applied index above commit index" ) ;
99
+
100
100
if applied_index < commit_index {
101
101
let mut scan = log. scan ( ( applied_index + 1 ) ..=commit_index) ?;
102
102
while let Some ( entry) = scan. next ( ) . transpose ( ) ? {
103
103
self . apply ( state, entry) ?;
104
104
}
105
105
}
106
- Ok ( self . applied_index )
106
+ Ok ( state . get_applied_index ( ) )
107
107
}
108
108
109
109
/// Applies an entry to the state machine.
110
110
pub fn apply ( & mut self , state : & mut dyn State , entry : Entry ) -> Result < Index > {
111
- // Apply the command, unless it's a noop .
111
+ // Apply the command.
112
112
debug ! ( "Applying {:?}" , entry) ;
113
- if let Some ( command) = entry. command {
114
- match state. mutate ( entry. index , command) {
115
- Err ( error @ Error :: Internal ( _) ) => return Err ( error) ,
116
- result => self . notify_applied ( entry. index , result) ?,
117
- } ;
118
- }
119
- // We have to track applied_index here, separately from the state machine, because
120
- // no-op log entries are significant for whether a query should be executed.
121
- //
122
- // TODO: track noop commands in the state machine.
123
- self . applied_index = entry. index ;
113
+ match state. apply ( entry) {
114
+ Err ( error @ Error :: Internal ( _) ) => return Err ( error) ,
115
+ result => self . notify_applied ( state. get_applied_index ( ) , result) ?,
116
+ } ;
124
117
// Try to execute any pending queries, since they may have been submitted for a
125
118
// commit_index which hadn't been applied yet.
126
119
self . query_execute ( state) ?;
127
- Ok ( self . applied_index )
120
+ Ok ( state . get_applied_index ( ) )
128
121
}
129
122
130
123
/// Executes a state machine instruction.
@@ -202,7 +195,7 @@ impl Driver {
202
195
203
196
/// Executes any queries that are ready.
204
197
fn query_execute ( & mut self , state : & mut dyn State ) -> Result < ( ) > {
205
- for query in self . query_ready ( self . applied_index ) {
198
+ for query in self . query_ready ( state . get_applied_index ( ) ) {
206
199
debug ! ( "Executing query {:?}" , query. command) ;
207
200
let result = state. query ( query. command ) ;
208
201
if let Err ( error @ Error :: Internal ( _) ) = result {
@@ -291,11 +284,13 @@ pub mod tests {
291
284
* self . applied_index . lock ( ) . unwrap ( )
292
285
}
293
286
294
- // Appends the command to the internal commands list.
295
- fn mutate ( & mut self , index : Index , command : Vec < u8 > ) -> Result < Vec < u8 > > {
296
- self . commands . lock ( ) ?. push ( command. clone ( ) ) ;
297
- * self . applied_index . lock ( ) ? = index;
298
- Ok ( command)
287
+ // Appends the entry to the internal command list.
288
+ fn apply ( & mut self , entry : Entry ) -> Result < Vec < u8 > > {
289
+ if let Some ( command) = & entry. command {
290
+ self . commands . lock ( ) ?. push ( command. clone ( ) ) ;
291
+ }
292
+ * self . applied_index . lock ( ) ? = entry. index ;
293
+ Ok ( entry. command . unwrap_or_default ( ) )
299
294
}
300
295
301
296
// Appends the command to the internal commands list.
0 commit comments