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
|
* the enabled state of every bwc task. It should be set back to true
|
||||||
* after the backport of the backcompat code is complete.
|
* after the backport of the backcompat code is complete.
|
||||||
*/
|
*/
|
||||||
final boolean bwc_tests_enabled = true
|
final boolean bwc_tests_enabled = false
|
||||||
final String bwc_tests_disabled_issue = "" /* place a PR link here when committing bwc changes */
|
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_enabled == false) {
|
||||||
if (bwc_tests_disabled_issue.isEmpty()) {
|
if (bwc_tests_disabled_issue.isEmpty()) {
|
||||||
throw new GradleException("bwc_tests_disabled_issue must be set when bwc_tests_enabled == false")
|
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();
|
ClusterState currentState = clusterService.state();
|
||||||
logger.trace("Serving cluster state request using version {}", currentState.version());
|
logger.trace("Serving cluster state request using version {}", currentState.version());
|
||||||
ClusterState.Builder builder = ClusterState.builder(currentState.getClusterName());
|
ClusterState.Builder builder = ClusterState.builder(currentState.getClusterName());
|
||||||
|
builder.term(currentState.term());
|
||||||
builder.version(currentState.version());
|
builder.version(currentState.version());
|
||||||
builder.stateUUID(currentState.stateUUID());
|
builder.stateUUID(currentState.stateUUID());
|
||||||
|
builder.lastCommittedConfiguration(currentState.getLastCommittedConfiguration());
|
||||||
|
builder.lastAcceptedConfiguration(currentState.getLastAcceptedConfiguration());
|
||||||
if (request.nodes()) {
|
if (request.nodes()) {
|
||||||
builder.nodes(currentState.nodes());
|
builder.nodes(currentState.nodes());
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ package org.elasticsearch.cluster;
|
||||||
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.client.transport.TransportClient;
|
import org.elasticsearch.client.transport.TransportClient;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlock;
|
import org.elasticsearch.cluster.block.ClusterBlock;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlocks;
|
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.NamedWriteableRegistry;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
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.io.stream.VersionedNamedWriteable;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||||
|
@ -58,10 +62,14 @@ import org.elasticsearch.discovery.Discovery;
|
||||||
import org.elasticsearch.discovery.zen.PublishClusterStateAction;
|
import org.elasticsearch.discovery.zen.PublishClusterStateAction;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -154,6 +162,8 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
|
|
||||||
public static final long UNKNOWN_VERSION = -1;
|
public static final long UNKNOWN_VERSION = -1;
|
||||||
|
|
||||||
|
private final long term;
|
||||||
|
|
||||||
private final long version;
|
private final long version;
|
||||||
|
|
||||||
private final String stateUUID;
|
private final String stateUUID;
|
||||||
|
@ -172,16 +182,23 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
|
|
||||||
private final boolean wasReadFromDiff;
|
private final boolean wasReadFromDiff;
|
||||||
|
|
||||||
|
private final VotingConfiguration lastCommittedConfiguration;
|
||||||
|
|
||||||
|
private final VotingConfiguration lastAcceptedConfiguration;
|
||||||
|
|
||||||
// built on demand
|
// built on demand
|
||||||
private volatile RoutingNodes routingNodes;
|
private volatile RoutingNodes routingNodes;
|
||||||
|
|
||||||
public ClusterState(long version, String stateUUID, ClusterState state) {
|
public ClusterState(long term, long version, String stateUUID, ClusterState state) {
|
||||||
this(state.clusterName, version, stateUUID, state.metaData(), state.routingTable(), state.nodes(), state.blocks(), state.customs(),
|
this(state.clusterName, term, version, stateUUID, state.metaData(), state.routingTable(), state.nodes(), state.blocks(),
|
||||||
false);
|
state.customs(), state.getLastCommittedConfiguration(), state.getLastAcceptedConfiguration(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterState(ClusterName clusterName, long version, String stateUUID, MetaData metaData, RoutingTable routingTable,
|
public ClusterState(ClusterName clusterName, long term, long version, String stateUUID, MetaData metaData, RoutingTable routingTable,
|
||||||
DiscoveryNodes nodes, ClusterBlocks blocks, ImmutableOpenMap<String, Custom> customs, boolean wasReadFromDiff) {
|
DiscoveryNodes nodes, ClusterBlocks blocks, ImmutableOpenMap<String, Custom> customs,
|
||||||
|
VotingConfiguration lastCommittedConfiguration, VotingConfiguration lastAcceptedConfiguration,
|
||||||
|
boolean wasReadFromDiff) {
|
||||||
|
this.term = term;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.stateUUID = stateUUID;
|
this.stateUUID = stateUUID;
|
||||||
this.clusterName = clusterName;
|
this.clusterName = clusterName;
|
||||||
|
@ -190,9 +207,15 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
this.blocks = blocks;
|
this.blocks = blocks;
|
||||||
this.customs = customs;
|
this.customs = customs;
|
||||||
|
this.lastCommittedConfiguration = lastCommittedConfiguration;
|
||||||
|
this.lastAcceptedConfiguration = lastAcceptedConfiguration;
|
||||||
this.wasReadFromDiff = wasReadFromDiff;
|
this.wasReadFromDiff = wasReadFromDiff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long term() {
|
||||||
|
return term;
|
||||||
|
}
|
||||||
|
|
||||||
public long version() {
|
public long version() {
|
||||||
return this.version;
|
return this.version;
|
||||||
}
|
}
|
||||||
|
@ -257,6 +280,14 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
return this.clusterName;
|
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
|
// Used for testing and logging to determine how this cluster state was send over the wire
|
||||||
public boolean wasReadFromDiff() {
|
public boolean wasReadFromDiff() {
|
||||||
return wasReadFromDiff;
|
return wasReadFromDiff;
|
||||||
|
@ -277,8 +308,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("cluster uuid: ").append(metaData.clusterUUID()).append("\n");
|
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("version: ").append(version).append("\n");
|
||||||
sb.append("state uuid: ").append(stateUUID).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("from_diff: ").append(wasReadFromDiff).append("\n");
|
||||||
sb.append("meta data version: ").append(metaData.version()).append("\n");
|
sb.append("meta data version: ").append(metaData.version()).append("\n");
|
||||||
final String TAB = " ";
|
final String TAB = " ";
|
||||||
|
@ -386,8 +420,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
builder.field("cluster_uuid", metaData().clusterUUID());
|
builder.field("cluster_uuid", metaData().clusterUUID());
|
||||||
|
|
||||||
if (metrics.contains(Metric.VERSION)) {
|
if (metrics.contains(Metric.VERSION)) {
|
||||||
|
builder.field("term", term);
|
||||||
builder.field("version", version);
|
builder.field("version", version);
|
||||||
builder.field("state_uuid", stateUUID);
|
builder.field("state_uuid", stateUUID);
|
||||||
|
builder.field("last_committed_config", lastCommittedConfiguration);
|
||||||
|
builder.field("last_accepted_config", lastAcceptedConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metrics.contains(Metric.MASTER_NODE)) {
|
if (metrics.contains(Metric.MASTER_NODE)) {
|
||||||
|
@ -587,8 +624,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private final ClusterName clusterName;
|
private final ClusterName clusterName;
|
||||||
|
private long term = 0;
|
||||||
private long version = 0;
|
private long version = 0;
|
||||||
private String uuid = UNKNOWN_UUID;
|
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 MetaData metaData = MetaData.EMPTY_META_DATA;
|
||||||
private RoutingTable routingTable = RoutingTable.EMPTY_ROUTING_TABLE;
|
private RoutingTable routingTable = RoutingTable.EMPTY_ROUTING_TABLE;
|
||||||
private DiscoveryNodes nodes = DiscoveryNodes.EMPTY_NODES;
|
private DiscoveryNodes nodes = DiscoveryNodes.EMPTY_NODES;
|
||||||
|
@ -599,8 +639,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
|
|
||||||
public Builder(ClusterState state) {
|
public Builder(ClusterState state) {
|
||||||
this.clusterName = state.clusterName;
|
this.clusterName = state.clusterName;
|
||||||
|
this.term = state.term();
|
||||||
this.version = state.version();
|
this.version = state.version();
|
||||||
this.uuid = state.stateUUID();
|
this.uuid = state.stateUUID();
|
||||||
|
this.lastCommittedConfiguration = state.getLastCommittedConfiguration();
|
||||||
|
this.lastAcceptedConfiguration = state.getLastAcceptedConfiguration();
|
||||||
this.nodes = state.nodes();
|
this.nodes = state.nodes();
|
||||||
this.routingTable = state.routingTable();
|
this.routingTable = state.routingTable();
|
||||||
this.metaData = state.metaData();
|
this.metaData = state.metaData();
|
||||||
|
@ -650,6 +693,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder term(long term) {
|
||||||
|
this.term = term;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder version(long version) {
|
public Builder version(long version) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
return this;
|
return this;
|
||||||
|
@ -666,6 +714,16 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
return this;
|
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) {
|
public Builder putCustom(String type, Custom custom) {
|
||||||
customs.put(type, custom);
|
customs.put(type, custom);
|
||||||
return this;
|
return this;
|
||||||
|
@ -690,7 +748,8 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
if (UNKNOWN_UUID.equals(uuid)) {
|
if (UNKNOWN_UUID.equals(uuid)) {
|
||||||
uuid = UUIDs.randomBase64UUID();
|
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 {
|
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 {
|
public static ClusterState readFrom(StreamInput in, DiscoveryNode localNode) throws IOException {
|
||||||
ClusterName clusterName = new ClusterName(in);
|
ClusterName clusterName = new ClusterName(in);
|
||||||
Builder builder = new Builder(clusterName);
|
Builder builder = new Builder(clusterName);
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
builder.term = in.readLong();
|
||||||
|
}
|
||||||
builder.version = in.readLong();
|
builder.version = in.readLong();
|
||||||
builder.uuid = in.readString();
|
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.metaData = MetaData.readFrom(in);
|
||||||
builder.routingTable = RoutingTable.readFrom(in);
|
builder.routingTable = RoutingTable.readFrom(in);
|
||||||
builder.nodes = DiscoveryNodes.readFrom(in, localNode);
|
builder.nodes = DiscoveryNodes.readFrom(in, localNode);
|
||||||
|
@ -739,8 +805,15 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
clusterName.writeTo(out);
|
clusterName.writeTo(out);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
out.writeLong(term);
|
||||||
|
}
|
||||||
out.writeLong(version);
|
out.writeLong(version);
|
||||||
out.writeString(stateUUID);
|
out.writeString(stateUUID);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
lastCommittedConfiguration.writeTo(out);
|
||||||
|
lastAcceptedConfiguration.writeTo(out);
|
||||||
|
}
|
||||||
metaData.writeTo(out);
|
metaData.writeTo(out);
|
||||||
routingTable.writeTo(out);
|
routingTable.writeTo(out);
|
||||||
nodes.writeTo(out);
|
nodes.writeTo(out);
|
||||||
|
@ -762,6 +835,8 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
|
|
||||||
private static class ClusterStateDiff implements Diff<ClusterState> {
|
private static class ClusterStateDiff implements Diff<ClusterState> {
|
||||||
|
|
||||||
|
private final long toTerm;
|
||||||
|
|
||||||
private final long toVersion;
|
private final long toVersion;
|
||||||
|
|
||||||
private final String fromUuid;
|
private final String fromUuid;
|
||||||
|
@ -770,6 +845,10 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
|
|
||||||
private final ClusterName clusterName;
|
private final ClusterName clusterName;
|
||||||
|
|
||||||
|
private final VotingConfiguration lastCommittedConfiguration;
|
||||||
|
|
||||||
|
private final VotingConfiguration lastAcceptedConfiguration;
|
||||||
|
|
||||||
private final Diff<RoutingTable> routingTable;
|
private final Diff<RoutingTable> routingTable;
|
||||||
|
|
||||||
private final Diff<DiscoveryNodes> nodes;
|
private final Diff<DiscoveryNodes> nodes;
|
||||||
|
@ -783,8 +862,11 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
ClusterStateDiff(ClusterState before, ClusterState after) {
|
ClusterStateDiff(ClusterState before, ClusterState after) {
|
||||||
fromUuid = before.stateUUID;
|
fromUuid = before.stateUUID;
|
||||||
toUuid = after.stateUUID;
|
toUuid = after.stateUUID;
|
||||||
|
toTerm = after.term;
|
||||||
toVersion = after.version;
|
toVersion = after.version;
|
||||||
clusterName = after.clusterName;
|
clusterName = after.clusterName;
|
||||||
|
lastCommittedConfiguration = after.lastCommittedConfiguration;
|
||||||
|
lastAcceptedConfiguration = after.lastAcceptedConfiguration;
|
||||||
routingTable = after.routingTable.diff(before.routingTable);
|
routingTable = after.routingTable.diff(before.routingTable);
|
||||||
nodes = after.nodes.diff(before.nodes);
|
nodes = after.nodes.diff(before.nodes);
|
||||||
metaData = after.metaData.diff(before.metaData);
|
metaData = after.metaData.diff(before.metaData);
|
||||||
|
@ -796,7 +878,19 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
clusterName = new ClusterName(in);
|
clusterName = new ClusterName(in);
|
||||||
fromUuid = in.readString();
|
fromUuid = in.readString();
|
||||||
toUuid = in.readString();
|
toUuid = in.readString();
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
toTerm = in.readLong();
|
||||||
|
} else {
|
||||||
|
toTerm = 0L;
|
||||||
|
}
|
||||||
toVersion = in.readLong();
|
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);
|
routingTable = RoutingTable.readDiffFrom(in);
|
||||||
nodes = DiscoveryNodes.readDiffFrom(in, localNode);
|
nodes = DiscoveryNodes.readDiffFrom(in, localNode);
|
||||||
metaData = MetaData.readDiffFrom(in);
|
metaData = MetaData.readDiffFrom(in);
|
||||||
|
@ -809,7 +903,14 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
|
||||||
clusterName.writeTo(out);
|
clusterName.writeTo(out);
|
||||||
out.writeString(fromUuid);
|
out.writeString(fromUuid);
|
||||||
out.writeString(toUuid);
|
out.writeString(toUuid);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
out.writeLong(toTerm);
|
||||||
|
}
|
||||||
out.writeLong(toVersion);
|
out.writeLong(toVersion);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
lastCommittedConfiguration.writeTo(out);
|
||||||
|
lastAcceptedConfiguration.writeTo(out);
|
||||||
|
}
|
||||||
routingTable.writeTo(out);
|
routingTable.writeTo(out);
|
||||||
nodes.writeTo(out);
|
nodes.writeTo(out);
|
||||||
metaData.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);
|
throw new IncompatibleClusterStateVersionException(state.version, state.stateUUID, toVersion, fromUuid);
|
||||||
}
|
}
|
||||||
builder.stateUUID(toUuid);
|
builder.stateUUID(toUuid);
|
||||||
|
builder.term(toTerm);
|
||||||
builder.version(toVersion);
|
builder.version(toVersion);
|
||||||
|
builder.lastCommittedConfiguration(lastCommittedConfiguration);
|
||||||
|
builder.lastAcceptedConfiguration(lastAcceptedConfiguration);
|
||||||
builder.routingTable(routingTable.apply(state.routingTable));
|
builder.routingTable(routingTable.apply(state.routingTable));
|
||||||
builder.nodes(nodes.apply(state.nodes));
|
builder.nodes(nodes.apply(state.nodes));
|
||||||
builder.metaData(metaData.apply(state.metaData));
|
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" +
|
" \"acknowledged\" : true,\n" +
|
||||||
" \"state\" : {\n" +
|
" \"state\" : {\n" +
|
||||||
" \"cluster_uuid\" : \"_na_\",\n" +
|
" \"cluster_uuid\" : \"_na_\",\n" +
|
||||||
|
" \"term\" : 0,\n" +
|
||||||
" \"version\" : 0,\n" +
|
" \"version\" : 0,\n" +
|
||||||
" \"state_uuid\" : \"" + clusterState.stateUUID() + "\",\n" +
|
" \"state_uuid\" : \"" + clusterState.stateUUID() + "\",\n" +
|
||||||
|
" \"last_committed_config\" : [ ],\n" +
|
||||||
|
" \"last_accepted_config\" : [ ],\n" +
|
||||||
" \"master_node\" : \"node0\",\n" +
|
" \"master_node\" : \"node0\",\n" +
|
||||||
" \"blocks\" : { },\n" +
|
" \"blocks\" : { },\n" +
|
||||||
" \"nodes\" : {\n" +
|
" \"nodes\" : {\n" +
|
||||||
|
@ -138,8 +141,11 @@ public class ClusterRerouteResponseTests extends ESTestCase {
|
||||||
" \"acknowledged\" : true,\n" +
|
" \"acknowledged\" : true,\n" +
|
||||||
" \"state\" : {\n" +
|
" \"state\" : {\n" +
|
||||||
" \"cluster_uuid\" : \"_na_\",\n" +
|
" \"cluster_uuid\" : \"_na_\",\n" +
|
||||||
|
" \"term\" : 0,\n" +
|
||||||
" \"version\" : 0,\n" +
|
" \"version\" : 0,\n" +
|
||||||
" \"state_uuid\" : \"" + clusterState.stateUUID() + "\",\n" +
|
" \"state_uuid\" : \"" + clusterState.stateUUID() + "\",\n" +
|
||||||
|
" \"last_committed_config\" : [ ],\n" +
|
||||||
|
" \"last_accepted_config\" : [ ],\n" +
|
||||||
" \"master_node\" : \"node0\"\n" +
|
" \"master_node\" : \"node0\"\n" +
|
||||||
" },\n" +
|
" },\n" +
|
||||||
" \"explanations\" : [\n" +
|
" \"explanations\" : [\n" +
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
clusterState = builder.build();
|
clusterState = builder.build();
|
||||||
}
|
}
|
||||||
switch (randomInt(4)) {
|
switch (randomInt(5)) {
|
||||||
case 0:
|
case 0:
|
||||||
builder = randomNodes(clusterState);
|
builder = randomNodes(clusterState);
|
||||||
break;
|
break;
|
||||||
|
@ -113,11 +113,14 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
||||||
case 4:
|
case 4:
|
||||||
builder = randomMetaDataChanges(clusterState);
|
builder = randomMetaDataChanges(clusterState);
|
||||||
break;
|
break;
|
||||||
|
case 5:
|
||||||
|
builder = randomVotingConfiguration(clusterState);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Shouldn't be here");
|
throw new IllegalArgumentException("Shouldn't be here");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clusterState = builder.incrementVersion().build();
|
clusterState = builder.incrementVersion().term(randomLong()).build();
|
||||||
|
|
||||||
if (randomIntBetween(0, 10) < 1) {
|
if (randomIntBetween(0, 10) < 1) {
|
||||||
// Update cluster state via full serialization from time to time
|
// Update cluster state via full serialization from time to time
|
||||||
|
@ -141,7 +144,10 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
|
||||||
try {
|
try {
|
||||||
// Check non-diffable elements
|
// Check non-diffable elements
|
||||||
assertThat(clusterStateFromDiffs.version(), equalTo(clusterState.version()));
|
assertThat(clusterStateFromDiffs.version(), equalTo(clusterState.version()));
|
||||||
|
assertThat(clusterStateFromDiffs.term(), equalTo(clusterState.term()));
|
||||||
assertThat(clusterStateFromDiffs.stateUUID(), equalTo(clusterState.stateUUID()));
|
assertThat(clusterStateFromDiffs.stateUUID(), equalTo(clusterState.stateUUID()));
|
||||||
|
assertThat(clusterStateFromDiffs.getLastAcceptedConfiguration(), equalTo(clusterState.getLastAcceptedConfiguration()));
|
||||||
|
assertThat(clusterStateFromDiffs.getLastCommittedConfiguration(), equalTo(clusterState.getLastCommittedConfiguration()));
|
||||||
|
|
||||||
// Check nodes
|
// Check nodes
|
||||||
assertThat(clusterStateFromDiffs.nodes().getNodes(), equalTo(clusterState.nodes().getNodes()));
|
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
|
* Randomly updates nodes in the cluster state
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -19,10 +19,18 @@
|
||||||
package org.elasticsearch.cluster;
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.cluster.ClusterState.VotingConfiguration;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
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.emptyMap;
|
||||||
import static java.util.Collections.emptySet;
|
import static java.util.Collections.emptySet;
|
||||||
|
@ -55,6 +63,70 @@ public class ClusterStateTests extends ESTestCase {
|
||||||
|
|
||||||
// state from the same master compare by version
|
// state from the same master compare by version
|
||||||
assertThat(withMaster1a.supersedes(withMaster1b), equalTo(withMaster1a.version() > withMaster1b.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()
|
clusterState = ClusterState.builder(clusterState).blocks(ClusterBlocks.builder()
|
||||||
.addGlobalBlock(MetaData.CLUSTER_READ_ONLY_BLOCK)).incrementVersion().build();
|
.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
|
@Override
|
||||||
public Diff<ClusterState> diff(ClusterState previousState) {
|
public Diff<ClusterState> diff(ClusterState previousState) {
|
||||||
return new Diff<ClusterState>() {
|
return new Diff<ClusterState>() {
|
||||||
|
|
|
@ -504,8 +504,11 @@ public class ClusterStatsMonitoringDocTests extends BaseMonitoringDocTestCase<Cl
|
||||||
+ "\"nodes_hash\":1314980060,"
|
+ "\"nodes_hash\":1314980060,"
|
||||||
+ "\"status\":\"green\","
|
+ "\"status\":\"green\","
|
||||||
+ "\"cluster_uuid\":\"_cluster\","
|
+ "\"cluster_uuid\":\"_cluster\","
|
||||||
|
+ "\"term\":0,"
|
||||||
+ "\"version\":12,"
|
+ "\"version\":12,"
|
||||||
+ "\"state_uuid\":\"_state_uuid\","
|
+ "\"state_uuid\":\"_state_uuid\","
|
||||||
|
+ "\"last_committed_config\":[],"
|
||||||
|
+ "\"last_accepted_config\":[],"
|
||||||
+ "\"master_node\":\"_node\","
|
+ "\"master_node\":\"_node\","
|
||||||
+ "\"nodes\":{"
|
+ "\"nodes\":{"
|
||||||
+ "\"_node_id\":{"
|
+ "\"_node_id\":{"
|
||||||
|
|
Loading…
Reference in New Issue