Skip to content

Commit dac84b7

Browse files
committed
Track writes on Raft leader
This is in preparation for moving state machine application into the Raft node. For now, it only tracks the writes, without actually responding to clients when applied.
1 parent 2b2e34b commit dac84b7

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

Diff for: src/raft/node/leader.rs

+37-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use super::super::{Address, Event, Index, Instruction, Message, Request, Status};
1+
use super::super::{Address, Event, Index, Instruction, Message, Request, RequestID, Status};
22
use super::{Follower, Node, NodeID, RawNode, Role, Term, Ticks, HEARTBEAT_INTERVAL};
3-
use crate::error::Result;
3+
use crate::error::{Error, Result};
44

55
use ::log::{debug, info};
66
use std::collections::{HashMap, HashSet};
@@ -14,11 +14,30 @@ struct Progress {
1414
last: Index,
1515
}
1616

17+
/// A pending client write request.
18+
#[derive(Clone, Debug, PartialEq)]
19+
struct Write {
20+
/// The client or node which submitted the write.
21+
from: Address,
22+
/// The write request ID.
23+
id: RequestID,
24+
}
25+
1726
// A leader serves requests and replicates the log to followers.
1827
#[derive(Clone, Debug, PartialEq)]
1928
pub struct Leader {
2029
/// Peer replication progress.
2130
progress: HashMap<NodeID, Progress>,
31+
/// Keeps track of pending write requests, keyed by log index. These are
32+
/// added when the write is proposed and appended to the leader's log, and
33+
/// removed when the command is applied to the state machine, sending the
34+
/// command result to the waiting client.
35+
///
36+
/// If the leader loses leadership, all pending write requests are aborted
37+
/// by returning Error::Abort.
38+
///
39+
/// TODO: Actually return responses when applied.
40+
writes: HashMap<Index, Write>,
2241
/// Number of ticks since last periodic heartbeat.
2342
since_heartbeat: Ticks,
2443
}
@@ -28,7 +47,7 @@ impl Leader {
2847
pub fn new(peers: HashSet<NodeID>, last_index: Index) -> Self {
2948
let next = last_index + 1;
3049
let progress = peers.into_iter().map(|p| (p, Progress { next, last: 0 })).collect();
31-
Self { progress, since_heartbeat: 0 }
50+
Self { progress, writes: HashMap::new(), since_heartbeat: 0 }
3251
}
3352
}
3453

@@ -53,9 +72,18 @@ impl RawNode<Leader> {
5372
assert!(term > self.term, "Can only become follower in later term");
5473

5574
info!("Discovered new term {}", term);
75+
76+
// Cancel in-flight requests.
77+
self.state_tx.send(Instruction::Abort)?;
78+
for write in std::mem::take(&mut self.role.writes).into_values() {
79+
self.send(
80+
write.from,
81+
Event::ClientResponse { id: write.id, response: Err(Error::Abort) },
82+
)?;
83+
}
84+
5685
self.term = term;
5786
self.log.set_term(term, None)?;
58-
self.state_tx.send(Instruction::Abort)?;
5987
Ok(self.into_role(Follower::new(None, None)))
6088
}
6189

@@ -148,8 +176,11 @@ impl RawNode<Leader> {
148176
self.heartbeat()?;
149177
}
150178

179+
// A client submitted a write command. Propose it, and track it
180+
// until it's applied and the response is returned to the client.
151181
Event::ClientRequest { id, request: Request::Mutate(command) } => {
152182
let index = self.propose(Some(command))?;
183+
self.role.writes.insert(index, Write { from: msg.from, id: id.clone() });
153184
self.state_tx.send(Instruction::Notify { id, address: msg.from, index })?;
154185
if self.peers.is_empty() {
155186
self.maybe_commit()?;
@@ -264,6 +295,8 @@ impl RawNode<Leader> {
264295
// TODO: Move application elsewhere, but needs access to applied index.
265296
let mut scan = self.log.scan((prev_commit_index + 1)..=commit_index)?;
266297
while let Some(entry) = scan.next().transpose()? {
298+
// TODO: Send response.
299+
self.role.writes.remove(&entry.index);
267300
self.state_tx.send(Instruction::Apply { entry })?;
268301
}
269302
}

0 commit comments

Comments
 (0)