Add term and config to cluster state (#32100)
Adds the publication term and the last accepted and committed configurations to the cluster state, following the formal model in https://github.com/elastic/elasticsearch-formal-models/blob/master/ZenWithTerms/tla/ZenWithTerms.tla The term represents the reign of a master, and the last committed / accepted configurations represent the set of quorums that cluster state changes will require (If there's no reconfiguration, last accepted and last committed configurations coincide).
This commit is contained in:
parent
e31a877a64
commit
ad78f73942
|
@ -173,8 +173,8 @@ task verifyVersions {
|
|||
* the enabled state of every bwc task. It should be set back to true
|
||||
* after the backport of the backcompat code is complete.
|
||||
*/
|
||||
final boolean bwc_tests_enabled = true
|
||||
final String bwc_tests_disabled_issue = "" /* place a PR link here when committing bwc changes */
|
||||
final boolean bwc_tests_enabled = false
|
||||
final String bwc_tests_disabled_issue = "https://github.com/elastic/elasticsearch/issues/32006" // BWC handled at a later time
|
||||
if (bwc_tests_enabled == false) {
|
||||
if (bwc_tests_disabled_issue.isEmpty()) {
|
||||
throw new GradleException("bwc_tests_disabled_issue must be set when bwc_tests_enabled == false")
|
||||
|
|
|
@ -76,8 +76,11 @@ public class TransportClusterStateAction extends TransportMasterNodeReadAction<C
|
|||
ClusterState currentState = clusterService.state();
|
||||
logger.trace("Serving cluster state request using version {}", currentState.version());
|
||||
ClusterState.Builder builder = ClusterState.builder(currentState.getClusterName());
|
||||
builder.term(currentState.term());
|
||||
builder.version(currentState.version());
|
||||
builder.stateUUID(currentState.stateUUID());
|
||||
builder.lastCommittedConfiguration(currentState.getLastCommittedConfiguration());
|
||||
builder.lastAcceptedConfiguration(currentState.getLastAcceptedConfiguration());
|
||||
if (request.nodes()) {
|
||||
builder.nodes(currentState.nodes());
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ package org.elasticsearch.cluster;
|
|||
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.cluster.block.ClusterBlock;
|
||||
import org.elasticsearch.cluster.block.ClusterBlocks;
|
||||
|
@ -49,8 +51,10 @@ import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
|||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.io.stream.VersionedNamedWriteable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
|
@ -58,10 +62,14 @@ import org.elasticsearch.discovery.Discovery;
|
|||
import org.elasticsearch.discovery.zen.PublishClusterStateAction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -154,6 +162,8 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
|
||||
public static final long UNKNOWN_VERSION = -1;
|
||||
|
||||
private final long term;
|
||||
|
||||
private final long version;
|
||||
|
||||
private final String stateUUID;
|
||||
|
@ -172,16 +182,23 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
|
||||
private final boolean wasReadFromDiff;
|
||||
|
||||
private final VotingConfiguration lastCommittedConfiguration;
|
||||
|
||||
private final VotingConfiguration lastAcceptedConfiguration;
|
||||
|
||||
// built on demand
|
||||
private volatile RoutingNodes routingNodes;
|
||||
|
||||
public ClusterState(long version, String stateUUID, ClusterState state) {
|
||||
this(state.clusterName, version, stateUUID, state.metaData(), state.routingTable(), state.nodes(), state.blocks(), state.customs(),
|
||||
false);
|
||||
public ClusterState(long term, long version, String stateUUID, ClusterState state) {
|
||||
this(state.clusterName, term, version, stateUUID, state.metaData(), state.routingTable(), state.nodes(), state.blocks(),
|
||||
state.customs(), state.getLastCommittedConfiguration(), state.getLastAcceptedConfiguration(), false);
|
||||
}
|
||||
|
||||
public ClusterState(ClusterName clusterName, long version, String stateUUID, MetaData metaData, RoutingTable routingTable,
|
||||
DiscoveryNodes nodes, ClusterBlocks blocks, ImmutableOpenMap<String, Custom> customs, boolean wasReadFromDiff) {
|
||||
public ClusterState(ClusterName clusterName, long term, long version, String stateUUID, MetaData metaData, RoutingTable routingTable,
|
||||
DiscoveryNodes nodes, ClusterBlocks blocks, ImmutableOpenMap<String, Custom> customs,
|
||||
VotingConfiguration lastCommittedConfiguration, VotingConfiguration lastAcceptedConfiguration,
|
||||
boolean wasReadFromDiff) {
|
||||
this.term = term;
|
||||
this.version = version;
|
||||
this.stateUUID = stateUUID;
|
||||
this.clusterName = clusterName;
|
||||
|
@ -190,9 +207,15 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
this.nodes = nodes;
|
||||
this.blocks = blocks;
|
||||
this.customs = customs;
|
||||
this.lastCommittedConfiguration = lastCommittedConfiguration;
|
||||
this.lastAcceptedConfiguration = lastAcceptedConfiguration;
|
||||
this.wasReadFromDiff = wasReadFromDiff;
|
||||
}
|
||||
|
||||
public long term() {
|
||||
return term;
|
||||
}
|
||||
|
||||
public long version() {
|
||||
return this.version;
|
||||
}
|
||||
|
@ -257,6 +280,14 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
return this.clusterName;
|
||||
}
|
||||
|
||||
public VotingConfiguration getLastAcceptedConfiguration() {
|
||||
return lastAcceptedConfiguration;
|
||||
}
|
||||
|
||||
public VotingConfiguration getLastCommittedConfiguration() {
|
||||
return lastCommittedConfiguration;
|
||||
}
|
||||
|
||||
// Used for testing and logging to determine how this cluster state was send over the wire
|
||||
public boolean wasReadFromDiff() {
|
||||
return wasReadFromDiff;
|
||||
|
@ -277,8 +308,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("cluster uuid: ").append(metaData.clusterUUID()).append("\n");
|
||||
sb.append("term: ").append(term).append("\n");
|
||||
sb.append("version: ").append(version).append("\n");
|
||||
sb.append("state uuid: ").append(stateUUID).append("\n");
|
||||
sb.append("last committed config: ").append(getLastCommittedConfiguration()).append("\n");
|
||||
sb.append("last accepted config: ").append(getLastAcceptedConfiguration()).append("\n");
|
||||
sb.append("from_diff: ").append(wasReadFromDiff).append("\n");
|
||||
sb.append("meta data version: ").append(metaData.version()).append("\n");
|
||||
final String TAB = " ";
|
||||
|
@ -386,8 +420,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
builder.field("cluster_uuid", metaData().clusterUUID());
|
||||
|
||||
if (metrics.contains(Metric.VERSION)) {
|
||||
builder.field("term", term);
|
||||
builder.field("version", version);
|
||||
builder.field("state_uuid", stateUUID);
|
||||
builder.field("last_committed_config", lastCommittedConfiguration);
|
||||
builder.field("last_accepted_config", lastAcceptedConfiguration);
|
||||
}
|
||||
|
||||
if (metrics.contains(Metric.MASTER_NODE)) {
|
||||
|
@ -587,8 +624,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
public static class Builder {
|
||||
|
||||
private final ClusterName clusterName;
|
||||
private long term = 0;
|
||||
private long version = 0;
|
||||
private String uuid = UNKNOWN_UUID;
|
||||
private VotingConfiguration lastCommittedConfiguration = VotingConfiguration.EMPTY_CONFIG;
|
||||
private VotingConfiguration lastAcceptedConfiguration = VotingConfiguration.EMPTY_CONFIG;
|
||||
private MetaData metaData = MetaData.EMPTY_META_DATA;
|
||||
private RoutingTable routingTable = RoutingTable.EMPTY_ROUTING_TABLE;
|
||||
private DiscoveryNodes nodes = DiscoveryNodes.EMPTY_NODES;
|
||||
|
@ -599,8 +639,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
|
||||
public Builder(ClusterState state) {
|
||||
this.clusterName = state.clusterName;
|
||||
this.term = state.term();
|
||||
this.version = state.version();
|
||||
this.uuid = state.stateUUID();
|
||||
this.lastCommittedConfiguration = state.getLastCommittedConfiguration();
|
||||
this.lastAcceptedConfiguration = state.getLastAcceptedConfiguration();
|
||||
this.nodes = state.nodes();
|
||||
this.routingTable = state.routingTable();
|
||||
this.metaData = state.metaData();
|
||||
|
@ -650,6 +693,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder term(long term) {
|
||||
this.term = term;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder version(long version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
|
@ -666,6 +714,16 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder lastCommittedConfiguration(VotingConfiguration config) {
|
||||
this.lastCommittedConfiguration = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder lastAcceptedConfiguration(VotingConfiguration config) {
|
||||
this.lastAcceptedConfiguration = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder putCustom(String type, Custom custom) {
|
||||
customs.put(type, custom);
|
||||
return this;
|
||||
|
@ -690,7 +748,8 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
if (UNKNOWN_UUID.equals(uuid)) {
|
||||
uuid = UUIDs.randomBase64UUID();
|
||||
}
|
||||
return new ClusterState(clusterName, version, uuid, metaData, routingTable, nodes, blocks, customs.build(), fromDiff);
|
||||
return new ClusterState(clusterName, term, version, uuid, metaData, routingTable, nodes, blocks, customs.build(),
|
||||
lastCommittedConfiguration, lastAcceptedConfiguration, fromDiff);
|
||||
}
|
||||
|
||||
public static byte[] toBytes(ClusterState state) throws IOException {
|
||||
|
@ -722,8 +781,15 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
public static ClusterState readFrom(StreamInput in, DiscoveryNode localNode) throws IOException {
|
||||
ClusterName clusterName = new ClusterName(in);
|
||||
Builder builder = new Builder(clusterName);
|
||||
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
builder.term = in.readLong();
|
||||
}
|
||||
builder.version = in.readLong();
|
||||
builder.uuid = in.readString();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
builder.lastCommittedConfiguration(new VotingConfiguration(in));
|
||||
builder.lastAcceptedConfiguration(new VotingConfiguration(in));
|
||||
}
|
||||
builder.metaData = MetaData.readFrom(in);
|
||||
builder.routingTable = RoutingTable.readFrom(in);
|
||||
builder.nodes = DiscoveryNodes.readFrom(in, localNode);
|
||||
|
@ -739,8 +805,15 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
clusterName.writeTo(out);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
out.writeLong(term);
|
||||
}
|
||||
out.writeLong(version);
|
||||
out.writeString(stateUUID);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
lastCommittedConfiguration.writeTo(out);
|
||||
lastAcceptedConfiguration.writeTo(out);
|
||||
}
|
||||
metaData.writeTo(out);
|
||||
routingTable.writeTo(out);
|
||||
nodes.writeTo(out);
|
||||
|
@ -762,6 +835,8 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
|
||||
private static class ClusterStateDiff implements Diff<ClusterState> {
|
||||
|
||||
private final long toTerm;
|
||||
|
||||
private final long toVersion;
|
||||
|
||||
private final String fromUuid;
|
||||
|
@ -770,6 +845,10 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
|
||||
private final ClusterName clusterName;
|
||||
|
||||
private final VotingConfiguration lastCommittedConfiguration;
|
||||
|
||||
private final VotingConfiguration lastAcceptedConfiguration;
|
||||
|
||||
private final Diff<RoutingTable> routingTable;
|
||||
|
||||
private final Diff<DiscoveryNodes> nodes;
|
||||
|
@ -783,8 +862,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
ClusterStateDiff(ClusterState before, ClusterState after) {
|
||||
fromUuid = before.stateUUID;
|
||||
toUuid = after.stateUUID;
|
||||
toTerm = after.term;
|
||||
toVersion = after.version;
|
||||
clusterName = after.clusterName;
|
||||
lastCommittedConfiguration = after.lastCommittedConfiguration;
|
||||
lastAcceptedConfiguration = after.lastAcceptedConfiguration;
|
||||
routingTable = after.routingTable.diff(before.routingTable);
|
||||
nodes = after.nodes.diff(before.nodes);
|
||||
metaData = after.metaData.diff(before.metaData);
|
||||
|
@ -796,7 +878,19 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
clusterName = new ClusterName(in);
|
||||
fromUuid = in.readString();
|
||||
toUuid = in.readString();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
toTerm = in.readLong();
|
||||
} else {
|
||||
toTerm = 0L;
|
||||
}
|
||||
toVersion = in.readLong();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
lastCommittedConfiguration = new VotingConfiguration(in);
|
||||
lastAcceptedConfiguration = new VotingConfiguration(in);
|
||||
} else {
|
||||
lastCommittedConfiguration = VotingConfiguration.EMPTY_CONFIG;
|
||||
lastAcceptedConfiguration = VotingConfiguration.EMPTY_CONFIG;
|
||||
}
|
||||
routingTable = RoutingTable.readDiffFrom(in);
|
||||
nodes = DiscoveryNodes.readDiffFrom(in, localNode);
|
||||
metaData = MetaData.readDiffFrom(in);
|
||||
|
@ -809,7 +903,14 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
clusterName.writeTo(out);
|
||||
out.writeString(fromUuid);
|
||||
out.writeString(toUuid);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
out.writeLong(toTerm);
|
||||
}
|
||||
out.writeLong(toVersion);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
lastCommittedConfiguration.writeTo(out);
|
||||
lastAcceptedConfiguration.writeTo(out);
|
||||
}
|
||||
routingTable.writeTo(out);
|
||||
nodes.writeTo(out);
|
||||
metaData.writeTo(out);
|
||||
|
@ -828,7 +929,10 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
throw new IncompatibleClusterStateVersionException(state.version, state.stateUUID, toVersion, fromUuid);
|
||||
}
|
||||
builder.stateUUID(toUuid);
|
||||
builder.term(toTerm);
|
||||
builder.version(toVersion);
|
||||
builder.lastCommittedConfiguration(lastCommittedConfiguration);
|
||||
builder.lastAcceptedConfiguration(lastAcceptedConfiguration);
|
||||
builder.routingTable(routingTable.apply(state.routingTable));
|
||||
builder.nodes(nodes.apply(state.nodes));
|
||||
builder.metaData(metaData.apply(state.metaData));
|
||||
|
@ -840,4 +944,67 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of persistent node ids, denoting the voting configuration for cluster state changes.
|
||||
*/
|
||||
public static class VotingConfiguration implements Writeable, ToXContentFragment {
|
||||
|
||||
public static final VotingConfiguration EMPTY_CONFIG = new VotingConfiguration(Collections.emptySet());
|
||||
|
||||
private final Set<String> nodeIds;
|
||||
|
||||
public VotingConfiguration(Set<String> nodeIds) {
|
||||
this.nodeIds = Collections.unmodifiableSet(new HashSet<>(nodeIds));
|
||||
}
|
||||
|
||||
public VotingConfiguration(StreamInput in) throws IOException {
|
||||
nodeIds = Collections.unmodifiableSet(Sets.newHashSet(in.readStringArray()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeStringArray(nodeIds.toArray(new String[nodeIds.size()]));
|
||||
}
|
||||
|
||||
public boolean hasQuorum(Collection<String> votes) {
|
||||
final HashSet<String> intersection = new HashSet<>(nodeIds);
|
||||
intersection.retainAll(votes);
|
||||
return intersection.size() * 2 > nodeIds.size();
|
||||
}
|
||||
|
||||
public Set<String> getNodeIds() {
|
||||
return nodeIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VotingConfiguration{" + String.join(",", nodeIds) + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VotingConfiguration that = (VotingConfiguration) o;
|
||||
return Objects.equals(nodeIds, that.nodeIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(nodeIds);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return nodeIds.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startArray();
|
||||
for (String nodeId : nodeIds) {
|
||||
builder.value(nodeId);
|
||||
}
|
||||
return builder.endArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,8 +70,11 @@ public class ClusterRerouteResponseTests extends ESTestCase {
|
|||
" \"acknowledged\" : true,\n" +
|
||||
" \"state\" : {\n" +
|
||||
" \"cluster_uuid\" : \"_na_\",\n" +
|
||||
" \"term\" : 0,\n" +
|
||||
" \"version\" : 0,\n" +
|
||||
" \"state_uuid\" : \"" + clusterState.stateUUID() + "\",\n" +
|
||||
" \"last_committed_config\" : [ ],\n" +
|
||||
" \"last_accepted_config\" : [ ],\n" +
|
||||
" \"master_node\" : \"node0\",\n" +
|
||||
" \"blocks\" : { },\n" +
|
||||
" \"nodes\" : {\n" +
|
||||
|
@ -138,8 +141,11 @@ public class ClusterRerouteResponseTests extends ESTestCase {
|
|||
" \"acknowledged\" : true,\n" +
|
||||
" \"state\" : {\n" +
|
||||
" \"cluster_uuid\" : \"_na_\",\n" +
|
||||
" \"term\" : 0,\n" +
|
||||
" \"version\" : 0,\n" +
|
||||
" \"state_uuid\" : \"" + clusterState.stateUUID() + "\",\n" +
|
||||
" \"last_committed_config\" : [ ],\n" +
|
||||
" \"last_accepted_config\" : [ ],\n" +
|
||||
" \"master_node\" : \"node0\"\n" +
|
||||
" },\n" +
|
||||
" \"explanations\" : [\n" +
|
||||
|
|
|
@ -97,7 +97,7 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
|||
if (i > 0) {
|
||||
clusterState = builder.build();
|
||||
}
|
||||
switch (randomInt(4)) {
|
||||
switch (randomInt(5)) {
|
||||
case 0:
|
||||
builder = randomNodes(clusterState);
|
||||
break;
|
||||
|
@ -113,11 +113,14 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
|||
case 4:
|
||||
builder = randomMetaDataChanges(clusterState);
|
||||
break;
|
||||
case 5:
|
||||
builder = randomVotingConfiguration(clusterState);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Shouldn't be here");
|
||||
}
|
||||
}
|
||||
clusterState = builder.incrementVersion().build();
|
||||
clusterState = builder.incrementVersion().term(randomLong()).build();
|
||||
|
||||
if (randomIntBetween(0, 10) < 1) {
|
||||
// Update cluster state via full serialization from time to time
|
||||
|
@ -141,7 +144,10 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
|||
try {
|
||||
// Check non-diffable elements
|
||||
assertThat(clusterStateFromDiffs.version(), equalTo(clusterState.version()));
|
||||
assertThat(clusterStateFromDiffs.term(), equalTo(clusterState.term()));
|
||||
assertThat(clusterStateFromDiffs.stateUUID(), equalTo(clusterState.stateUUID()));
|
||||
assertThat(clusterStateFromDiffs.getLastAcceptedConfiguration(), equalTo(clusterState.getLastAcceptedConfiguration()));
|
||||
assertThat(clusterStateFromDiffs.getLastCommittedConfiguration(), equalTo(clusterState.getLastCommittedConfiguration()));
|
||||
|
||||
// Check nodes
|
||||
assertThat(clusterStateFromDiffs.nodes().getNodes(), equalTo(clusterState.nodes().getNodes()));
|
||||
|
@ -190,6 +196,20 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
|||
|
||||
}
|
||||
|
||||
private ClusterState.Builder randomVotingConfiguration(ClusterState clusterState) {
|
||||
ClusterState.Builder builder = ClusterState.builder(clusterState);
|
||||
if (randomBoolean()) {
|
||||
builder.lastCommittedConfiguration(
|
||||
new ClusterState.VotingConfiguration(Sets.newHashSet(generateRandomStringArray(10, 10, false))));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
builder.lastAcceptedConfiguration(
|
||||
new ClusterState.VotingConfiguration(Sets.newHashSet(generateRandomStringArray(10, 10, false))));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly updates nodes in the cluster state
|
||||
*/
|
||||
|
|
|
@ -19,10 +19,18 @@
|
|||
package org.elasticsearch.cluster;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.ClusterState.VotingConfiguration;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.EqualsHashCodeTestUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.emptySet;
|
||||
|
@ -55,6 +63,70 @@ public class ClusterStateTests extends ESTestCase {
|
|||
|
||||
// state from the same master compare by version
|
||||
assertThat(withMaster1a.supersedes(withMaster1b), equalTo(withMaster1a.version() > withMaster1b.version()));
|
||||
}
|
||||
|
||||
public void testVotingConfiguration() {
|
||||
VotingConfiguration config0 = new VotingConfiguration(Sets.newHashSet());
|
||||
assertThat(config0, equalTo(VotingConfiguration.EMPTY_CONFIG));
|
||||
assertThat(config0.getNodeIds(), equalTo(Sets.newHashSet()));
|
||||
assertThat(config0.isEmpty(), equalTo(true));
|
||||
assertThat(config0.hasQuorum(Sets.newHashSet()), equalTo(false));
|
||||
assertThat(config0.hasQuorum(Sets.newHashSet("id1")), equalTo(false));
|
||||
|
||||
VotingConfiguration config1 = new VotingConfiguration(Sets.newHashSet("id1"));
|
||||
assertThat(config1.getNodeIds(), equalTo(Sets.newHashSet("id1")));
|
||||
assertThat(config1.isEmpty(), equalTo(false));
|
||||
assertThat(config1.hasQuorum(Sets.newHashSet("id1")), equalTo(true));
|
||||
assertThat(config1.hasQuorum(Sets.newHashSet("id1", "id2")), equalTo(true));
|
||||
assertThat(config1.hasQuorum(Sets.newHashSet("id2")), equalTo(false));
|
||||
assertThat(config1.hasQuorum(Sets.newHashSet()), equalTo(false));
|
||||
|
||||
VotingConfiguration config2 = new VotingConfiguration(Sets.newHashSet("id1", "id2"));
|
||||
assertThat(config2.getNodeIds(), equalTo(Sets.newHashSet("id1", "id2")));
|
||||
assertThat(config2.isEmpty(), equalTo(false));
|
||||
assertThat(config2.hasQuorum(Sets.newHashSet("id1", "id2")), equalTo(true));
|
||||
assertThat(config2.hasQuorum(Sets.newHashSet("id1", "id2", "id3")), equalTo(true));
|
||||
assertThat(config2.hasQuorum(Sets.newHashSet("id1")), equalTo(false));
|
||||
assertThat(config2.hasQuorum(Sets.newHashSet("id2")), equalTo(false));
|
||||
assertThat(config2.hasQuorum(Sets.newHashSet("id3")), equalTo(false));
|
||||
assertThat(config2.hasQuorum(Sets.newHashSet("id1", "id3")), equalTo(false));
|
||||
assertThat(config2.hasQuorum(Sets.newHashSet()), equalTo(false));
|
||||
|
||||
VotingConfiguration config3 = new VotingConfiguration(Sets.newHashSet("id1", "id2", "id3"));
|
||||
assertThat(config3.getNodeIds(), equalTo(Sets.newHashSet("id1", "id2", "id3")));
|
||||
assertThat(config3.isEmpty(), equalTo(false));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id1", "id2")), equalTo(true));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id2", "id3")), equalTo(true));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id1", "id3")), equalTo(true));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id1", "id2", "id3")), equalTo(true));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id1", "id2", "id4")), equalTo(true));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id1")), equalTo(false));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id2")), equalTo(false));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id3")), equalTo(false));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id1", "id4")), equalTo(false));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet("id1", "id4", "id5")), equalTo(false));
|
||||
assertThat(config3.hasQuorum(Sets.newHashSet()), equalTo(false));
|
||||
}
|
||||
|
||||
public void testVotingConfigurationSerializationEqualsHashCode() {
|
||||
VotingConfiguration initialConfig = new VotingConfiguration(
|
||||
Sets.newHashSet(generateRandomStringArray(randomInt(10), 20, false)));
|
||||
EqualsHashCodeTestUtils.checkEqualsAndHashCode(initialConfig,
|
||||
orig -> ESTestCase.copyWriteable(orig, new NamedWriteableRegistry(Collections.emptyList()), VotingConfiguration::new),
|
||||
cfg -> {
|
||||
Set<String> newNodeIds = new HashSet<>(cfg.getNodeIds());
|
||||
if (cfg.isEmpty() == false && randomBoolean()) {
|
||||
// remove random element
|
||||
newNodeIds.remove(randomFrom(cfg.getNodeIds()));
|
||||
} else if (cfg.isEmpty() == false && randomBoolean()) {
|
||||
// change random element
|
||||
newNodeIds.remove(randomFrom(cfg.getNodeIds()));
|
||||
newNodeIds.add(randomAlphaOfLength(20));
|
||||
} else {
|
||||
// add random element
|
||||
newNodeIds.add(randomAlphaOfLength(20));
|
||||
}
|
||||
return new VotingConfiguration(newNodeIds);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -487,7 +487,8 @@ public class PublishClusterStateActionTests extends ESTestCase {
|
|||
clusterState = ClusterState.builder(clusterState).blocks(ClusterBlocks.builder()
|
||||
.addGlobalBlock(MetaData.CLUSTER_READ_ONLY_BLOCK)).incrementVersion().build();
|
||||
|
||||
ClusterState unserializableClusterState = new ClusterState(clusterState.version(), clusterState.stateUUID(), clusterState) {
|
||||
ClusterState unserializableClusterState = new ClusterState(clusterState.term(), clusterState.version(), clusterState.stateUUID(),
|
||||
clusterState) {
|
||||
@Override
|
||||
public Diff<ClusterState> diff(ClusterState previousState) {
|
||||
return new Diff<ClusterState>() {
|
||||
|
|
|
@ -504,8 +504,11 @@ public class ClusterStatsMonitoringDocTests extends BaseMonitoringDocTestCase<Cl
|
|||
+ "\"nodes_hash\":1314980060,"
|
||||
+ "\"status\":\"green\","
|
||||
+ "\"cluster_uuid\":\"_cluster\","
|
||||
+ "\"term\":0,"
|
||||
+ "\"version\":12,"
|
||||
+ "\"state_uuid\":\"_state_uuid\","
|
||||
+ "\"last_committed_config\":[],"
|
||||
+ "\"last_accepted_config\":[],"
|
||||
+ "\"master_node\":\"_node\","
|
||||
+ "\"nodes\":{"
|
||||
+ "\"_node_id\":{"
|
||||
|
|
Loading…
Reference in New Issue