This is an automated email from the ASF dual-hosted git repository.
williamsong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ratis.git
The following commit(s) were added to refs/heads/master by this push:
new 5ea4f02f4 RATIS-1930. Add a conf for enable/disable majority-add.
(#961)
5ea4f02f4 is described below
commit 5ea4f02f4aac920eefd023cb2e3fb359b06c400a
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Mon Nov 6 18:59:50 2023 -0800
RATIS-1930. Add a conf for enable/disable majority-add. (#961)
---
ratis-docs/src/site/markdown/configurations.md | 19 ++++++++++++++++++-
.../apache/ratis/server/RaftServerConfigKeys.java | 21 ++++++++++++++++++++-
.../apache/ratis/server/impl/RaftServerImpl.java | 11 ++++++++---
.../ratis/InstallSnapshotFromLeaderTests.java | 10 ++++------
4 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/ratis-docs/src/site/markdown/configurations.md
b/ratis-docs/src/site/markdown/configurations.md
index 4fc3c5caa..dd953e7fd 100644
--- a/ratis-docs/src/site/markdown/configurations.md
+++ b/ratis-docs/src/site/markdown/configurations.md
@@ -646,7 +646,7 @@ First election timeout is introduced to reduce unavailable
time when a RaftGroup
| **Property** | `raft.server.leaderelection.pre-vote` |
|:----------------|:--------------------------------------|
| **Description** | enable pre-vote |
-| **Type** | bool |
+| **Type** | boolean |
| **Default** | true |
In Pre-Vote, the candidate does not change its term and try to learn
@@ -654,3 +654,20 @@ if a majority of the cluster would be willing to grant the
candidate their votes
(if the candidate’s log is sufficiently up-to-date,
and the voters have not received heartbeats from a valid leader
for at least a baseline election timeout).
+
+| **Property** | `raft.server.leaderelection.member.majority-add` |
+|:----------------|:-------------------------------------------------|
+| **Description** | enable majority-add |
+| **Type** | boolean |
+| **Default** | false |
+
+Does it allow *majority-add*, i.e. adding a majority of members in a single
setConf?
+
+- Note that, when a single setConf removes and adds members at the same time,
+the majority is counted after the removal.
+For examples,
+ 1. setConf to a 3-member group by adding 2 new members is NOT a majority-add.
+ 2. However, setConf to a 3-member group by removing 2 of members and adding
2 new members is a majority-add.
+
+- Note also that adding 1 new member to an 1-member group is always allowed,
+ although it is a majority-add.
\ No newline at end of file
diff --git
a/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
b/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
index a59bd8b37..f2de074a8 100644
---
a/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
+++
b/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
@@ -881,10 +881,29 @@ public interface RaftServerConfigKeys {
static boolean preVote(RaftProperties properties) {
return getBoolean(properties::getBoolean, PRE_VOTE_KEY,
PRE_VOTE_DEFAULT, getDefaultLog());
}
-
static void setPreVote(RaftProperties properties, boolean enablePreVote) {
setBoolean(properties::setBoolean, PRE_VOTE_KEY, enablePreVote);
}
+
+ /**
+ * Does it allow majority-add, i.e. adding a majority of members in a
single setConf?
+ * <p>
+ * Note that, when a single setConf removes and adds members at the same
time,
+ * the majority is counted after the removal.
+ * For examples, setConf to a 3-member group by adding 2 new members is
NOT a majority-add.
+ * However, setConf to a 3-member group by removing 2 of members and
adding 2 new members is a majority-add.
+ * <p>
+ * Note also that adding 1 new member to an 1-member group is always
allowed,
+ * although it is a majority-add.
+ */
+ String MEMBER_MAJORITY_ADD_KEY = PREFIX + ".member.majority-add";
+ boolean MEMBER_MAJORITY_ADD_DEFAULT = false;
+ static boolean memberMajorityAdd(RaftProperties properties) {
+ return getBoolean(properties::getBoolean, MEMBER_MAJORITY_ADD_KEY,
MEMBER_MAJORITY_ADD_DEFAULT, getDefaultLog());
+ }
+ static void setMemberMajorityAdd(RaftProperties properties, boolean
enableMemberMajorityAdd) {
+ setBoolean(properties::setBoolean, MEMBER_MAJORITY_ADD_KEY,
enableMemberMajorityAdd);
+ }
}
static void main(String[] args) {
diff --git
a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
index 2cbd3146a..5e6c81215 100644
---
a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
+++
b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
@@ -214,6 +214,7 @@ class RaftServerImpl implements RaftServer.Division,
private final DivisionProperties divisionProperties;
private final TimeDuration leaderStepDownWaitTime;
+ private final boolean memberMajorityAddEnabled;
private final TimeDuration sleepDeviationThreshold;
private final LifeCycle lifeCycle;
@@ -259,7 +260,8 @@ class RaftServerImpl implements RaftServer.Division,
final RaftProperties properties = proxy.getProperties();
this.divisionProperties = new DivisionPropertiesImpl(properties);
- leaderStepDownWaitTime =
RaftServerConfigKeys.LeaderElection.leaderStepDownWaitTime(properties);
+ this.leaderStepDownWaitTime =
RaftServerConfigKeys.LeaderElection.leaderStepDownWaitTime(properties);
+ this.memberMajorityAddEnabled =
RaftServerConfigKeys.LeaderElection.memberMajorityAdd(properties);
this.sleepDeviationThreshold =
RaftServerConfigKeys.sleepDeviationThreshold(properties);
this.proxy = proxy;
@@ -1314,8 +1316,11 @@ class RaftServerImpl implements RaftServer.Division,
return pending.getFuture();
}
if (current.changeMajority(serversInNewConf)) {
- throw new SetConfigurationException("Failed to set configuration:
request " + request
- + " changes a majority set of the current configuration " +
current);
+ if (!memberMajorityAddEnabled) {
+ throw new SetConfigurationException("Failed to set configuration:
request " + request
+ + " changes a majority set of the current configuration " +
current);
+ }
+ LOG.warn("Try to add/replace a majority of servers in a single
setConf: {}", request);
}
getRaftServer().addRaftPeers(serversInNewConf);
diff --git
a/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java
b/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java
index e51c98548..15dafb88c 100644
---
a/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java
+++
b/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java
@@ -21,7 +21,6 @@ import org.apache.ratis.client.RaftClient;
import org.apache.ratis.conf.RaftProperties;
import org.apache.ratis.protocol.RaftClientReply;
import org.apache.ratis.protocol.RaftGroupId;
-import org.apache.ratis.protocol.RaftPeer;
import org.apache.ratis.protocol.RaftPeerId;
import org.apache.ratis.server.RaftServer;
import org.apache.ratis.server.RaftServerConfigKeys;
@@ -65,11 +64,11 @@ public abstract class
InstallSnapshotFromLeaderTests<CLUSTER extends MiniRaftClu
{
final RaftProperties prop = getProperties();
- RaftServerConfigKeys.Log.setPurgeGap(prop, PURGE_GAP);
- RaftServerConfigKeys.Snapshot.setAutoTriggerThreshold(
- prop, SNAPSHOT_TRIGGER_THRESHOLD);
RaftServerConfigKeys.Snapshot.setAutoTriggerEnabled(prop, true);
+ RaftServerConfigKeys.Snapshot.setAutoTriggerThreshold(prop,
SNAPSHOT_TRIGGER_THRESHOLD);
+ RaftServerConfigKeys.Log.setPurgeGap(prop, PURGE_GAP);
RaftServerConfigKeys.Log.Appender.setSnapshotChunkSizeMax(prop,
SizeInBytes.ONE_KB);
+ RaftServerConfigKeys.LeaderElection.setMemberMajorityAdd(prop, true);
}
private static final int SNAPSHOT_TRIGGER_THRESHOLD = 64;
@@ -112,8 +111,7 @@ public abstract class
InstallSnapshotFromLeaderTests<CLUSTER extends MiniRaftClu
final MiniRaftCluster.PeerChanges change = cluster.addNewPeers(2, true,
true);
// trigger setConfiguration
- RaftServerTestUtil.runWithMinorityPeers(cluster,
Arrays.asList(change.allPeersInNewConf),
- peers ->
cluster.setConfiguration(peers.toArray(RaftPeer.emptyArray())));
+ cluster.setConfiguration(change.allPeersInNewConf);
RaftServerTestUtil
.waitAndCheckNewConf(cluster, change.allPeersInNewConf, 0, null);