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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::fmt;
use display_more::DisplayOptionExt;
use openraft_macros::since;
use crate::RaftState;
use crate::RaftTypeConfig;
use crate::errors::PreconditionFailed;
use crate::raft_state::LogStateReader;
use crate::type_config::alias::CommittedLeaderIdOf;
use crate::type_config::alias::LogIdOf;
use crate::vote::raft_vote::RaftVoteExt;
/// A condition on the Raft state that must hold before an operation is proposed.
///
/// The Raft core checks it on the leader, against the state at the moment the
/// operation is about to be proposed. If it does not hold, nothing is written
/// and the operation fails with [`PreconditionFailed`].
#[since(version = "0.10.0")]
#[derive(Debug, Clone)]
#[derive(PartialEq, Eq)]
pub enum Precondition<C>
where C: RaftTypeConfig
{
/// Require `committed_leader_id` to still be the established leader.
///
/// Satisfied only when this node's vote is committed and elects exactly `committed_leader_id`.
/// A node whose vote is not yet committed has no established leader and never satisfies it.
CommittedLeaderId {
committed_leader_id: CommittedLeaderIdOf<C>,
},
/// Require the last log id to be exactly `last_log_id`, where `None` means an empty log.
///
/// The last log entry may be uncommitted, and an uncommitted tail is reverted when a new
/// leader is elected without having seen it. Satisfying this condition therefore does not
/// guarantee the matched entry survives.
LastLogId { last_log_id: Option<LogIdOf<C>> },
/// Require the effective membership to be the one stored at `last_membership_log_id`, where
/// `None` means no membership log has been appended.
///
/// Use it to serialize membership changes: the change is proposed only while the effective
/// membership is still the one the caller based its decision on, so a concurrent change made
/// in between is rejected instead of silently overwritten.
///
/// The effective membership may itself be uncommitted, and an uncommitted membership log is
/// reverted when a new leader is elected without having seen it. Satisfying this condition
/// therefore does not guarantee the matched membership survives.
LastMembershipLogId { last_membership_log_id: Option<LogIdOf<C>> },
}
impl<C> Precondition<C>
where C: RaftTypeConfig
{
/// Return `Ok(())` if `state` satisfies this condition, otherwise the mismatch it found.
pub fn ensure_satisfied(&self, state: &RaftState<C>) -> Result<(), PreconditionFailed<C>> {
match self {
Precondition::CommittedLeaderId { committed_leader_id } => {
let actual = state.vote_ref().try_to_committed_leader_id();
if actual.as_ref() == Some(committed_leader_id) {
Ok(())
} else {
Err(PreconditionFailed::CommittedLeaderIdMismatch {
expected: committed_leader_id.clone(),
actual,
})
}
}
Precondition::LastLogId { last_log_id } => {
let actual = state.last_log_id();
if actual == last_log_id.as_ref() {
Ok(())
} else {
Err(PreconditionFailed::LastLogIdMismatch {
expected: last_log_id.clone(),
actual: actual.cloned(),
})
}
}
Precondition::LastMembershipLogId { last_membership_log_id } => {
let actual = state.membership_state.effective().log_id();
if actual == last_membership_log_id {
Ok(())
} else {
Err(PreconditionFailed::LastMembershipLogIdMismatch {
expected: last_membership_log_id.clone(),
actual: actual.clone(),
})
}
}
}
}
}
impl<C> fmt::Display for Precondition<C>
where C: RaftTypeConfig
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Precondition::CommittedLeaderId { committed_leader_id } => {
write!(f, "CommittedLeaderId({})", committed_leader_id)
}
Precondition::LastLogId { last_log_id } => {
write!(f, "LastLogId({})", last_log_id.display())
}
Precondition::LastMembershipLogId { last_membership_log_id } => {
write!(f, "LastMembershipLogId({})", last_membership_log_id.display())
}
}
}
}
#[cfg(test)]
mod precondition_test;