From 54f955aa0e1f0a8c3d2bcd8cec87c3119601c3ae Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Fri, 5 Mar 2021 16:52:12 -0700 Subject: [PATCH 01/12] First draft of node shutdown metadata This is just a basic class to iterate on, plus tests. --- .../cluster/NodeShutdownMetadata.java | 264 ++++++++++++++++++ .../cluster/NodeShutdownMetadataTests.java | 64 +++++ 2 files changed, 328 insertions(+) create mode 100644 server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java create mode 100644 server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java diff --git a/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java new file mode 100644 index 0000000000000..b77e848bb4769 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java @@ -0,0 +1,264 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class NodeShutdownMetadata implements Metadata.Custom { + public static final String TYPE = "node_shutdown"; + public static final Version NODE_SHUTDOWN_VERSION = Version.V_8_0_0; + + private static final ParseField NODES_FIELD = new ParseField("nodes"); + + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(TYPE, a -> { + final Map nodes = ((List) a[0]).stream() + .collect(Collectors.toMap(NodeShutdownInfo::getNodeId, Function.identity())); + return new NodeShutdownMetadata(nodes); + }); + + static { + PARSER.declareNamedObjects( + ConstructingObjectParser.constructorArg(), + (p, c, n) -> NodeShutdownInfo.parse(p), + v -> { throw new IllegalArgumentException("ordered " + NODES_FIELD.getPreferredName() + " are not supported"); }, + NODES_FIELD + ); + } + + public static NodeShutdownMetadata parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + private final Map nodes; + + public NodeShutdownMetadata(Map nodes) { + this.nodes = nodes; + } + + public NodeShutdownMetadata(StreamInput in) throws IOException { + this.nodes = in.readMap(StreamInput::readString, NodeShutdownInfo::new); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeMap(nodes, StreamOutput::writeString, (outStream, v) -> v.writeTo(outStream)); + } + + public Map getNodes() { + return nodes; + } + + @Override + public Diff diff(Metadata.Custom previousState) { + return new NodeShutdownMetadataDiff((NodeShutdownMetadata) previousState, this); + } + + @Override + public EnumSet context() { + return Metadata.ALL_CONTEXTS; + } + + @Override + public String getWriteableName() { + return TYPE; + } + + @Override + public Version getMinimalSupportedVersion() { + return NODE_SHUTDOWN_VERSION; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if ((o instanceof NodeShutdownMetadata) == false) return false; + NodeShutdownMetadata that = (NodeShutdownMetadata) o; + return getNodes().equals(that.getNodes()); + } + + @Override + public int hashCode() { + return Objects.hash(getNodes()); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.field(NODES_FIELD.getPreferredName(), nodes); + return builder; + } + + public static class NodeShutdownMetadataDiff implements NamedDiff { + + private final Diff> nodesDiff; + + NodeShutdownMetadataDiff(NodeShutdownMetadata before, NodeShutdownMetadata after) { + this.nodesDiff = DiffableUtils.diff(before.nodes, after.nodes, DiffableUtils.getStringKeySerializer()); + } + + public NodeShutdownMetadataDiff(StreamInput in) throws IOException { + this.nodesDiff = DiffableUtils.readJdkMapDiff( + in, + DiffableUtils.getStringKeySerializer(), + NodeShutdownInfo::new, + NodeShutdownMetadataDiff::readNodesDiffFrom + ); + } + + @Override + public Metadata.Custom apply(Metadata.Custom part) { + TreeMap newNodes = new TreeMap<>(nodesDiff.apply(((NodeShutdownMetadata) part).getNodes())); + return new NodeShutdownMetadata(newNodes); + } + + @Override + public String getWriteableName() { + return TYPE; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + nodesDiff.writeTo(out); + } + + static Diff readNodesDiffFrom(StreamInput in) throws IOException { + return AbstractDiffable.readDiffFrom(NodeShutdownInfo::new, in); + } + } + + public static class NodeShutdownInfo extends AbstractDiffable + implements + ToXContentObject, + Diffable { + + private static final ParseField NODE_ID_FIELD = new ParseField("node_id"); + private static final ParseField TYPE_FIELD = new ParseField("type"); + private static final ParseField REASON_FIELD = new ParseField("reason"); + private static final ParseField STATUS_FIELD = new ParseField("status"); + private static final ParseField STARTED_AT_FIELD = new ParseField("shutdown_started"); + private static final ParseField STARTED_AT_MILLIS_FIELD = new ParseField("shutdown_started_millis"); + + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "node_shutdown_info", + a -> new NodeShutdownInfo((String) a[0], (String) a[1], (String) a[2], (boolean) a[3], (long) a[4]) + ); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), NODE_ID_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), TYPE_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), REASON_FIELD); + PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), STATUS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), STARTED_AT_MILLIS_FIELD); + } + + public static NodeShutdownInfo parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + private final String nodeId; + private final String type; + private final String reason; + private final boolean status; // GWB> Replace with an actual status object + private final long startedAtDate; + + public NodeShutdownInfo(String nodeId, String type, String reason, boolean status, long startedAtDate) { + this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); + this.type = Objects.requireNonNull(type, "shutdown type must not be null"); + this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); + this.status = status; + this.startedAtDate = startedAtDate; + } + + public NodeShutdownInfo(StreamInput in) throws IOException { + this.nodeId = in.readString(); + this.type = in.readString(); + this.reason = in.readString(); + this.status = in.readBoolean(); + this.startedAtDate = in.readVLong(); + } + + public String getNodeId() { + return nodeId; + } + + public String getType() { + return type; + } + + public String getReason() { + return reason; + } + + public boolean isStatus() { + return status; + } + + public long getStartedAtDate() { + return startedAtDate; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(nodeId); + out.writeString(type); + out.writeString(reason); + out.writeBoolean(status); + out.writeVLong(startedAtDate); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + { + builder.field(NODE_ID_FIELD.getPreferredName(), nodeId); + builder.field(TYPE_FIELD.getPreferredName(), type); + builder.field(REASON_FIELD.getPreferredName(), reason); + builder.field(STATUS_FIELD.getPreferredName(), status); + builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_FIELD.getPreferredName(), startedAtDate); + } + builder.endObject(); + + return builder; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if ((o instanceof NodeShutdownInfo) == false) return false; + NodeShutdownInfo that = (NodeShutdownInfo) o; + return isStatus() == that.isStatus() + && getStartedAtDate() == that.getStartedAtDate() + && getNodeId().equals(that.getNodeId()) + && getType().equals(that.getType()) + && getReason().equals(that.getReason()); + } + + @Override + public int hashCode() { + return Objects.hash(getNodeId(), getType(), getReason(), isStatus(), getStartedAtDate()); + } + } +} diff --git a/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java new file mode 100644 index 0000000000000..534ecf1f20703 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster; + +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractDiffableSerializationTestCase; + +import java.io.IOException; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class NodeShutdownMetadataTests extends AbstractDiffableSerializationTestCase { + + @Override + protected Writeable.Reader> diffReader() { + return NodeShutdownMetadata.NodeShutdownMetadataDiff::new; + } + + @Override + protected NodeShutdownMetadata doParseInstance(XContentParser parser) throws IOException { + return NodeShutdownMetadata.parse(parser); + } + + @Override + protected Writeable.Reader instanceReader() { + return NodeShutdownMetadata::new; + } + + @Override + protected NodeShutdownMetadata createTestInstance() { + Map nodes = randomList(0, 10, this::randomNodeShutdownInfo).stream() + .collect(Collectors.toMap(NodeShutdownMetadata.NodeShutdownInfo::getNodeId, Function.identity())); + return new NodeShutdownMetadata(nodes); + } + + private NodeShutdownMetadata.NodeShutdownInfo randomNodeShutdownInfo() { + return new NodeShutdownMetadata.NodeShutdownInfo( + randomAlphaOfLength(5), + randomAlphaOfLength(5), + randomAlphaOfLength(5), + randomBoolean(), + randomNonNegativeLong() + ); + } + + @Override + protected Metadata.Custom makeTestChanges(Metadata.Custom testInstance) { + return randomValueOtherThan(testInstance, this::createTestInstance); + } + + @Override + protected Metadata.Custom mutateInstance(Metadata.Custom instance) throws IOException { + return makeTestChanges(instance); + } +} From c82e639fdd093b1b28da74b038162b48c23e709e Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Tue, 9 Mar 2021 16:36:59 -0700 Subject: [PATCH 02/12] Javadoc & some renaming --- .../cluster/NodeShutdownMetadata.java | 79 +++++++++++++------ .../cluster/NodeShutdownMetadataTests.java | 8 +- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java index b77e848bb4769..d77854eab17fe 100644 --- a/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java @@ -19,6 +19,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -27,6 +28,11 @@ import java.util.function.Function; import java.util.stream.Collectors; +/** + * Contains the data about nodes which are currently configured to shut down, either permanently or temporarily. + * + * Stored in the cluster state as custom metadata. + */ public class NodeShutdownMetadata implements Metadata.Custom { public static final String TYPE = "node_shutdown"; public static final Version NODE_SHUTDOWN_VERSION = Version.V_8_0_0; @@ -35,15 +41,15 @@ public class NodeShutdownMetadata implements Metadata.Custom { @SuppressWarnings("unchecked") public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(TYPE, a -> { - final Map nodes = ((List) a[0]).stream() - .collect(Collectors.toMap(NodeShutdownInfo::getNodeId, Function.identity())); + final Map nodes = ((List) a[0]).stream() + .collect(Collectors.toMap(SingleNodeShutdownMetadata::getNodeId, Function.identity())); return new NodeShutdownMetadata(nodes); }); static { PARSER.declareNamedObjects( ConstructingObjectParser.constructorArg(), - (p, c, n) -> NodeShutdownInfo.parse(p), + (p, c, n) -> SingleNodeShutdownMetadata.parse(p), v -> { throw new IllegalArgumentException("ordered " + NODES_FIELD.getPreferredName() + " are not supported"); }, NODES_FIELD ); @@ -53,14 +59,14 @@ public static NodeShutdownMetadata parse(XContentParser parser) { return PARSER.apply(parser, null); } - private final Map nodes; + private final Map nodes; - public NodeShutdownMetadata(Map nodes) { + public NodeShutdownMetadata(Map nodes) { this.nodes = nodes; } public NodeShutdownMetadata(StreamInput in) throws IOException { - this.nodes = in.readMap(StreamInput::readString, NodeShutdownInfo::new); + this.nodes = in.readMap(StreamInput::readString, SingleNodeShutdownMetadata::new); } @Override @@ -68,8 +74,12 @@ public void writeTo(StreamOutput out) throws IOException { out.writeMap(nodes, StreamOutput::writeString, (outStream, v) -> v.writeTo(outStream)); } - public Map getNodes() { - return nodes; + /** + * Retrieve the data about nodes which are currently in the process of shutting down. + * @return A map of node IDs to information about the node's shutdown status. + */ + public Map getPerNodeInfo() { + return Collections.unmodifiableMap(nodes); } @Override @@ -97,12 +107,12 @@ public boolean equals(Object o) { if (this == o) return true; if ((o instanceof NodeShutdownMetadata) == false) return false; NodeShutdownMetadata that = (NodeShutdownMetadata) o; - return getNodes().equals(that.getNodes()); + return getPerNodeInfo().equals(that.getPerNodeInfo()); } @Override public int hashCode() { - return Objects.hash(getNodes()); + return Objects.hash(getPerNodeInfo()); } @Override @@ -111,9 +121,12 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Handles diffing and appling diffs for {@link NodeShutdownMetadata} as necessary for the cluster state infrastructure. + */ public static class NodeShutdownMetadataDiff implements NamedDiff { - private final Diff> nodesDiff; + private final Diff> nodesDiff; NodeShutdownMetadataDiff(NodeShutdownMetadata before, NodeShutdownMetadata after) { this.nodesDiff = DiffableUtils.diff(before.nodes, after.nodes, DiffableUtils.getStringKeySerializer()); @@ -123,14 +136,14 @@ public NodeShutdownMetadataDiff(StreamInput in) throws IOException { this.nodesDiff = DiffableUtils.readJdkMapDiff( in, DiffableUtils.getStringKeySerializer(), - NodeShutdownInfo::new, + SingleNodeShutdownMetadata::new, NodeShutdownMetadataDiff::readNodesDiffFrom ); } @Override public Metadata.Custom apply(Metadata.Custom part) { - TreeMap newNodes = new TreeMap<>(nodesDiff.apply(((NodeShutdownMetadata) part).getNodes())); + TreeMap newNodes = new TreeMap<>(nodesDiff.apply(((NodeShutdownMetadata) part).getPerNodeInfo())); return new NodeShutdownMetadata(newNodes); } @@ -144,15 +157,18 @@ public void writeTo(StreamOutput out) throws IOException { nodesDiff.writeTo(out); } - static Diff readNodesDiffFrom(StreamInput in) throws IOException { - return AbstractDiffable.readDiffFrom(NodeShutdownInfo::new, in); + static Diff readNodesDiffFrom(StreamInput in) throws IOException { + return AbstractDiffable.readDiffFrom(SingleNodeShutdownMetadata::new, in); } } - public static class NodeShutdownInfo extends AbstractDiffable + /** + * Contains data about a single node's shutdown readiness. + */ + public static class SingleNodeShutdownMetadata extends AbstractDiffable implements ToXContentObject, - Diffable { + Diffable { private static final ParseField NODE_ID_FIELD = new ParseField("node_id"); private static final ParseField TYPE_FIELD = new ParseField("type"); @@ -161,9 +177,9 @@ public static class NodeShutdownInfo extends AbstractDiffable private static final ParseField STARTED_AT_FIELD = new ParseField("shutdown_started"); private static final ParseField STARTED_AT_MILLIS_FIELD = new ParseField("shutdown_started_millis"); - public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "node_shutdown_info", - a -> new NodeShutdownInfo((String) a[0], (String) a[1], (String) a[2], (boolean) a[3], (long) a[4]) + a -> new SingleNodeShutdownMetadata((String) a[0], (String) a[1], (String) a[2], (boolean) a[3], (long) a[4]) ); static { @@ -174,7 +190,7 @@ public static class NodeShutdownInfo extends AbstractDiffable PARSER.declareLong(ConstructingObjectParser.constructorArg(), STARTED_AT_MILLIS_FIELD); } - public static NodeShutdownInfo parse(XContentParser parser) { + public static SingleNodeShutdownMetadata parse(XContentParser parser) { return PARSER.apply(parser, null); } @@ -184,7 +200,7 @@ public static NodeShutdownInfo parse(XContentParser parser) { private final boolean status; // GWB> Replace with an actual status object private final long startedAtDate; - public NodeShutdownInfo(String nodeId, String type, String reason, boolean status, long startedAtDate) { + public SingleNodeShutdownMetadata(String nodeId, String type, String reason, boolean status, long startedAtDate) { this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); this.type = Objects.requireNonNull(type, "shutdown type must not be null"); this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); @@ -192,7 +208,7 @@ public NodeShutdownInfo(String nodeId, String type, String reason, boolean statu this.startedAtDate = startedAtDate; } - public NodeShutdownInfo(StreamInput in) throws IOException { + public SingleNodeShutdownMetadata(StreamInput in) throws IOException { this.nodeId = in.readString(); this.type = in.readString(); this.reason = in.readString(); @@ -200,22 +216,37 @@ public NodeShutdownInfo(StreamInput in) throws IOException { this.startedAtDate = in.readVLong(); } + /** + * @return The ID of the node this {@link SingleNodeShutdownMetadata} concerns. + */ public String getNodeId() { return nodeId; } + /** + * @return The type of shutdown this is (shutdown vs. permanent). + */ public String getType() { return type; } + /** + * @return The user-supplied reason this node is shutting down. + */ public String getReason() { return reason; } + /** + * @return True if this node is ready to shut down, false otherwise. + */ public boolean isStatus() { return status; } + /** + * @return The timestamp that this shutdown procedure was started. + */ public long getStartedAtDate() { return startedAtDate; } @@ -247,8 +278,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public boolean equals(Object o) { if (this == o) return true; - if ((o instanceof NodeShutdownInfo) == false) return false; - NodeShutdownInfo that = (NodeShutdownInfo) o; + if ((o instanceof SingleNodeShutdownMetadata) == false) return false; + SingleNodeShutdownMetadata that = (SingleNodeShutdownMetadata) o; return isStatus() == that.isStatus() && getStartedAtDate() == that.getStartedAtDate() && getNodeId().equals(that.getNodeId()) diff --git a/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java index 534ecf1f20703..9fde99ec55add 100644 --- a/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java @@ -37,13 +37,13 @@ protected Writeable.Reader instanceReader() { @Override protected NodeShutdownMetadata createTestInstance() { - Map nodes = randomList(0, 10, this::randomNodeShutdownInfo).stream() - .collect(Collectors.toMap(NodeShutdownMetadata.NodeShutdownInfo::getNodeId, Function.identity())); + Map nodes = randomList(0, 10, this::randomNodeShutdownInfo).stream() + .collect(Collectors.toMap(NodeShutdownMetadata.SingleNodeShutdownMetadata::getNodeId, Function.identity())); return new NodeShutdownMetadata(nodes); } - private NodeShutdownMetadata.NodeShutdownInfo randomNodeShutdownInfo() { - return new NodeShutdownMetadata.NodeShutdownInfo( + private NodeShutdownMetadata.SingleNodeShutdownMetadata randomNodeShutdownInfo() { + return new NodeShutdownMetadata.SingleNodeShutdownMetadata( randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5), From e86439bac396b48f2bb797079068c97655196843 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Tue, 9 Mar 2021 16:41:07 -0700 Subject: [PATCH 03/12] Move packages & break out SingleNodeShutdownMetadata into its own class --- .../{ => metadata}/NodeShutdownMetadata.java | 142 +--------------- .../metadata/SingleNodeShutdownMetadata.java | 153 ++++++++++++++++++ .../NodeShutdownMetadataTests.java | 12 +- 3 files changed, 167 insertions(+), 140 deletions(-) rename server/src/main/java/org/elasticsearch/cluster/{ => metadata}/NodeShutdownMetadata.java (50%) create mode 100644 server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java rename server/src/test/java/org/elasticsearch/cluster/{ => metadata}/NodeShutdownMetadataTests.java (78%) diff --git a/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java similarity index 50% rename from server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java rename to server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java index d77854eab17fe..a1caa7e77422c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/NodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java @@ -6,15 +6,17 @@ * Side Public License, v 1. */ -package org.elasticsearch.cluster; +package org.elasticsearch.cluster.metadata; import org.elasticsearch.Version; -import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.cluster.AbstractDiffable; +import org.elasticsearch.cluster.Diff; +import org.elasticsearch.cluster.DiffableUtils; +import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ConstructingObjectParser; -import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; @@ -143,7 +145,9 @@ public NodeShutdownMetadataDiff(StreamInput in) throws IOException { @Override public Metadata.Custom apply(Metadata.Custom part) { - TreeMap newNodes = new TreeMap<>(nodesDiff.apply(((NodeShutdownMetadata) part).getPerNodeInfo())); + TreeMap newNodes = new TreeMap<>( + nodesDiff.apply(((NodeShutdownMetadata) part).getPerNodeInfo()) + ); return new NodeShutdownMetadata(newNodes); } @@ -162,134 +166,4 @@ static Diff readNodesDiffFrom(StreamInput in) throws } } - /** - * Contains data about a single node's shutdown readiness. - */ - public static class SingleNodeShutdownMetadata extends AbstractDiffable - implements - ToXContentObject, - Diffable { - - private static final ParseField NODE_ID_FIELD = new ParseField("node_id"); - private static final ParseField TYPE_FIELD = new ParseField("type"); - private static final ParseField REASON_FIELD = new ParseField("reason"); - private static final ParseField STATUS_FIELD = new ParseField("status"); - private static final ParseField STARTED_AT_FIELD = new ParseField("shutdown_started"); - private static final ParseField STARTED_AT_MILLIS_FIELD = new ParseField("shutdown_started_millis"); - - public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "node_shutdown_info", - a -> new SingleNodeShutdownMetadata((String) a[0], (String) a[1], (String) a[2], (boolean) a[3], (long) a[4]) - ); - - static { - PARSER.declareString(ConstructingObjectParser.constructorArg(), NODE_ID_FIELD); - PARSER.declareString(ConstructingObjectParser.constructorArg(), TYPE_FIELD); - PARSER.declareString(ConstructingObjectParser.constructorArg(), REASON_FIELD); - PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), STATUS_FIELD); - PARSER.declareLong(ConstructingObjectParser.constructorArg(), STARTED_AT_MILLIS_FIELD); - } - - public static SingleNodeShutdownMetadata parse(XContentParser parser) { - return PARSER.apply(parser, null); - } - - private final String nodeId; - private final String type; - private final String reason; - private final boolean status; // GWB> Replace with an actual status object - private final long startedAtDate; - - public SingleNodeShutdownMetadata(String nodeId, String type, String reason, boolean status, long startedAtDate) { - this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); - this.type = Objects.requireNonNull(type, "shutdown type must not be null"); - this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); - this.status = status; - this.startedAtDate = startedAtDate; - } - - public SingleNodeShutdownMetadata(StreamInput in) throws IOException { - this.nodeId = in.readString(); - this.type = in.readString(); - this.reason = in.readString(); - this.status = in.readBoolean(); - this.startedAtDate = in.readVLong(); - } - - /** - * @return The ID of the node this {@link SingleNodeShutdownMetadata} concerns. - */ - public String getNodeId() { - return nodeId; - } - - /** - * @return The type of shutdown this is (shutdown vs. permanent). - */ - public String getType() { - return type; - } - - /** - * @return The user-supplied reason this node is shutting down. - */ - public String getReason() { - return reason; - } - - /** - * @return True if this node is ready to shut down, false otherwise. - */ - public boolean isStatus() { - return status; - } - - /** - * @return The timestamp that this shutdown procedure was started. - */ - public long getStartedAtDate() { - return startedAtDate; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeString(nodeId); - out.writeString(type); - out.writeString(reason); - out.writeBoolean(status); - out.writeVLong(startedAtDate); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - { - builder.field(NODE_ID_FIELD.getPreferredName(), nodeId); - builder.field(TYPE_FIELD.getPreferredName(), type); - builder.field(REASON_FIELD.getPreferredName(), reason); - builder.field(STATUS_FIELD.getPreferredName(), status); - builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_FIELD.getPreferredName(), startedAtDate); - } - builder.endObject(); - - return builder; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if ((o instanceof SingleNodeShutdownMetadata) == false) return false; - SingleNodeShutdownMetadata that = (SingleNodeShutdownMetadata) o; - return isStatus() == that.isStatus() - && getStartedAtDate() == that.getStartedAtDate() - && getNodeId().equals(that.getNodeId()) - && getType().equals(that.getType()) - && getReason().equals(that.getReason()); - } - - @Override - public int hashCode() { - return Objects.hash(getNodeId(), getType(), getReason(), isStatus(), getStartedAtDate()); - } - } } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java new file mode 100644 index 0000000000000..e812ded440f9c --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster.metadata; + +import org.elasticsearch.cluster.AbstractDiffable; +import org.elasticsearch.cluster.Diffable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +/** + * Contains data about a single node's shutdown readiness. + */ +public class SingleNodeShutdownMetadata extends AbstractDiffable + implements + ToXContentObject, + Diffable { + + private static final ParseField NODE_ID_FIELD = new ParseField("node_id"); + private static final ParseField TYPE_FIELD = new ParseField("type"); + private static final ParseField REASON_FIELD = new ParseField("reason"); + private static final ParseField STATUS_FIELD = new ParseField("status"); + private static final ParseField STARTED_AT_FIELD = new ParseField("shutdown_started"); + private static final ParseField STARTED_AT_MILLIS_FIELD = new ParseField("shutdown_started_millis"); + + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "node_shutdown_info", + a -> new SingleNodeShutdownMetadata((String) a[0], (String) a[1], (String) a[2], (boolean) a[3], (long) a[4]) + ); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), NODE_ID_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), TYPE_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), REASON_FIELD); + PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), STATUS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), STARTED_AT_MILLIS_FIELD); + } + + public static SingleNodeShutdownMetadata parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + private final String nodeId; + private final String type; + private final String reason; + private final boolean status; // GWB> Replace with an actual status object + private final long startedAtDate; + + public SingleNodeShutdownMetadata(String nodeId, String type, String reason, boolean status, long startedAtDate) { + this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); + this.type = Objects.requireNonNull(type, "shutdown type must not be null"); + this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); + this.status = status; + this.startedAtDate = startedAtDate; + } + + public SingleNodeShutdownMetadata(StreamInput in) throws IOException { + this.nodeId = in.readString(); + this.type = in.readString(); + this.reason = in.readString(); + this.status = in.readBoolean(); + this.startedAtDate = in.readVLong(); + } + + /** + * @return The ID of the node this {@link SingleNodeShutdownMetadata} concerns. + */ + public String getNodeId() { + return nodeId; + } + + /** + * @return The type of shutdown this is (shutdown vs. permanent). + */ + public String getType() { + return type; + } + + /** + * @return The user-supplied reason this node is shutting down. + */ + public String getReason() { + return reason; + } + + /** + * @return True if this node is ready to shut down, false otherwise. + */ + public boolean isStatus() { + return status; + } + + /** + * @return The timestamp that this shutdown procedure was started. + */ + public long getStartedAtDate() { + return startedAtDate; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(nodeId); + out.writeString(type); + out.writeString(reason); + out.writeBoolean(status); + out.writeVLong(startedAtDate); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + { + builder.field(NODE_ID_FIELD.getPreferredName(), nodeId); + builder.field(TYPE_FIELD.getPreferredName(), type); + builder.field(REASON_FIELD.getPreferredName(), reason); + builder.field(STATUS_FIELD.getPreferredName(), status); + builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_FIELD.getPreferredName(), startedAtDate); + } + builder.endObject(); + + return builder; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if ((o instanceof SingleNodeShutdownMetadata) == false) return false; + SingleNodeShutdownMetadata that = (SingleNodeShutdownMetadata) o; + return isStatus() == that.isStatus() + && getStartedAtDate() == that.getStartedAtDate() + && getNodeId().equals(that.getNodeId()) + && getType().equals(that.getType()) + && getReason().equals(that.getReason()); + } + + @Override + public int hashCode() { + return Objects.hash(getNodeId(), getType(), getReason(), isStatus(), getStartedAtDate()); + } +} diff --git a/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java similarity index 78% rename from server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java rename to server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java index 9fde99ec55add..ad77cc4622713 100644 --- a/server/src/test/java/org/elasticsearch/cluster/NodeShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -package org.elasticsearch.cluster; +package org.elasticsearch.cluster.metadata; -import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.cluster.Diff; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.AbstractDiffableSerializationTestCase; @@ -37,13 +37,13 @@ protected Writeable.Reader instanceReader() { @Override protected NodeShutdownMetadata createTestInstance() { - Map nodes = randomList(0, 10, this::randomNodeShutdownInfo).stream() - .collect(Collectors.toMap(NodeShutdownMetadata.SingleNodeShutdownMetadata::getNodeId, Function.identity())); + Map nodes = randomList(0, 10, this::randomNodeShutdownInfo).stream() + .collect(Collectors.toMap(SingleNodeShutdownMetadata::getNodeId, Function.identity())); return new NodeShutdownMetadata(nodes); } - private NodeShutdownMetadata.SingleNodeShutdownMetadata randomNodeShutdownInfo() { - return new NodeShutdownMetadata.SingleNodeShutdownMetadata( + private SingleNodeShutdownMetadata randomNodeShutdownInfo() { + return new SingleNodeShutdownMetadata( randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5), From e056897ad0c14c3424fabe4c641d69a00dbe9038 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Mon, 22 Mar 2021 16:37:13 -0600 Subject: [PATCH 04/12] Register the metadata as necessary --- .../main/java/org/elasticsearch/cluster/ClusterModule.java | 4 ++++ .../cluster/metadata/NodeShutdownMetadata.java | 6 +++++- .../cluster/metadata/NodeShutdownMetadataTests.java | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java b/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java index 95c20e6ee99a2..47d8ecdd4c97f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService; import org.elasticsearch.cluster.metadata.MetadataMappingService; import org.elasticsearch.cluster.metadata.MetadataUpdateSettingsService; +import org.elasticsearch.cluster.metadata.NodeShutdownMetadata; import org.elasticsearch.cluster.metadata.RepositoriesMetadata; import org.elasticsearch.cluster.routing.DelayedAllocationService; import org.elasticsearch.cluster.routing.allocation.AllocationService; @@ -132,6 +133,7 @@ public static List getNamedWriteables() { registerMetadataCustom(entries, ComposableIndexTemplateMetadata.TYPE, ComposableIndexTemplateMetadata::new, ComposableIndexTemplateMetadata::readDiffFrom); registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom); + registerMetadataCustom(entries, NodeShutdownMetadata.TYPE, NodeShutdownMetadata::new, NodeShutdownMetadata::readDiffFrom); // Task Status (not Diffable) entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new)); @@ -157,6 +159,8 @@ public static List getNamedXWriteables() { ComposableIndexTemplateMetadata::fromXContent)); entries.add(new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(DataStreamMetadata.TYPE), DataStreamMetadata::fromXContent)); + entries.add(new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(NodeShutdownMetadata.TYPE), + NodeShutdownMetadata::fromXContent)); return entries; } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java index a1caa7e77422c..d84493905cd1d 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java @@ -57,10 +57,14 @@ public class NodeShutdownMetadata implements Metadata.Custom { ); } - public static NodeShutdownMetadata parse(XContentParser parser) { + public static NodeShutdownMetadata fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } + public static NamedDiff readDiffFrom(StreamInput in) throws IOException { + return new NodeShutdownMetadataDiff(in); + } + private final Map nodes; public NodeShutdownMetadata(Map nodes) { diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java index ad77cc4622713..7332ae5adaf11 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java @@ -27,7 +27,7 @@ protected Writeable.Reader> diffReader() { @Override protected NodeShutdownMetadata doParseInstance(XContentParser parser) throws IOException { - return NodeShutdownMetadata.parse(parser); + return NodeShutdownMetadata.fromXContent(parser); } @Override From 0847643e39acb9bc192e5ef04693609db94ae10e Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Mon, 22 Mar 2021 16:57:53 -0600 Subject: [PATCH 05/12] NodeShutdownMetadata -> NodesShutdownMetadata --- .../elasticsearch/cluster/ClusterModule.java | 8 +++--- ...tadata.java => NodesShutdownMetadata.java} | 26 +++++++++---------- ...s.java => NodesShutdownMetadataTests.java} | 14 +++++----- 3 files changed, 24 insertions(+), 24 deletions(-) rename server/src/main/java/org/elasticsearch/cluster/metadata/{NodeShutdownMetadata.java => NodesShutdownMetadata.java} (83%) rename server/src/test/java/org/elasticsearch/cluster/metadata/{NodeShutdownMetadataTests.java => NodesShutdownMetadataTests.java} (79%) diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java b/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java index 47d8ecdd4c97f..27fad07235e9f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterModule.java @@ -22,7 +22,7 @@ import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService; import org.elasticsearch.cluster.metadata.MetadataMappingService; import org.elasticsearch.cluster.metadata.MetadataUpdateSettingsService; -import org.elasticsearch.cluster.metadata.NodeShutdownMetadata; +import org.elasticsearch.cluster.metadata.NodesShutdownMetadata; import org.elasticsearch.cluster.metadata.RepositoriesMetadata; import org.elasticsearch.cluster.routing.DelayedAllocationService; import org.elasticsearch.cluster.routing.allocation.AllocationService; @@ -133,7 +133,7 @@ public static List getNamedWriteables() { registerMetadataCustom(entries, ComposableIndexTemplateMetadata.TYPE, ComposableIndexTemplateMetadata::new, ComposableIndexTemplateMetadata::readDiffFrom); registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom); - registerMetadataCustom(entries, NodeShutdownMetadata.TYPE, NodeShutdownMetadata::new, NodeShutdownMetadata::readDiffFrom); + registerMetadataCustom(entries, NodesShutdownMetadata.TYPE, NodesShutdownMetadata::new, NodesShutdownMetadata::readDiffFrom); // Task Status (not Diffable) entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new)); @@ -159,8 +159,8 @@ public static List getNamedXWriteables() { ComposableIndexTemplateMetadata::fromXContent)); entries.add(new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(DataStreamMetadata.TYPE), DataStreamMetadata::fromXContent)); - entries.add(new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(NodeShutdownMetadata.TYPE), - NodeShutdownMetadata::fromXContent)); + entries.add(new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(NodesShutdownMetadata.TYPE), + NodesShutdownMetadata::fromXContent)); return entries; } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadata.java similarity index 83% rename from server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java rename to server/src/main/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadata.java index d84493905cd1d..81859c0b138e4 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadata.java @@ -35,17 +35,17 @@ * * Stored in the cluster state as custom metadata. */ -public class NodeShutdownMetadata implements Metadata.Custom { +public class NodesShutdownMetadata implements Metadata.Custom { public static final String TYPE = "node_shutdown"; public static final Version NODE_SHUTDOWN_VERSION = Version.V_8_0_0; private static final ParseField NODES_FIELD = new ParseField("nodes"); @SuppressWarnings("unchecked") - public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(TYPE, a -> { + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(TYPE, a -> { final Map nodes = ((List) a[0]).stream() .collect(Collectors.toMap(SingleNodeShutdownMetadata::getNodeId, Function.identity())); - return new NodeShutdownMetadata(nodes); + return new NodesShutdownMetadata(nodes); }); static { @@ -57,7 +57,7 @@ public class NodeShutdownMetadata implements Metadata.Custom { ); } - public static NodeShutdownMetadata fromXContent(XContentParser parser) { + public static NodesShutdownMetadata fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } @@ -67,11 +67,11 @@ public static NamedDiff readDiffFrom(StreamInput in) throws IOE private final Map nodes; - public NodeShutdownMetadata(Map nodes) { + public NodesShutdownMetadata(Map nodes) { this.nodes = nodes; } - public NodeShutdownMetadata(StreamInput in) throws IOException { + public NodesShutdownMetadata(StreamInput in) throws IOException { this.nodes = in.readMap(StreamInput::readString, SingleNodeShutdownMetadata::new); } @@ -90,7 +90,7 @@ public Map getPerNodeInfo() { @Override public Diff diff(Metadata.Custom previousState) { - return new NodeShutdownMetadataDiff((NodeShutdownMetadata) previousState, this); + return new NodeShutdownMetadataDiff((NodesShutdownMetadata) previousState, this); } @Override @@ -111,8 +111,8 @@ public Version getMinimalSupportedVersion() { @Override public boolean equals(Object o) { if (this == o) return true; - if ((o instanceof NodeShutdownMetadata) == false) return false; - NodeShutdownMetadata that = (NodeShutdownMetadata) o; + if ((o instanceof NodesShutdownMetadata) == false) return false; + NodesShutdownMetadata that = (NodesShutdownMetadata) o; return getPerNodeInfo().equals(that.getPerNodeInfo()); } @@ -128,13 +128,13 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } /** - * Handles diffing and appling diffs for {@link NodeShutdownMetadata} as necessary for the cluster state infrastructure. + * Handles diffing and appling diffs for {@link NodesShutdownMetadata} as necessary for the cluster state infrastructure. */ public static class NodeShutdownMetadataDiff implements NamedDiff { private final Diff> nodesDiff; - NodeShutdownMetadataDiff(NodeShutdownMetadata before, NodeShutdownMetadata after) { + NodeShutdownMetadataDiff(NodesShutdownMetadata before, NodesShutdownMetadata after) { this.nodesDiff = DiffableUtils.diff(before.nodes, after.nodes, DiffableUtils.getStringKeySerializer()); } @@ -150,9 +150,9 @@ public NodeShutdownMetadataDiff(StreamInput in) throws IOException { @Override public Metadata.Custom apply(Metadata.Custom part) { TreeMap newNodes = new TreeMap<>( - nodesDiff.apply(((NodeShutdownMetadata) part).getPerNodeInfo()) + nodesDiff.apply(((NodesShutdownMetadata) part).getPerNodeInfo()) ); - return new NodeShutdownMetadata(newNodes); + return new NodesShutdownMetadata(newNodes); } @Override diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java similarity index 79% rename from server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java rename to server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java index 7332ae5adaf11..02139b8573856 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/NodeShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java @@ -18,28 +18,28 @@ import java.util.function.Function; import java.util.stream.Collectors; -public class NodeShutdownMetadataTests extends AbstractDiffableSerializationTestCase { +public class NodesShutdownMetadataTests extends AbstractDiffableSerializationTestCase { @Override protected Writeable.Reader> diffReader() { - return NodeShutdownMetadata.NodeShutdownMetadataDiff::new; + return NodesShutdownMetadata.NodeShutdownMetadataDiff::new; } @Override - protected NodeShutdownMetadata doParseInstance(XContentParser parser) throws IOException { - return NodeShutdownMetadata.fromXContent(parser); + protected NodesShutdownMetadata doParseInstance(XContentParser parser) throws IOException { + return NodesShutdownMetadata.fromXContent(parser); } @Override protected Writeable.Reader instanceReader() { - return NodeShutdownMetadata::new; + return NodesShutdownMetadata::new; } @Override - protected NodeShutdownMetadata createTestInstance() { + protected NodesShutdownMetadata createTestInstance() { Map nodes = randomList(0, 10, this::randomNodeShutdownInfo).stream() .collect(Collectors.toMap(SingleNodeShutdownMetadata::getNodeId, Function.identity())); - return new NodeShutdownMetadata(nodes); + return new NodesShutdownMetadata(nodes); } private SingleNodeShutdownMetadata randomNodeShutdownInfo() { From 152f780100add6898a9cc2acf8c695317e6e8a80 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Mon, 22 Mar 2021 17:40:10 -0600 Subject: [PATCH 06/12] Status is a real status now, not just a boolean --- .../metadata/SingleNodeShutdownMetadata.java | 137 ++++++++++++++++-- .../metadata/NodesShutdownMetadataTests.java | 19 ++- 2 files changed, 143 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java index e812ded440f9c..1fc0f58220819 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java @@ -10,10 +10,12 @@ import org.elasticsearch.cluster.AbstractDiffable; import org.elasticsearch.cluster.Diffable; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; @@ -33,20 +35,27 @@ public class SingleNodeShutdownMetadata extends AbstractDiffable PARSER = new ConstructingObjectParser<>( "node_shutdown_info", - a -> new SingleNodeShutdownMetadata((String) a[0], (String) a[1], (String) a[2], (boolean) a[3], (long) a[4]) + a -> new SingleNodeShutdownMetadata((String) a[0], (String) a[1], (String) a[2], Status.valueOf((String) a[3]), (long) a[4], + (ComponentShutdownStatus) a[5]) ); static { PARSER.declareString(ConstructingObjectParser.constructorArg(), NODE_ID_FIELD); PARSER.declareString(ConstructingObjectParser.constructorArg(), TYPE_FIELD); PARSER.declareString(ConstructingObjectParser.constructorArg(), REASON_FIELD); - PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), STATUS_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), STATUS_FIELD); PARSER.declareLong(ConstructingObjectParser.constructorArg(), STARTED_AT_MILLIS_FIELD); + PARSER.declareObject( + ConstructingObjectParser.constructorArg(), + (parser, context) -> ComponentShutdownStatus.parse(parser), + SHARD_MIGRATION_FIELD + ); } public static SingleNodeShutdownMetadata parse(XContentParser parser) { @@ -56,23 +65,28 @@ public static SingleNodeShutdownMetadata parse(XContentParser parser) { private final String nodeId; private final String type; private final String reason; - private final boolean status; // GWB> Replace with an actual status object + private final Status status; private final long startedAtDate; + private final ComponentShutdownStatus shardMigrationStatus; - public SingleNodeShutdownMetadata(String nodeId, String type, String reason, boolean status, long startedAtDate) { + + public SingleNodeShutdownMetadata( + String nodeId, String type, String reason, Status status, long startedAtDate, ComponentShutdownStatus shardMigrationStatus) { this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); this.type = Objects.requireNonNull(type, "shutdown type must not be null"); this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); this.status = status; this.startedAtDate = startedAtDate; + this.shardMigrationStatus = Objects.requireNonNull(shardMigrationStatus, "shard migration status must not be null"); } public SingleNodeShutdownMetadata(StreamInput in) throws IOException { this.nodeId = in.readString(); this.type = in.readString(); this.reason = in.readString(); - this.status = in.readBoolean(); + this.status = in.readEnum(Status.class); this.startedAtDate = in.readVLong(); + this.shardMigrationStatus = new ComponentShutdownStatus(in); } /** @@ -97,9 +111,9 @@ public String getReason() { } /** - * @return True if this node is ready to shut down, false otherwise. + * @return The status of this node's shutdown. */ - public boolean isStatus() { + public Status isStatus() { return status; } @@ -115,8 +129,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(nodeId); out.writeString(type); out.writeString(reason); - out.writeBoolean(status); + out.writeEnum(status); out.writeVLong(startedAtDate); + shardMigrationStatus.writeTo(out); } @Override @@ -127,7 +142,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(TYPE_FIELD.getPreferredName(), type); builder.field(REASON_FIELD.getPreferredName(), reason); builder.field(STATUS_FIELD.getPreferredName(), status); - builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_FIELD.getPreferredName(), startedAtDate); + builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_READABLE_FIELD, startedAtDate); + builder.field(SHARD_MIGRATION_FIELD.getPreferredName(), shardMigrationStatus); } builder.endObject(); @@ -150,4 +166,103 @@ && getType().equals(that.getType()) public int hashCode() { return Objects.hash(getNodeId(), getType(), getReason(), isStatus(), getStartedAtDate()); } + + public enum Type { + REMOVE, + RESTART + } + + public enum Status { + NOT_STARTED, + IN_PROGRESS, + STALLED, + COMPLETE + } + + public static class ComponentShutdownStatus extends AbstractDiffable implements ToXContentFragment { + private final Status status; + @Nullable private final Long startedAtDate; + @Nullable private final String errorMessage; + + private static final ParseField STATUS_FIELD = new ParseField("status"); + + private static final ParseField TIME_STARTED_FIELD = new ParseField("time_started_millis"); + private static final ParseField ERROR_FIELD = new ParseField("error"); + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "node_shutdown_component", + a -> new ComponentShutdownStatus(Status.valueOf((String) a[0]), (Long) a[1], (String) a[2]) + ); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), STATUS_FIELD); + PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), TIME_STARTED_FIELD); + PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), ERROR_FIELD); + } + + public static ComponentShutdownStatus parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + public ComponentShutdownStatus(Status status, Long startedAtDate, String errorMessage) { + this.status = status; + this.startedAtDate = startedAtDate; + this.errorMessage = errorMessage; + } + + public ComponentShutdownStatus(StreamInput in) throws IOException { + this.status = in.readEnum(Status.class); + this.startedAtDate = in.readOptionalVLong(); + this.errorMessage = in.readOptionalString(); + } + + public Status getStatus() { + return status; + } + + public Long getStartedAtDate() { + return startedAtDate; + } + + public String getErrorMessage() { + return errorMessage; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + { + builder.field(STATUS_FIELD.getPreferredName(), status); + if (startedAtDate != null) { + builder.timeField(TIME_STARTED_FIELD.getPreferredName(), "time_started", startedAtDate); + } + if (errorMessage != null) { + builder.field(ERROR_FIELD.getPreferredName(), errorMessage); + } + } + builder.endObject(); + return builder; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeEnum(status); + out.writeOptionalVLong(startedAtDate); + out.writeOptionalString(errorMessage); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if ((o instanceof ComponentShutdownStatus) == false) return false; + ComponentShutdownStatus that = (ComponentShutdownStatus) o; + return getStatus() == that.getStatus() + && Objects.equals(getStartedAtDate(), that.getStartedAtDate()) + && Objects.equals(getErrorMessage(), that.getErrorMessage()); + } + + @Override + public int hashCode() { + return Objects.hash(getStatus(), getStartedAtDate(), getErrorMessage()); + } + } } diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java index 02139b8573856..46b455f561519 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java @@ -14,6 +14,8 @@ import org.elasticsearch.test.AbstractDiffableSerializationTestCase; import java.io.IOException; +import java.util.ArrayList; +import java.util.EnumSet; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -47,8 +49,21 @@ private SingleNodeShutdownMetadata randomNodeShutdownInfo() { randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5), - randomBoolean(), - randomNonNegativeLong() + randomStatus(), + randomNonNegativeLong(), + randomComponentStatus() + ); + } + + private SingleNodeShutdownMetadata.Status randomStatus() { + return randomFrom(new ArrayList<>(EnumSet.allOf(SingleNodeShutdownMetadata.Status.class))); + } + + private SingleNodeShutdownMetadata.ComponentShutdownStatus randomComponentStatus() { + return new SingleNodeShutdownMetadata.ComponentShutdownStatus( + randomStatus(), + randomBoolean() ? null : randomNonNegativeLong(), + randomBoolean() ? null : randomAlphaOfLengthBetween(4, 10) ); } From 437afc98751fa25ac7984d13bf1b3b5ff79dbff4 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Tue, 23 Mar 2021 15:16:08 -0600 Subject: [PATCH 07/12] Actually use `Type` enum --- .../metadata/SingleNodeShutdownMetadata.java | 20 ++++++++++++------- .../metadata/NodesShutdownMetadataTests.java | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java index 1fc0f58220819..9f77a36e71575 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java @@ -41,8 +41,14 @@ public class SingleNodeShutdownMetadata extends AbstractDiffable PARSER = new ConstructingObjectParser<>( "node_shutdown_info", - a -> new SingleNodeShutdownMetadata((String) a[0], (String) a[1], (String) a[2], Status.valueOf((String) a[3]), (long) a[4], - (ComponentShutdownStatus) a[5]) + a -> new SingleNodeShutdownMetadata( + (String) a[0], + Type.valueOf((String) a[1]), + (String) a[2], + Status.valueOf((String) a[3]), + (long) a[4], + (ComponentShutdownStatus) a[5] + ) ); static { @@ -63,7 +69,7 @@ public static SingleNodeShutdownMetadata parse(XContentParser parser) { } private final String nodeId; - private final String type; + private final Type type; private final String reason; private final Status status; private final long startedAtDate; @@ -71,7 +77,7 @@ public static SingleNodeShutdownMetadata parse(XContentParser parser) { public SingleNodeShutdownMetadata( - String nodeId, String type, String reason, Status status, long startedAtDate, ComponentShutdownStatus shardMigrationStatus) { + String nodeId, Type type, String reason, Status status, long startedAtDate, ComponentShutdownStatus shardMigrationStatus) { this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); this.type = Objects.requireNonNull(type, "shutdown type must not be null"); this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); @@ -82,7 +88,7 @@ public SingleNodeShutdownMetadata( public SingleNodeShutdownMetadata(StreamInput in) throws IOException { this.nodeId = in.readString(); - this.type = in.readString(); + this.type = in.readEnum(Type.class); this.reason = in.readString(); this.status = in.readEnum(Status.class); this.startedAtDate = in.readVLong(); @@ -99,7 +105,7 @@ public String getNodeId() { /** * @return The type of shutdown this is (shutdown vs. permanent). */ - public String getType() { + public Type getType() { return type; } @@ -127,7 +133,7 @@ public long getStartedAtDate() { @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(nodeId); - out.writeString(type); + out.writeEnum(type); out.writeString(reason); out.writeEnum(status); out.writeVLong(startedAtDate); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java index 46b455f561519..eb0440cc483fc 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java @@ -47,7 +47,7 @@ protected NodesShutdownMetadata createTestInstance() { private SingleNodeShutdownMetadata randomNodeShutdownInfo() { return new SingleNodeShutdownMetadata( randomAlphaOfLength(5), - randomAlphaOfLength(5), + randomBoolean() ? SingleNodeShutdownMetadata.Type.REMOVE : SingleNodeShutdownMetadata.Type.RESTART, randomAlphaOfLength(5), randomStatus(), randomNonNegativeLong(), From eae23851af66c21b580ae2075c27b57c964471ab Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Tue, 23 Mar 2021 15:17:25 -0600 Subject: [PATCH 08/12] Units in timestamp variable names --- .../metadata/SingleNodeShutdownMetadata.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java index 9f77a36e71575..6caa6e13df1d8 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java @@ -72,17 +72,17 @@ public static SingleNodeShutdownMetadata parse(XContentParser parser) { private final Type type; private final String reason; private final Status status; - private final long startedAtDate; + private final long startedAtMillis; private final ComponentShutdownStatus shardMigrationStatus; public SingleNodeShutdownMetadata( - String nodeId, Type type, String reason, Status status, long startedAtDate, ComponentShutdownStatus shardMigrationStatus) { + String nodeId, Type type, String reason, Status status, long startedAtMillis, ComponentShutdownStatus shardMigrationStatus) { this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); this.type = Objects.requireNonNull(type, "shutdown type must not be null"); this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); this.status = status; - this.startedAtDate = startedAtDate; + this.startedAtMillis = startedAtMillis; this.shardMigrationStatus = Objects.requireNonNull(shardMigrationStatus, "shard migration status must not be null"); } @@ -91,7 +91,7 @@ public SingleNodeShutdownMetadata(StreamInput in) throws IOException { this.type = in.readEnum(Type.class); this.reason = in.readString(); this.status = in.readEnum(Status.class); - this.startedAtDate = in.readVLong(); + this.startedAtMillis = in.readVLong(); this.shardMigrationStatus = new ComponentShutdownStatus(in); } @@ -126,8 +126,8 @@ public Status isStatus() { /** * @return The timestamp that this shutdown procedure was started. */ - public long getStartedAtDate() { - return startedAtDate; + public long getStartedAtMillis() { + return startedAtMillis; } @Override @@ -136,7 +136,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeEnum(type); out.writeString(reason); out.writeEnum(status); - out.writeVLong(startedAtDate); + out.writeVLong(startedAtMillis); shardMigrationStatus.writeTo(out); } @@ -148,7 +148,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(TYPE_FIELD.getPreferredName(), type); builder.field(REASON_FIELD.getPreferredName(), reason); builder.field(STATUS_FIELD.getPreferredName(), status); - builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_READABLE_FIELD, startedAtDate); + builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_READABLE_FIELD, startedAtMillis); builder.field(SHARD_MIGRATION_FIELD.getPreferredName(), shardMigrationStatus); } builder.endObject(); @@ -162,7 +162,7 @@ public boolean equals(Object o) { if ((o instanceof SingleNodeShutdownMetadata) == false) return false; SingleNodeShutdownMetadata that = (SingleNodeShutdownMetadata) o; return isStatus() == that.isStatus() - && getStartedAtDate() == that.getStartedAtDate() + && getStartedAtMillis() == that.getStartedAtMillis() && getNodeId().equals(that.getNodeId()) && getType().equals(that.getType()) && getReason().equals(that.getReason()); @@ -170,7 +170,7 @@ && getType().equals(that.getType()) @Override public int hashCode() { - return Objects.hash(getNodeId(), getType(), getReason(), isStatus(), getStartedAtDate()); + return Objects.hash(getNodeId(), getType(), getReason(), isStatus(), getStartedAtMillis()); } public enum Type { @@ -187,7 +187,7 @@ public enum Status { public static class ComponentShutdownStatus extends AbstractDiffable implements ToXContentFragment { private final Status status; - @Nullable private final Long startedAtDate; + @Nullable private final Long startedAtMillis; @Nullable private final String errorMessage; private static final ParseField STATUS_FIELD = new ParseField("status"); @@ -209,15 +209,15 @@ public static ComponentShutdownStatus parse(XContentParser parser) { return PARSER.apply(parser, null); } - public ComponentShutdownStatus(Status status, Long startedAtDate, String errorMessage) { + public ComponentShutdownStatus(Status status, Long startedAtMillis, String errorMessage) { this.status = status; - this.startedAtDate = startedAtDate; + this.startedAtMillis = startedAtMillis; this.errorMessage = errorMessage; } public ComponentShutdownStatus(StreamInput in) throws IOException { this.status = in.readEnum(Status.class); - this.startedAtDate = in.readOptionalVLong(); + this.startedAtMillis = in.readOptionalVLong(); this.errorMessage = in.readOptionalString(); } @@ -225,8 +225,8 @@ public Status getStatus() { return status; } - public Long getStartedAtDate() { - return startedAtDate; + public Long getStartedAtMillis() { + return startedAtMillis; } public String getErrorMessage() { @@ -238,8 +238,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.startObject(); { builder.field(STATUS_FIELD.getPreferredName(), status); - if (startedAtDate != null) { - builder.timeField(TIME_STARTED_FIELD.getPreferredName(), "time_started", startedAtDate); + if (startedAtMillis != null) { + builder.timeField(TIME_STARTED_FIELD.getPreferredName(), "time_started", startedAtMillis); } if (errorMessage != null) { builder.field(ERROR_FIELD.getPreferredName(), errorMessage); @@ -252,7 +252,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public void writeTo(StreamOutput out) throws IOException { out.writeEnum(status); - out.writeOptionalVLong(startedAtDate); + out.writeOptionalVLong(startedAtMillis); out.writeOptionalString(errorMessage); } @@ -262,13 +262,13 @@ public boolean equals(Object o) { if ((o instanceof ComponentShutdownStatus) == false) return false; ComponentShutdownStatus that = (ComponentShutdownStatus) o; return getStatus() == that.getStatus() - && Objects.equals(getStartedAtDate(), that.getStartedAtDate()) + && Objects.equals(getStartedAtMillis(), that.getStartedAtMillis()) && Objects.equals(getErrorMessage(), that.getErrorMessage()); } @Override public int hashCode() { - return Objects.hash(getStatus(), getStartedAtDate(), getErrorMessage()); + return Objects.hash(getStatus(), getStartedAtMillis(), getErrorMessage()); } } } From 16d5f75fa108a2de329744e913699f1acf961e29 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Tue, 23 Mar 2021 15:18:39 -0600 Subject: [PATCH 09/12] @Nullable where I forgot it --- .../cluster/metadata/SingleNodeShutdownMetadata.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java index 6caa6e13df1d8..4cd92cb09fc05 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java @@ -209,7 +209,7 @@ public static ComponentShutdownStatus parse(XContentParser parser) { return PARSER.apply(parser, null); } - public ComponentShutdownStatus(Status status, Long startedAtMillis, String errorMessage) { + public ComponentShutdownStatus(Status status, @Nullable Long startedAtMillis, @Nullable String errorMessage) { this.status = status; this.startedAtMillis = startedAtMillis; this.errorMessage = errorMessage; @@ -225,10 +225,12 @@ public Status getStatus() { return status; } + @Nullable public Long getStartedAtMillis() { return startedAtMillis; } + @Nullable public String getErrorMessage() { return errorMessage; } From bde0e5e3fda72fafe79d103fc2cfd3928cd722b1 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Tue, 23 Mar 2021 15:21:43 -0600 Subject: [PATCH 10/12] Break Component status out into its own class --- .../metadata/NodeShutdownComponentStatus.java | 109 ++++++++++++++++++ .../metadata/SingleNodeShutdownMetadata.java | 100 +--------------- .../metadata/NodesShutdownMetadataTests.java | 4 +- 3 files changed, 116 insertions(+), 97 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java new file mode 100644 index 0000000000000..eba9123480d45 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster.metadata; + +import org.elasticsearch.cluster.AbstractDiffable; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentFragment; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +public class NodeShutdownComponentStatus extends AbstractDiffable implements ToXContentFragment { + private final SingleNodeShutdownMetadata.Status status; + @Nullable private final Long startedAtMillis; + @Nullable private final String errorMessage; + + private static final ParseField STATUS_FIELD = new ParseField("status"); + + private static final ParseField TIME_STARTED_FIELD = new ParseField("time_started_millis"); + private static final ParseField ERROR_FIELD = new ParseField("error"); + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "node_shutdown_component", + a -> new NodeShutdownComponentStatus(SingleNodeShutdownMetadata.Status.valueOf((String) a[0]), (Long) a[1], (String) a[2])); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), STATUS_FIELD); + PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), TIME_STARTED_FIELD); + PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), ERROR_FIELD); + } + + public static NodeShutdownComponentStatus parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + public NodeShutdownComponentStatus( + SingleNodeShutdownMetadata.Status status, + @Nullable Long startedAtMillis, + @Nullable String errorMessage) { + this.status = status; + this.startedAtMillis = startedAtMillis; + this.errorMessage = errorMessage; + } + + public NodeShutdownComponentStatus(StreamInput in) throws IOException { + this.status = in.readEnum(SingleNodeShutdownMetadata.Status.class); + this.startedAtMillis = in.readOptionalVLong(); + this.errorMessage = in.readOptionalString(); + } + + public SingleNodeShutdownMetadata.Status getStatus() { + return status; + } + + @Nullable public Long getStartedAtMillis() { + return startedAtMillis; + } + + @Nullable public String getErrorMessage() { + return errorMessage; + } + + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + { + builder.field(STATUS_FIELD.getPreferredName(), status); + if (startedAtMillis != null) { + builder.timeField(TIME_STARTED_FIELD.getPreferredName(), "time_started", startedAtMillis); + } + if (errorMessage != null) { + builder.field(ERROR_FIELD.getPreferredName(), errorMessage); + } + } + builder.endObject(); + return builder; + } + + @Override public void writeTo(StreamOutput out) throws IOException { + out.writeEnum(status); + out.writeOptionalVLong(startedAtMillis); + out.writeOptionalString(errorMessage); + } + + @Override public boolean equals(Object o) { + if (this == o) + return true; + if ((o instanceof NodeShutdownComponentStatus) == false) + return false; + NodeShutdownComponentStatus that = (NodeShutdownComponentStatus) o; + return getStatus() == that.getStatus() && Objects.equals(getStartedAtMillis(), that.getStartedAtMillis()) && Objects.equals( + getErrorMessage(), + that.getErrorMessage()); + } + + @Override public int hashCode() { + return Objects.hash(getStatus(), getStartedAtMillis(), getErrorMessage()); + } +} diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java index 4cd92cb09fc05..cd261befdffd6 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java @@ -10,12 +10,10 @@ import org.elasticsearch.cluster.AbstractDiffable; import org.elasticsearch.cluster.Diffable; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ConstructingObjectParser; -import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; @@ -47,7 +45,7 @@ public class SingleNodeShutdownMetadata extends AbstractDiffable ComponentShutdownStatus.parse(parser), + (parser, context) -> NodeShutdownComponentStatus.parse(parser), SHARD_MIGRATION_FIELD ); } @@ -73,11 +71,11 @@ public static SingleNodeShutdownMetadata parse(XContentParser parser) { private final String reason; private final Status status; private final long startedAtMillis; - private final ComponentShutdownStatus shardMigrationStatus; + private final NodeShutdownComponentStatus shardMigrationStatus; public SingleNodeShutdownMetadata( - String nodeId, Type type, String reason, Status status, long startedAtMillis, ComponentShutdownStatus shardMigrationStatus) { + String nodeId, Type type, String reason, Status status, long startedAtMillis, NodeShutdownComponentStatus shardMigrationStatus) { this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); this.type = Objects.requireNonNull(type, "shutdown type must not be null"); this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); @@ -92,7 +90,7 @@ public SingleNodeShutdownMetadata(StreamInput in) throws IOException { this.reason = in.readString(); this.status = in.readEnum(Status.class); this.startedAtMillis = in.readVLong(); - this.shardMigrationStatus = new ComponentShutdownStatus(in); + this.shardMigrationStatus = new NodeShutdownComponentStatus(in); } /** @@ -185,92 +183,4 @@ public enum Status { COMPLETE } - public static class ComponentShutdownStatus extends AbstractDiffable implements ToXContentFragment { - private final Status status; - @Nullable private final Long startedAtMillis; - @Nullable private final String errorMessage; - - private static final ParseField STATUS_FIELD = new ParseField("status"); - - private static final ParseField TIME_STARTED_FIELD = new ParseField("time_started_millis"); - private static final ParseField ERROR_FIELD = new ParseField("error"); - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "node_shutdown_component", - a -> new ComponentShutdownStatus(Status.valueOf((String) a[0]), (Long) a[1], (String) a[2]) - ); - - static { - PARSER.declareString(ConstructingObjectParser.constructorArg(), STATUS_FIELD); - PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), TIME_STARTED_FIELD); - PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), ERROR_FIELD); - } - - public static ComponentShutdownStatus parse(XContentParser parser) { - return PARSER.apply(parser, null); - } - - public ComponentShutdownStatus(Status status, @Nullable Long startedAtMillis, @Nullable String errorMessage) { - this.status = status; - this.startedAtMillis = startedAtMillis; - this.errorMessage = errorMessage; - } - - public ComponentShutdownStatus(StreamInput in) throws IOException { - this.status = in.readEnum(Status.class); - this.startedAtMillis = in.readOptionalVLong(); - this.errorMessage = in.readOptionalString(); - } - - public Status getStatus() { - return status; - } - - @Nullable - public Long getStartedAtMillis() { - return startedAtMillis; - } - - @Nullable - public String getErrorMessage() { - return errorMessage; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - { - builder.field(STATUS_FIELD.getPreferredName(), status); - if (startedAtMillis != null) { - builder.timeField(TIME_STARTED_FIELD.getPreferredName(), "time_started", startedAtMillis); - } - if (errorMessage != null) { - builder.field(ERROR_FIELD.getPreferredName(), errorMessage); - } - } - builder.endObject(); - return builder; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeEnum(status); - out.writeOptionalVLong(startedAtMillis); - out.writeOptionalString(errorMessage); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if ((o instanceof ComponentShutdownStatus) == false) return false; - ComponentShutdownStatus that = (ComponentShutdownStatus) o; - return getStatus() == that.getStatus() - && Objects.equals(getStartedAtMillis(), that.getStartedAtMillis()) - && Objects.equals(getErrorMessage(), that.getErrorMessage()); - } - - @Override - public int hashCode() { - return Objects.hash(getStatus(), getStartedAtMillis(), getErrorMessage()); - } - } } diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java index eb0440cc483fc..33439231ed308 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java @@ -59,8 +59,8 @@ private SingleNodeShutdownMetadata.Status randomStatus() { return randomFrom(new ArrayList<>(EnumSet.allOf(SingleNodeShutdownMetadata.Status.class))); } - private SingleNodeShutdownMetadata.ComponentShutdownStatus randomComponentStatus() { - return new SingleNodeShutdownMetadata.ComponentShutdownStatus( + private NodeShutdownComponentStatus randomComponentStatus() { + return new NodeShutdownComponentStatus( randomStatus(), randomBoolean() ? null : randomNonNegativeLong(), randomBoolean() ? null : randomAlphaOfLengthBetween(4, 10) From aab295238d5aa5e103fc56ca13090b6abffd8c5d Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Wed, 24 Mar 2021 14:12:53 -0600 Subject: [PATCH 11/12] Add remaining fields --- .../metadata/SingleNodeShutdownMetadata.java | 59 ++++++++++++++++--- .../metadata/NodesShutdownMetadataTests.java | 5 +- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java index cd261befdffd6..8bbe23ae73f2e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/SingleNodeShutdownMetadata.java @@ -32,10 +32,12 @@ public class SingleNodeShutdownMetadata extends AbstractDiffable PARSER = new ConstructingObjectParser<>( "node_shutdown_info", @@ -45,7 +47,9 @@ public class SingleNodeShutdownMetadata extends AbstractDiffable NodeShutdownComponentStatus.parse(parser), SHARD_MIGRATION_FIELD ); + PARSER.declareObject( + ConstructingObjectParser.constructorArg(), + (parser, context) -> NodeShutdownComponentStatus.parse(parser), + PERSISTENT_TASKS_FIELD + ); + PARSER.declareObject( + ConstructingObjectParser.constructorArg(), + (parser, context) -> NodeShutdownComponentStatus.parse(parser), + PLUGINS_STATUS + ); } public static SingleNodeShutdownMetadata parse(XContentParser parser) { @@ -72,16 +86,26 @@ public static SingleNodeShutdownMetadata parse(XContentParser parser) { private final Status status; private final long startedAtMillis; private final NodeShutdownComponentStatus shardMigrationStatus; + private final NodeShutdownComponentStatus persistentTasksStatus; + private final NodeShutdownComponentStatus pluginsStatus; public SingleNodeShutdownMetadata( - String nodeId, Type type, String reason, Status status, long startedAtMillis, NodeShutdownComponentStatus shardMigrationStatus) { + String nodeId, + Type type, + String reason, + Status status, + long startedAtMillis, + NodeShutdownComponentStatus shardMigrationStatus, + NodeShutdownComponentStatus persistentTasksStatus, NodeShutdownComponentStatus pluginsStatus) { this.nodeId = Objects.requireNonNull(nodeId, "node ID must not be null"); this.type = Objects.requireNonNull(type, "shutdown type must not be null"); this.reason = Objects.requireNonNull(reason, "shutdown reason must not be null"); this.status = status; this.startedAtMillis = startedAtMillis; this.shardMigrationStatus = Objects.requireNonNull(shardMigrationStatus, "shard migration status must not be null"); + this.persistentTasksStatus = Objects.requireNonNull(persistentTasksStatus, "persistent tasks status must not be null"); + this.pluginsStatus = Objects.requireNonNull(pluginsStatus, "plugins status must not be null"); } public SingleNodeShutdownMetadata(StreamInput in) throws IOException { @@ -91,6 +115,8 @@ public SingleNodeShutdownMetadata(StreamInput in) throws IOException { this.status = in.readEnum(Status.class); this.startedAtMillis = in.readVLong(); this.shardMigrationStatus = new NodeShutdownComponentStatus(in); + this.persistentTasksStatus = new NodeShutdownComponentStatus(in); + this.pluginsStatus = new NodeShutdownComponentStatus(in); } /** @@ -136,6 +162,8 @@ public void writeTo(StreamOutput out) throws IOException { out.writeEnum(status); out.writeVLong(startedAtMillis); shardMigrationStatus.writeTo(out); + persistentTasksStatus.writeTo(out); + pluginsStatus.writeTo(out); } @Override @@ -148,6 +176,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(STATUS_FIELD.getPreferredName(), status); builder.timeField(STARTED_AT_MILLIS_FIELD.getPreferredName(), STARTED_AT_READABLE_FIELD, startedAtMillis); builder.field(SHARD_MIGRATION_FIELD.getPreferredName(), shardMigrationStatus); + builder.field(PERSISTENT_TASKS_FIELD.getPreferredName(), persistentTasksStatus); + builder.field(PLUGINS_STATUS.getPreferredName(), pluginsStatus); } builder.endObject(); @@ -159,16 +189,28 @@ public boolean equals(Object o) { if (this == o) return true; if ((o instanceof SingleNodeShutdownMetadata) == false) return false; SingleNodeShutdownMetadata that = (SingleNodeShutdownMetadata) o; - return isStatus() == that.isStatus() - && getStartedAtMillis() == that.getStartedAtMillis() + return getStartedAtMillis() == that.getStartedAtMillis() && getNodeId().equals(that.getNodeId()) - && getType().equals(that.getType()) - && getReason().equals(that.getReason()); + && getType() == that.getType() + && getReason().equals(that.getReason()) + && status == that.status + && shardMigrationStatus.equals(that.shardMigrationStatus) + && persistentTasksStatus.equals(that.persistentTasksStatus) + && pluginsStatus.equals(that.pluginsStatus); } @Override public int hashCode() { - return Objects.hash(getNodeId(), getType(), getReason(), isStatus(), getStartedAtMillis()); + return Objects.hash( + getNodeId(), + getType(), + getReason(), + status, + getStartedAtMillis(), + shardMigrationStatus, + persistentTasksStatus, + pluginsStatus + ); } public enum Type { @@ -182,5 +224,4 @@ public enum Status { STALLED, COMPLETE } - } diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java index 33439231ed308..e971eda0f672f 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/NodesShutdownMetadataTests.java @@ -51,8 +51,9 @@ private SingleNodeShutdownMetadata randomNodeShutdownInfo() { randomAlphaOfLength(5), randomStatus(), randomNonNegativeLong(), - randomComponentStatus() - ); + randomComponentStatus(), + randomComponentStatus(), + randomComponentStatus()); } private SingleNodeShutdownMetadata.Status randomStatus() { From f55b51b8ffb154b7ccc0b60f457b50e30a7a7844 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Thu, 25 Mar 2021 10:50:26 -0600 Subject: [PATCH 12/12] Javadoc & cleanup per review --- .../metadata/NodeShutdownComponentStatus.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java index eba9123480d45..67b6ec9048022 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/NodeShutdownComponentStatus.java @@ -21,13 +21,15 @@ import java.io.IOException; import java.util.Objects; +/** + * Contains information about the status of a single component (e.g. `shard_migration`, `persistent_tasks`) of the node shutdown process. + */ public class NodeShutdownComponentStatus extends AbstractDiffable implements ToXContentFragment { private final SingleNodeShutdownMetadata.Status status; @Nullable private final Long startedAtMillis; @Nullable private final String errorMessage; private static final ParseField STATUS_FIELD = new ParseField("status"); - private static final ParseField TIME_STARTED_FIELD = new ParseField("time_started_millis"); private static final ParseField ERROR_FIELD = new ParseField("error"); private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( @@ -59,14 +61,23 @@ public NodeShutdownComponentStatus(StreamInput in) throws IOException { this.errorMessage = in.readOptionalString(); } + /** + * @return The overall status of this component. + */ public SingleNodeShutdownMetadata.Status getStatus() { return status; } + /** + * @return The timestamp this component started shutting down. Null if the component has not yet started shutting down. + */ @Nullable public Long getStartedAtMillis() { return startedAtMillis; } + /** + * @return The error message this component encountered while trying to shut down, if any. Null if no errors have been encountered. + */ @Nullable public String getErrorMessage() { return errorMessage; }