1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
use crate::engine::Command;
use crate::raft_types::RaftLogId;
use crate::Entry;
use crate::RaftTypeConfig;
use crate::StorageError;
/// Defines behaviors of a runtime to support the protocol engine.
///
/// An Engine defines the consensus algorithm, i.e., what to do(`command`) when some `event`
/// happens:
///
/// It receives events such as `write-log-entry` from a client,
/// or `elect` from a timer, and outputs `command`, such as
/// `append-entry-to-storage`, or `commit-entry-at-index-5` to a runtime to execute.
///
/// A `RaftRuntime` talks to `RaftStorage` and `RaftNetwork` to get things done.
///
/// The workflow of writing something through raft protocol with engine and runtime would be like
/// this:
///
/// ```text
/// Client Engine Runtime Storage Netwoork
/// | | write x=1 | | |
/// |-------------------------------------------------------->| | |
/// | event:write | | | |
/// | .------------------------------------------------| | |
/// | '--------------->| | | |
/// | | cmd:append-log-1 | | |
/// | |--+--------------------------->| append | |
/// | | | |----------->| |
/// | | | |<-----------| |
/// | | | cmd:replicate-log-1 | ok | |
/// | | '--------------------------->| | |
/// | | | | send |
/// | | |------------------------>|
/// | | | | send |
/// | | |------------------------>|
/// | | | | |
/// | | |<------------------------|
/// | event:ok | | ok | |
/// | .------------------------------------------------| | |
/// | '--------------->| | | |
/// | | |<------------------------|
/// | event:ok | | ok | |
/// | .------------------------------------------------| | |
/// | '--------------->| | | |
/// | | cmd:commit-log-1 | | |
/// | |------------------------------>| | |
/// | | cmd:apply-log-1 | | |
/// | |------------------------------>| | |
/// | | | apply | |
/// | | |----------->| |
/// | | |<-----------| |
/// | | | ok | |
/// |<--------------------------------------------------------| | |
/// | response | | | |
/// ```
///
/// TODO: add this diagram to guides/
#[async_trait::async_trait]
pub(crate) trait RaftRuntime<C: RaftTypeConfig> {
/// Run a command produced by the engine.
///
/// A command consumes zero or more input entries.
/// `curr` points to next non consumed entry in the `input_entries`.
/// It's the command's duty to decide move `curr` forward.
///
/// TODO(xp): remove this method. The API should run all commands in one shot.
/// E.g. `run_engine_commands(input_entries, commands)`.
/// But it relates to the different behaviors of server states.
/// LeaderState is a wrapper of RaftCore thus it can not just reuse
/// `RaftCore::run_engine_commands()`. Thus LeaderState may have to re-implememnt
/// every command. This can be done after moving all raft-algorithm logic into
/// Engine. Then a Runtime do not need to differentiate states such as LeaderState
/// or FollowerState and all command execution can be implemented in one method.
async fn run_command<'e, Ent>(
&mut self,
input_entries: &'e [Ent],
curr: &mut usize,
cmd: Command<C::NodeId, C::Node>,
) -> Result<(), StorageError<C::NodeId>>
where
Ent: RaftLogId<C::NodeId> + Sync + Send + 'e,
&'e Ent: Into<Entry<C>>;
}