Skip to content

Commit 6908a3c

Browse files
committed
Change: Remove feature flag single-term-leader
In OpenRaft, whether a term supports multiple or single leaders is determined by the ordering implementation of `LeaderId`. This behavior is independent of compilation and can be configured at the application level, removing the need for a compilation flag `single-term-leader`. ### Changes: - The `single-term-leader` feature flag is removed. - Leader mode (multi-leader or single-leader per term) is now controlled by the `LeaderId` associated type in the application configuration. ### Configuration Guide: To enable **standard Raft mode (single leader per term)**: - Set `LeaderId` to `openraft::impls::leader_id_std::LeaderId` in the `declare_raft_types!` statement, or within the `RaftTypeConfig` implementation. Example: ```rust openraft::declare_raft_types!( pub TypeConfig: D = String, LeaderId = openraft::impls::leader_id_std::LeaderId<TypeConfig>, ); ``` To use the **default multi-leader per term mode**, leave `LeaderId` unset or explicitly set it to `openraft::impls::leader_id_adv::LeaderId`. Example: ```rust openraft::declare_raft_types!( pub TypeConfig: D = String, ); // Or: openraft::declare_raft_types!( pub TypeConfig: D = String, LeaderId = openraft::impls::leader_id_adv::LeaderId<TypeConfig>, ); ``` Upgrade tip: If the `single-term-leader` flag was previously used, remove it and configure `LeaderId` as follows: ```rust openraft::declare_raft_types!( pub TypeConfig: LeaderId = openraft::impls::leader_id_std::LeaderId<TypeConfig>, ); ```
1 parent b230bc2 commit 6908a3c

File tree

38 files changed

+196
-150
lines changed

38 files changed

+196
-150
lines changed

.github/workflows/ci.yaml

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -63,29 +63,20 @@ jobs:
6363
args: --features bench nothing-to-run --manifest-path openraft/Cargo.toml
6464

6565

66-
# Run openraft unit test `openraft/` and integration test `tests/`.
67-
openraft-test:
66+
# Run openraft unit test `openraft/`
67+
test-crate-openraft:
6868
runs-on: ubuntu-latest
6969

7070
strategy:
7171
fail-fast: false
7272
matrix:
7373
include:
7474
- toolchain: "stable"
75-
send_delay: "0"
7675
features: ""
7776

78-
# With network delay
7977
- toolchain: "nightly"
80-
send_delay: "30"
8178
features: ""
8279

83-
# Feature-flag: Standard raft term
84-
- toolchain: "nightly"
85-
send_delay: "0"
86-
features: "single-term-leader"
87-
88-
8980
steps:
9081
- name: Setup | Checkout
9182
uses: actions/checkout@v4
@@ -98,19 +89,57 @@ jobs:
9889
override: true
9990

10091

101-
# - A store with defensive checks returns error when unexpected accesses are sent to RaftStore.
102-
# - Raft should not depend on defensive error to work correctly.
10392
- name: Test crate `openraft/`
10493
uses: actions-rs/cargo@v1
10594
with:
10695
command: test
10796
args: --features "${{ matrix.features }}" --manifest-path openraft/Cargo.toml
10897
env:
109-
# Parallel tests block each other and result in timeout.
110-
RUST_TEST_THREADS: 2
11198
RUST_LOG: debug
11299
RUST_BACKTRACE: full
113-
OPENRAFT_NETWORK_SEND_DELAY: ${{ matrix.send_delay }}
100+
101+
102+
- name: Upload artifact
103+
uses: actions/upload-artifact@v3
104+
if: failure()
105+
with:
106+
name: "ut-openraft-${{ matrix.toolchain }}-${{ matrix.features }}"
107+
path: |
108+
openraft/_log/
109+
110+
# Run integration test `tests/`.
111+
test-crate-tests:
112+
runs-on: ubuntu-latest
113+
114+
strategy:
115+
fail-fast: false
116+
matrix:
117+
include:
118+
- toolchain: "stable"
119+
send_delay: "0"
120+
features: ""
121+
122+
# With network delay
123+
- toolchain: "nightly"
124+
send_delay: "30"
125+
features: ""
126+
127+
# Feature-flag: Standard raft term
128+
- toolchain: "nightly"
129+
send_delay: "0"
130+
features: "single-term-leader"
131+
132+
133+
steps:
134+
- name: Setup | Checkout
135+
uses: actions/checkout@v4
136+
137+
138+
- name: Setup | Toolchain
139+
uses: actions-rs/[email protected]
140+
with:
141+
toolchain: "${{ matrix.toolchain }}"
142+
override: true
114143

115144

116145
- name: Test crate `tests/`
@@ -130,9 +159,8 @@ jobs:
130159
uses: actions/upload-artifact@v3
131160
if: failure()
132161
with:
133-
name: "ut-${{ matrix.toolchain }}-${{ matrix.features }}-${{ matrix.send_delay }}"
162+
name: "ut-tests-${{ matrix.toolchain }}-${{ matrix.features }}-${{ matrix.send_delay }}"
134163
path: |
135-
openraft/_log/
136164
tests/_log/
137165
138166
@@ -252,16 +280,8 @@ jobs:
252280
- toolchain: "nightly"
253281
features: "serde"
254282

255-
# Some test requires feature single-term-leader on and serde off.
256-
# This can only be tested without building another crate that enables
257-
# `serde`.
258-
# Move this test to unit-test when the snapshot API is upgraded to
259-
# non-serde-dependent.
260-
- toolchain: "nightly"
261-
features: "single-term-leader"
262-
263283
- toolchain: "nightly"
264-
features: "single-term-leader,serde,singlethreaded"
284+
features: "serde,singlethreaded"
265285

266286

267287
steps:
@@ -364,7 +384,7 @@ jobs:
364384
shell: bash
365385
run: |
366386
cargo clippy --no-deps --workspace --all-targets -- -D warnings
367-
cargo clippy --no-deps --workspace --all-targets --features "bt,serde,bench,single-term-leader,compat" -- -D warnings
387+
cargo clippy --no-deps --workspace --all-targets --features "bt,serde,bench,compat" -- -D warnings
368388
369389
370390
- name: Build-doc

cluster_benchmark/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ tracing = "0.1.29"
3131
[features]
3232

3333
bt = ["openraft/bt"]
34-
single-term-leader = ["openraft/single-term-leader"]
34+
single-term-leader = []
3535

3636
[profile.release]
3737
debug = 2

cluster_benchmark/tests/benchmark/store.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,20 @@ pub struct ClientResponse {}
3737

3838
pub type NodeId = u64;
3939

40+
/// Choose a LeaderId implementation by feature flag.
41+
mod leader_id_mode {
42+
#[cfg(not(feature = "single-term-leader"))]
43+
pub use openraft::impls::leader_id_adv::LeaderId;
44+
#[cfg(feature = "single-term-leader")]
45+
pub use openraft::impls::leader_id_std::LeaderId;
46+
}
47+
4048
openraft::declare_raft_types!(
4149
pub TypeConfig:
4250
D = ClientRequest,
4351
R = ClientResponse,
4452
Node = (),
53+
LeaderId = leader_id_mode::LeaderId<TypeConfig>,
4554
);
4655

4756
#[derive(Debug)]

examples/utils/declare_types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::TypeConfig;
44
pub type Raft = openraft::Raft<TypeConfig>;
55

66
pub type Vote = openraft::Vote<TypeConfig>;
7-
pub type LeaderId = openraft::LeaderId<TypeConfig>;
7+
pub type LeaderId = <TypeConfig as openraft::RaftTypeConfig>::LeaderId;
88
pub type LogId = openraft::LogId<TypeConfig>;
99
pub type Entry = openraft::Entry<TypeConfig>;
1010
pub type EntryPayload = openraft::EntryPayload<TypeConfig>;

openraft/Cargo.toml

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,9 @@ bt = ["anyerror/backtrace", "anyhow/backtrace"]
6262
# If you'd like to use `serde` to serialize messages.
6363
serde = ["dep:serde"]
6464

65-
# Turn on this feature it allows at most ONE quorum-granted leader for each term.
66-
# This is the way standard raft does, by making the LeaderId a partial order value.
67-
#
68-
# - With this feature on:
69-
# It is more likely to conflict during election. But every log only needs to store one `term` in it.
70-
#
71-
# - With this feature off:
72-
# Election conflict rate will be reduced, but every log has to store a `LeaderId{ term, node_id}`,
73-
# which may be costly if an application uses a big NodeId type.
74-
#
75-
# This feature is disabled by default.
65+
# This feature is removed.
66+
# Use `openraft::impls::leader_id_std::Leader` for `RaftTypeConfig`
67+
# to enable standard Raft leader election.
7668
single-term-leader = []
7769

7870
# Enable this feature to use `openraft::alias::*` type shortcuts.
@@ -103,8 +95,6 @@ loosen-follower-log-revert = []
10395
# See: https://docs.rs/tracing/latest/tracing/#emitting-log-records
10496
tracing-log = [ "tracing/log" ]
10597

106-
# default = ["single-term-leader"]
107-
10898
[package.metadata.docs.rs]
10999

110100
# Enable these feature flags to show all types/mods,

openraft/src/core/notification.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::raft_state::IOId;
77
use crate::replication;
88
use crate::replication::ReplicationSessionId;
99
use crate::type_config::alias::InstantOf;
10-
use crate::vote::CommittedVote;
11-
use crate::vote::NonCommittedVote;
10+
use crate::vote::committed::CommittedVote;
11+
use crate::vote::non_committed::NonCommittedVote;
1212
use crate::RaftTypeConfig;
1313
use crate::StorageError;
1414
use crate::Vote;

openraft/src/core/raft_core.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ use crate::type_config::alias::ResponderOf;
9393
use crate::type_config::alias::WatchSenderOf;
9494
use crate::type_config::async_runtime::MpscUnboundedReceiver;
9595
use crate::type_config::TypeConfigExt;
96+
use crate::vote::committed::CommittedVote;
97+
use crate::vote::non_committed::NonCommittedVote;
9698
use crate::vote::vote_status::VoteStatus;
97-
use crate::vote::CommittedVote;
98-
use crate::vote::NonCommittedVote;
9999
use crate::vote::RaftLeaderId;
100100
use crate::ChangeMembers;
101101
use crate::Instant;

openraft/src/core/tick.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ mod tests {
180180
type NodeId = u64;
181181
type Node = ();
182182
type Term = u64;
183-
type LeaderId = crate::impls::LeaderId<Self>;
183+
type LeaderId = crate::impls::leader_id_adv::LeaderId<Self>;
184184
type Entry = crate::Entry<TickUTConfig>;
185185
type SnapshotData = Cursor<Vec<u8>>;
186186
type AsyncRuntime = TokioRuntime;

openraft/src/docs/data/leader_id.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
## Leader-id in Advanced mode and Standard mode
22

3-
Openraft provides two `LeaderId` types to switch between these two modes with feature [`single-term-leader`] :
3+
Openraft provides two `LeaderId` types to switch between these two modes:
4+
- the default mode: every term may have more than one leader
5+
(enabled by default, or explicitly by setting [`RaftTypeConfig::LeaderId`] to [`leader_id_adv::LeaderId`]).
6+
- and the standard Raft mode: every term has only one leader
7+
(enabled by setting [`RaftTypeConfig::LeaderId`] to [`leader_id_std::LeaderId`]).
48

5-
The feature [`single-term-leader`] affects the `PartialOrd` implementation of `LeaderId`, where `LeaderId` is defined as a tuple `(term, node_id)`.
9+
[`leader_id_adv::LeaderId`] is totally ordered.
10+
[`leader_id_std::LeaderId`] is `PartialOrd`.
611

712
### Definition of `LeaderId`
813

@@ -15,27 +20,26 @@ Within Openraft, and also implicitly in the standard Raft, `LeaderId` is utilize
1520
`A.term > B.term ↔ A > B`.
1621
<br/><br/>
1722

18-
- Conversely, in Openraft, with the [`single-term-leader`] feature disabled by default, `LeaderId` follows a `total order` based on lexicographical comparison:
23+
- Conversely, in Openraft, with [`leader_id_adv::LeaderId`], `LeaderId` follows a `total order` based on lexicographical comparison:
1924

2025
`A.term > B.term || (A.term == B.term && A.node_id > B.node_id) ↔ A > B`.
2126

22-
Activating the `single-term-leader` feature makes `LeaderId` conform to the `partial order` seen in standard Raft.
27+
Using [`leader_id_std::LeaderId`] makes `LeaderId` conform to the `partial order` seen in standard Raft.
2328

2429
### Usage of `LeaderId`
2530

2631
When handling `VoteRequest`, both Openraft and standard Raft (though not explicitly detailed) rely on the ordering of `LeaderId` to decide whether to grant a vote:
2732
**a node will grant a vote with a `LeaderId` that is greater than any it has previously granted**.
2833

29-
Consequently, by default in Openraft (with `single-term-leader` disabled), it is possible to elect multiple `Leader`s within the same term, with the last elected `Leader` being recognized as valid. In contrast, under standard Raft protocol, only a single `Leader` is elected per `term`.
34+
Consequently, by default in Openraft (with [`leader_id_adv::LeaderId`]), it is possible to elect multiple `Leader`s within the same term, with the last elected `Leader` being recognized as valid. In contrast, under standard Raft protocol, only a single `Leader` is elected per `term`.
3035

3136
### Default: advanced mode
3237

33-
`cargo build` without [`single-term-leader`][], is the advanced mode, the default mode:
38+
Use `openraft::impls::leader_id_adv::LeaderId` for [`RaftTypeConfig::LeaderId`] or leave it to default to switch to advanced mode.
3439
`LeaderId` is defined as the following, and it is a **totally ordered** value(two or more leaders can be granted in the same term):
3540

3641
```ignore
3742
// Advanced mode(default):
38-
#[cfg(not(feature = "single-term-leader"))]
3943
#[derive(PartialOrd, Ord)]
4044
pub struct LeaderId<NID: NodeId>
4145
{
@@ -57,12 +61,11 @@ elected(although only the last is valid and can commit logs).
5761

5862
#### Standard mode
5963

60-
`cargo build --features "single-term-leader"` builds openraft in standard raft mode.
64+
Use `openraft::impls::leader_id_std::LeaderId` for [`RaftTypeConfig::LeaderId`] to switch to standard mode.
6165
In the standard mode, `LeaderId` is defined as the following, and it is a **partially ordered** value(no two leaders can be granted in the same term):
6266

6367
```ignore
6468
// Standard raft mode:
65-
#[cfg(feature = "single-term-leader")]
6669
pub struct LeaderId<NID: NodeId>
6770
{
6871
pub term: u64,
@@ -125,4 +128,6 @@ So let a committed `Vote` override a incomparable non-committed is safe.
125128
- Cons: election conflicting rate may increase.
126129

127130

128-
[`single-term-leader`]: crate::docs::feature_flags
131+
[`RaftTypeConfig::LeaderId`]: crate::RaftTypeConfig::LeaderId
132+
[`leader_id_adv::LeaderId`]: `crate::impls::leader_id_adv::LeaderId`
133+
[`leader_id_std::LeaderId`]: `crate::impls::leader_id_std::LeaderId`

openraft/src/docs/data/vote.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ i.e., it is legal that `!(vote_a => vote_b) && !(vote_a <= vote_b)`.
3434
Because `Vote.leader_id` may be a partial order value:
3535

3636
Openraft provides two election modes.
37-
- the default mode: every term may have more than one leader.
38-
- and the standard Raft mode: every term has only one leader(enabled by [`single-term-leader`]),
37+
- the default mode: every term may have more than one leader
38+
(enabled by default, or explicitly by setting [`RaftTypeConfig::LeaderId`] to [`leader_id_adv::LeaderId`]).
39+
- and the standard Raft mode: every term has only one leader
40+
(enabled by setting [`RaftTypeConfig::LeaderId`] to [`leader_id_std::LeaderId`]),
3941

4042
The only difference between these two modes is the definition of `LeaderId`, and the `PartialOrd` implementation of it.
4143
See: [`leader-id`].
@@ -79,5 +81,7 @@ For node-2:
7981

8082

8183
[`Vote`]: `crate::vote::Vote`
82-
[`single-term-leader`]: `crate::docs::feature_flags`
84+
[`RaftTypeConfig::LeaderId`]: `crate::RaftTypeConfig::LeaderId`
85+
[`leader_id_adv::LeaderId`]: `crate::impls::leader_id_adv::LeaderId`
86+
[`leader_id_std::LeaderId`]: `crate::impls::leader_id_std::LeaderId`
8387
[`leader-id`]: `crate::docs::data::leader_id`

openraft/src/docs/faq/faq.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,6 @@ pub(crate) fn following_handler(&mut self) -> FollowingHandler<C> {
237237
```
238238

239239

240-
[`single-term-leader`]: `crate::docs::feature_flags#single_term_leader`
241-
242240
[`Linearizable Read`]: `crate::docs::protocol::read`
243241
[`leader_id`]: `crate::docs::data::leader_id`
244242

openraft/src/docs/feature_flags/feature-flags.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ in storage and network, such as `Vote` or `AppendEntriesRequest`.
2424

2525
## feature-flag `single-term-leader`
2626

27+
**This feature flag is removed**.
28+
User [`leader_id_std::LeaderId`] in [`RaftTypeConfig`] instead.
29+
2730
Allows only one leader to be elected in each `term`.
2831
This is the standard raft policy, which increases election conflict rate
2932
but reduce `LogId` size(`(term, node_id, index)` to `(term, index)`).
@@ -72,3 +75,6 @@ let snapshot_data: <MyTypeConfig as RaftTypeConfig>::SnapshotData;
7275
Note that the type shortcuts are not stable and may be changed in the future.
7376
It is also a good idea to copy the type shortcuts to your own codebase if you
7477
want to use them.
78+
79+
[`RaftTypeConfig`]: crate::RaftTypeConfig
80+
[`leader_id_std::LeaderId`]: crate::impls::leader_id_std::LeaderId

openraft/src/engine/command.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::raft_state::IOId;
2121
use crate::replication::request::Replicate;
2222
use crate::replication::ReplicationSessionId;
2323
use crate::type_config::alias::OneshotSenderOf;
24-
use crate::vote::CommittedVote;
24+
use crate::vote::committed::CommittedVote;
2525
use crate::LogId;
2626
use crate::OptionalSend;
2727
use crate::RaftTypeConfig;

openraft/src/engine/engine_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ use crate::storage::SnapshotMeta;
4747
use crate::type_config::alias::ResponderOf;
4848
use crate::type_config::alias::SnapshotDataOf;
4949
use crate::type_config::TypeConfigExt;
50-
use crate::vote::raft_term::RaftTerm;
5150
use crate::vote::RaftLeaderId;
51+
use crate::vote::RaftTerm;
5252
use crate::LogId;
5353
use crate::LogIdOptionExt;
5454
use crate::Membership;

openraft/src/engine/handler/following_handler/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::error::RejectAppendEntries;
1616
use crate::raft_state::IOId;
1717
use crate::raft_state::LogStateReader;
1818
use crate::storage::Snapshot;
19-
use crate::vote::CommittedVote;
19+
use crate::vote::committed::CommittedVote;
2020
use crate::EffectiveMembership;
2121
use crate::LogId;
2222
use crate::LogIdOptionExt;

openraft/src/engine/testing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ where N: Node + Ord
3737
type Node = N;
3838
type Entry = crate::Entry<Self>;
3939
type Term = u64;
40-
type LeaderId = crate::impls::LeaderId<Self>;
40+
type LeaderId = crate::impls::leader_id_adv::LeaderId<Self>;
4141
type SnapshotData = Cursor<Vec<u8>>;
4242
type AsyncRuntime = TokioRuntime;
4343
type Responder = crate::impls::OneshotResponder<Self>;

0 commit comments

Comments
 (0)