Skip to content

Commit 4c488a6

Browse files
authored
Change: move external command trigger to dedicated Trigger struct (#888)
* Refactor: move RaftTypeConfig to separate file * Change: move external command trigger to dedicated Trigger struct Moved trigger election, heartbeat, snapshot and purge log from `Raft` to a new `Trigger` struct, to separate externally trigger actions from the main Raft API. --- Marked the old trigger methods in `Raft` as deprecated, and recommended using the new `Trigger` struct instead. The main motivation of these changes is to organize the Raft API in a more structured way, by extracting trigger actions into a dedicated struct, instead of mixing them together in the `Raft` API.
1 parent 7eccd06 commit 4c488a6

18 files changed

+207
-117
lines changed

openraft/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub mod raft;
6565
pub mod storage;
6666
pub mod testing;
6767
pub mod timer;
68+
pub mod type_config;
6869

6970
pub(crate) mod engine;
7071
pub(crate) mod log_id_range;
@@ -89,6 +90,7 @@ pub use macros::add_async_trait;
8990
pub use network::RPCTypes;
9091
pub use network::RaftNetwork;
9192
pub use network::RaftNetworkFactory;
93+
pub use type_config::RaftTypeConfig;
9294

9395
pub use crate::async_runtime::AsyncRuntime;
9496
pub use crate::async_runtime::TokioRuntime;
@@ -114,7 +116,6 @@ pub use crate::node::EmptyNode;
114116
pub use crate::node::Node;
115117
pub use crate::node::NodeId;
116118
pub use crate::raft::Raft;
117-
pub use crate::raft::RaftTypeConfig;
118119
pub use crate::raft_state::MembershipState;
119120
pub use crate::raft_state::RaftState;
120121
pub use crate::raft_types::SnapshotId;

openraft/src/raft/core_state.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::error::Fatal;
2+
use crate::AsyncRuntime;
3+
use crate::NodeId;
4+
5+
/// The running state of RaftCore
6+
pub(in crate::raft) enum CoreState<NID, A>
7+
where
8+
NID: NodeId,
9+
A: AsyncRuntime,
10+
{
11+
/// The RaftCore task is still running.
12+
Running(A::JoinHandle<Result<(), Fatal<NID>>>),
13+
14+
/// The RaftCore task has finished. The return value of the task is stored.
15+
Done(Result<(), Fatal<NID>>),
16+
}

openraft/src/raft/mod.rs

+33-99
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22
33
mod message;
44
mod raft_inner;
5+
mod trigger;
6+
7+
pub(in crate::raft) mod core_state;
58

69
use std::fmt::Debug;
710
use std::marker::PhantomData;
811
use std::sync::atomic::Ordering;
912
use std::sync::Arc;
1013
use std::time::Duration;
1114

15+
use core_state::CoreState;
1216
use maplit::btreemap;
1317
pub use message::AppendEntriesRequest;
1418
pub use message::AppendEntriesResponse;
@@ -17,9 +21,6 @@ pub use message::InstallSnapshotRequest;
1721
pub use message::InstallSnapshotResponse;
1822
pub use message::VoteRequest;
1923
pub use message::VoteResponse;
20-
use tokio::io::AsyncRead;
21-
use tokio::io::AsyncSeek;
22-
use tokio::io::AsyncWrite;
2324
use tokio::sync::mpsc;
2425
use tokio::sync::oneshot;
2526
use tokio::sync::watch;
@@ -31,16 +32,13 @@ use tracing::Level;
3132
use crate::config::Config;
3233
use crate::config::RuntimeConfig;
3334
use crate::core::command_state::CommandState;
34-
use crate::core::raft_msg::external_command::ExternalCommand;
3535
use crate::core::raft_msg::RaftMsg;
3636
use crate::core::replication_lag;
3737
use crate::core::sm;
3838
use crate::core::RaftCore;
3939
use crate::core::Tick;
4040
use crate::engine::Engine;
4141
use crate::engine::EngineConfig;
42-
use crate::entry::FromAppData;
43-
use crate::entry::RaftEntry;
4442
use crate::error::CheckIsLeaderError;
4543
use crate::error::ClientWriteError;
4644
use crate::error::Fatal;
@@ -51,69 +49,19 @@ use crate::membership::IntoNodes;
5149
use crate::metrics::RaftMetrics;
5250
use crate::metrics::Wait;
5351
use crate::network::RaftNetworkFactory;
54-
use crate::node::Node;
5552
use crate::raft::raft_inner::RaftInner;
53+
use crate::raft::trigger::Trigger;
5654
use crate::storage::RaftLogStorage;
5755
use crate::storage::RaftStateMachine;
58-
use crate::AppData;
59-
use crate::AppDataResponse;
6056
use crate::AsyncRuntime;
6157
use crate::ChangeMembers;
6258
use crate::LogId;
6359
use crate::LogIdOptionExt;
6460
use crate::MessageSummary;
65-
use crate::NodeId;
66-
use crate::OptionalSend;
6761
use crate::RaftState;
62+
pub use crate::RaftTypeConfig;
6863
use crate::StorageHelper;
6964

70-
/// Configuration of types used by the [`Raft`] core engine.
71-
///
72-
/// The (empty) implementation structure defines request/response types, node ID type
73-
/// and the like. Refer to the documentation of associated types for more information.
74-
///
75-
/// ## Note
76-
///
77-
/// Since Rust cannot automatically infer traits for various inner types using this config
78-
/// type as a parameter, this trait simply uses all the traits required for various types
79-
/// as its supertraits as a workaround. To ease the declaration, the macro
80-
/// `declare_raft_types` is provided, which can be used to declare the type easily.
81-
///
82-
/// Example:
83-
/// ```ignore
84-
/// openraft::declare_raft_types!(
85-
/// /// Declare the type configuration for `MemStore`.
86-
/// pub Config: D = ClientRequest, R = ClientResponse, NodeId = MemNodeId
87-
/// );
88-
/// ```
89-
pub trait RaftTypeConfig:
90-
Sized + Send + Sync + Debug + Clone + Copy + Default + Eq + PartialEq + Ord + PartialOrd + 'static
91-
{
92-
/// Application-specific request data passed to the state machine.
93-
type D: AppData;
94-
95-
/// Application-specific response data returned by the state machine.
96-
type R: AppDataResponse;
97-
98-
/// A Raft node's ID.
99-
type NodeId: NodeId;
100-
101-
/// Raft application level node data
102-
type Node: Node;
103-
104-
/// Raft log entry, which can be built from an AppData.
105-
type Entry: RaftEntry<Self::NodeId, Self::Node> + FromAppData<Self::D>;
106-
107-
/// Snapshot data for exposing a snapshot for reading & writing.
108-
///
109-
/// See the [storage chapter of the guide](https://datafuselabs.github.io/openraft/getting-started.html#implement-raftstorage)
110-
/// for details on where and how this is used.
111-
type SnapshotData: AsyncRead + AsyncWrite + AsyncSeek + OptionalSend + Sync + Unpin + 'static;
112-
113-
/// Asynchronous runtime type.
114-
type AsyncRuntime: AsyncRuntime;
115-
}
116-
11765
/// Define types for a Raft type configuration.
11866
///
11967
/// Since Rust has some limitations when deriving traits for types with generic arguments
@@ -125,8 +73,14 @@ pub trait RaftTypeConfig:
12573
/// Example:
12674
/// ```ignore
12775
/// openraft::declare_raft_types!(
128-
/// /// Declare the type configuration for `MemStore`.
129-
/// pub Config: D = ClientRequest, R = ClientResponse, NodeId = MemNodeId
76+
/// pub Config:
77+
/// D = ClientRequest,
78+
/// R = ClientResponse,
79+
/// NodeId = u64,
80+
/// Node = openraft::BasicNode,
81+
/// Entry = openraft::Entry<TypeConfig>,
82+
/// SnapshotData = Cursor<Vec<u8>>,
83+
/// AsyncRuntime = openraft::TokioRuntime,
13084
/// );
13185
/// ```
13286
#[macro_export]
@@ -146,19 +100,6 @@ macro_rules! declare_raft_types {
146100
};
147101
}
148102

149-
/// The running state of RaftCore
150-
enum CoreState<NID, A>
151-
where
152-
NID: NodeId,
153-
A: AsyncRuntime,
154-
{
155-
/// The RaftCore task is still running.
156-
Running(A::JoinHandle<Result<(), Fatal<NID>>>),
157-
158-
/// The RaftCore task has finished. The return value of the task is stored.
159-
Done(Result<(), Fatal<NID>>),
160-
}
161-
162103
/// The Raft API.
163104
///
164105
/// This type implements the full Raft spec, and is the interface to a running Raft node.
@@ -337,46 +278,39 @@ where
337278
self.inner.runtime_config.enable_elect.store(enabled, Ordering::Relaxed);
338279
}
339280

340-
/// Trigger election at once and return at once.
281+
/// Return a handle to manually trigger raft actions, such as elect or build snapshot.
341282
///
342-
/// Returns error when RaftCore has [`Fatal`] error, e.g. shut down or having storage error.
343-
/// It is not affected by `Raft::enable_elect(false)`.
283+
/// Example:
284+
/// ```ignore
285+
/// let raft = Raft::new(...).await?;
286+
/// raft.trigger().elect().await?;
287+
/// ```
288+
pub fn trigger(&self) -> Trigger<C, N, LS> {
289+
Trigger::new(self.inner.as_ref())
290+
}
291+
292+
/// Trigger election at once and return at once.
293+
#[deprecated(note = "use `Raft::trigger().elect()` instead")]
344294
pub async fn trigger_elect(&self) -> Result<(), Fatal<C::NodeId>> {
345-
self.inner.send_external_command(ExternalCommand::Elect, "trigger_elect").await
295+
self.trigger().elect().await
346296
}
347297

348298
/// Trigger a heartbeat at once and return at once.
349-
///
350-
/// Returns error when RaftCore has [`Fatal`] error, e.g. shut down or having storage error.
351-
/// It is not affected by `Raft::enable_heartbeat(false)`.
299+
#[deprecated(note = "use `Raft::trigger().heartbeat()` instead")]
352300
pub async fn trigger_heartbeat(&self) -> Result<(), Fatal<C::NodeId>> {
353-
self.inner.send_external_command(ExternalCommand::Heartbeat, "trigger_heartbeat").await
301+
self.trigger().heartbeat().await
354302
}
355303

356304
/// Trigger to build a snapshot at once and return at once.
357-
///
358-
/// Returns error when RaftCore has [`Fatal`] error, e.g. shut down or having storage error.
305+
#[deprecated(note = "use `Raft::trigger().snapshot()` instead")]
359306
pub async fn trigger_snapshot(&self) -> Result<(), Fatal<C::NodeId>> {
360-
self.inner.send_external_command(ExternalCommand::Snapshot, "trigger_snapshot").await
307+
self.trigger().snapshot().await
361308
}
362309

363310
/// Initiate the log purge up to and including the given `upto` log index.
364-
///
365-
/// Logs that are not included in a snapshot will **NOT** be purged.
366-
/// In such scenario it will delete as many log as possible.
367-
/// The [`max_in_snapshot_log_to_keep`] config is not taken into account
368-
/// when purging logs.
369-
///
370-
/// It returns error only when RaftCore has [`Fatal`] error, e.g. shut down or having storage
371-
/// error.
372-
///
373-
/// Openraft won't purge logs at once, e.g. it may be delayed by several seconds, because if it
374-
/// is a leader and a replication task has been replicating the logs to a follower, the logs
375-
/// can't be purged until the replication task is finished.
376-
///
377-
/// [`max_in_snapshot_log_to_keep`]: `crate::Config::max_in_snapshot_log_to_keep`
311+
#[deprecated(note = "use `Raft::trigger().purge_log()` instead")]
378312
pub async fn purge_log(&self, upto: u64) -> Result<(), Fatal<C::NodeId>> {
379-
self.inner.send_external_command(ExternalCommand::PurgeLog { upto }, "purge_log").await
313+
self.trigger().purge_log(upto).await
380314
}
381315

382316
/// Submit an AppendEntries RPC to this Raft node.

openraft/src/raft/raft_inner.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::core::raft_msg::external_command::ExternalCommand;
1111
use crate::core::raft_msg::RaftMsg;
1212
use crate::core::TickHandle;
1313
use crate::error::Fatal;
14-
use crate::raft::CoreState;
14+
use crate::raft::core_state::CoreState;
1515
use crate::storage::RaftLogStorage;
1616
use crate::AsyncRuntime;
1717
use crate::Config;

openraft/src/raft/trigger.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//! Trigger an action to RaftCore by external caller.
2+
3+
use crate::core::raft_msg::external_command::ExternalCommand;
4+
use crate::error::Fatal;
5+
use crate::raft::RaftInner;
6+
use crate::storage::RaftLogStorage;
7+
use crate::RaftNetworkFactory;
8+
use crate::RaftTypeConfig;
9+
10+
/// Trigger is an interface to trigger an action to RaftCore by external caller.
11+
pub struct Trigger<'r, C, N, LS>
12+
where
13+
C: RaftTypeConfig,
14+
N: RaftNetworkFactory<C>,
15+
LS: RaftLogStorage<C>,
16+
{
17+
raft_inner: &'r RaftInner<C, N, LS>,
18+
}
19+
20+
impl<'r, C, N, LS> Trigger<'r, C, N, LS>
21+
where
22+
C: RaftTypeConfig,
23+
N: RaftNetworkFactory<C>,
24+
LS: RaftLogStorage<C>,
25+
{
26+
pub(in crate::raft) fn new(raft_inner: &'r RaftInner<C, N, LS>) -> Self {
27+
Self { raft_inner }
28+
}
29+
30+
/// Trigger election at once and return at once.
31+
///
32+
/// Returns error when RaftCore has [`Fatal`] error, e.g. shut down or having storage error.
33+
/// It is not affected by `Raft::enable_elect(false)`.
34+
pub async fn elect(&self) -> Result<(), Fatal<C::NodeId>> {
35+
self.raft_inner.send_external_command(ExternalCommand::Elect, "trigger_elect").await
36+
}
37+
38+
/// Trigger a heartbeat at once and return at once.
39+
///
40+
/// Returns error when RaftCore has [`Fatal`] error, e.g. shut down or having storage error.
41+
/// It is not affected by `Raft::enable_heartbeat(false)`.
42+
pub async fn heartbeat(&self) -> Result<(), Fatal<C::NodeId>> {
43+
self.raft_inner.send_external_command(ExternalCommand::Heartbeat, "trigger_heartbeat").await
44+
}
45+
46+
/// Trigger to build a snapshot at once and return at once.
47+
///
48+
/// Returns error when RaftCore has [`Fatal`] error, e.g. shut down or having storage error.
49+
pub async fn snapshot(&self) -> Result<(), Fatal<C::NodeId>> {
50+
self.raft_inner.send_external_command(ExternalCommand::Snapshot, "trigger_snapshot").await
51+
}
52+
53+
/// Initiate the log purge up to and including the given `upto` log index.
54+
///
55+
/// Logs that are not included in a snapshot will **NOT** be purged.
56+
/// In such scenario it will delete as many log as possible.
57+
/// The [`max_in_snapshot_log_to_keep`] config is not taken into account
58+
/// when purging logs.
59+
///
60+
/// It returns error only when RaftCore has [`Fatal`] error, e.g. shut down or having storage
61+
/// error.
62+
///
63+
/// Openraft won't purge logs at once, e.g. it may be delayed by several seconds, because if it
64+
/// is a leader and a replication task has been replicating the logs to a follower, the logs
65+
/// can't be purged until the replication task is finished.
66+
///
67+
/// [`max_in_snapshot_log_to_keep`]: `crate::Config::max_in_snapshot_log_to_keep`
68+
pub async fn purge_log(&self, upto: u64) -> Result<(), Fatal<C::NodeId>> {
69+
self.raft_inner.send_external_command(ExternalCommand::PurgeLog { upto }, "purge_log").await
70+
}
71+
}

0 commit comments

Comments
 (0)