Description
When using the Stream approach to eliminate memory allocation, the Stream requires a 'static lifetime as it must traverse from the RaftCore task to StateMachine task. This necessitates that the stream maintains a lock on
RaftCore.client_resp_channel
to get client response senders for the SM. Although the stream can batch-retrieve client response senders, this design may still increase task switching overhead as RaftCore and StateMachine tasks alternately acquire the lock. Is this trade-off acceptable for your implementation?
I think we need to first prepare a better design for streaming. It should in fact reduce switching, since you can stream the entries directly from Raft core instead from here. Here you'd just set up the task (or future) accepting the stream (and potentially cancel it, as needed).
I don't quite get that with
'static
lifetime. The stream is just an object somewhere on heap where two endpoints reside in two different tasks. You could also just use a regularmpsc
channel, where a pair of(entry, reply_sender)
is pushed and the state machine consumer picks it and executes it.
There should be a sending end for the mpsc sender to push new (entry, reply_sender)
in to the channel.
I assume the pseudo code for this part looks like:
impl RaftCore {
fn apply(&mut self, start: u64, end:u64) {
let handle = self.read_reply_sender_handle(); // (1)
let (tx,rx) = mpsc::channel();
spawn(async move || {
while true {
let (entry, reply_sender) = handle.read().await;
tx.send((entry,reply_sender)).await;
}
});
self.sm_handle.send(SMCommand::Apply(new_stream(rx)));
}
}
impl SMWorker {
fn loop(mut self) {
while let Some(sm_cmd) = self.rx.read().await {
let SMCommand::Apply(stream) = sm_cmd else { /* ... */ };
while let Some((entry, reply_sender)) = stream.next().await {
// apply entry and send back reply...
}
}
}
}
If I understand it correctly, at position (1)
, the handle has to hold a lock on the RaftCore.client_resp_channels
to get reply sender in another task. right?
We just need a mechanism to track applied ID, since applying of entries can complete out-of-order. OTOH, except for snapshot handling, does Raft core need to know the applied ID?
The RaftCore need applied ID to
- update Metrics
- to inform linearizable read when it is safe to read from state machine.
Originally posted by @schreter in #1334 (review)