|
7 | 7 |
|
8 | 8 | package org.elasticsearch.xpack.shutdown;
|
9 | 9 |
|
| 10 | +import org.apache.logging.log4j.message.ParameterizedMessage; |
10 | 11 | import org.elasticsearch.Build;
|
11 | 12 | import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
|
12 | 13 | import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
|
| 14 | +import org.elasticsearch.action.index.IndexRequestBuilder; |
13 | 15 | import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
| 16 | +import org.elasticsearch.cluster.ClusterState; |
| 17 | +import org.elasticsearch.cluster.metadata.IndexMetadata; |
14 | 18 | import org.elasticsearch.cluster.metadata.SingleNodeShutdownMetadata;
|
15 | 19 | import org.elasticsearch.cluster.node.DiscoveryNode;
|
| 20 | +import org.elasticsearch.cluster.routing.ShardRouting; |
| 21 | +import org.elasticsearch.cluster.routing.ShardRoutingState; |
| 22 | +import org.elasticsearch.cluster.routing.UnassignedInfo; |
16 | 23 | import org.elasticsearch.common.settings.Settings;
|
17 | 24 | import org.elasticsearch.plugins.Plugin;
|
18 | 25 | import org.elasticsearch.test.ESIntegTestCase;
|
19 | 26 | import org.elasticsearch.test.InternalTestCluster;
|
20 | 27 |
|
21 | 28 | import java.util.Arrays;
|
22 | 29 | import java.util.Collection;
|
| 30 | +import java.util.List; |
23 | 31 |
|
24 | 32 | import static org.elasticsearch.cluster.metadata.SingleNodeShutdownMetadata.Status.COMPLETE;
|
25 | 33 | import static org.hamcrest.Matchers.equalTo;
|
@@ -134,6 +142,71 @@ public void testShardStatusIsCompleteOnNonDataNodes() throws Exception {
|
134 | 142 | assertThat(getResp.getShutdownStatuses().get(0).migrationStatus().getStatus(), equalTo(COMPLETE));
|
135 | 143 | }
|
136 | 144 |
|
| 145 | + /** |
| 146 | + * Checks that, if we get to a situation where a shard can't move because all other nodes already have a copy of that shard, |
| 147 | + * we'll still return COMPLETE instead of STALLED. |
| 148 | + */ |
| 149 | + public void testNotStalledIfAllShardsHaveACopyOnAnotherNode() throws Exception { |
| 150 | + internalCluster().startNodes(2); |
| 151 | + |
| 152 | + final String indexName = "test"; |
| 153 | + prepareCreate(indexName).setSettings( |
| 154 | + Settings.builder() |
| 155 | + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) |
| 156 | + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) // <- Ensure we have a copy of the shard on both nodes |
| 157 | + .put(UnassignedInfo.INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), 0) // Disable "normal" delayed allocation |
| 158 | + ).get(); |
| 159 | + ensureGreen(indexName); |
| 160 | + indexRandomData(); |
| 161 | + |
| 162 | + String nodeToStopId = findIdOfNodeWithPrimaryShard(indexName); |
| 163 | + PutShutdownNodeAction.Request putShutdownRequest = new PutShutdownNodeAction.Request( |
| 164 | + nodeToStopId, |
| 165 | + SingleNodeShutdownMetadata.Type.REMOVE, |
| 166 | + this.getTestName(), |
| 167 | + null |
| 168 | + ); |
| 169 | + AcknowledgedResponse putShutdownResponse = client().execute(PutShutdownNodeAction.INSTANCE, putShutdownRequest).get(); |
| 170 | + assertTrue(putShutdownResponse.isAcknowledged()); |
| 171 | + |
| 172 | + assertBusy(() -> { |
| 173 | + GetShutdownStatusAction.Response getResp = client().execute( |
| 174 | + GetShutdownStatusAction.INSTANCE, |
| 175 | + new GetShutdownStatusAction.Request(nodeToStopId) |
| 176 | + ).get(); |
| 177 | + |
| 178 | + assertThat(getResp.getShutdownStatuses().get(0).migrationStatus().getStatus(), equalTo(COMPLETE)); |
| 179 | + }); |
| 180 | + } |
| 181 | + |
| 182 | + private void indexRandomData() throws Exception { |
| 183 | + int numDocs = scaledRandomIntBetween(100, 1000); |
| 184 | + IndexRequestBuilder[] builders = new IndexRequestBuilder[numDocs]; |
| 185 | + for (int i = 0; i < builders.length; i++) { |
| 186 | + builders[i] = client().prepareIndex("test", "_doc").setSource("field", "value"); |
| 187 | + } |
| 188 | + indexRandom(true, builders); |
| 189 | + } |
| 190 | + |
| 191 | + private String findIdOfNodeWithPrimaryShard(String indexName) { |
| 192 | + ClusterState state = client().admin().cluster().prepareState().get().getState(); |
| 193 | + List<ShardRouting> startedShards = state.routingTable().shardsWithState(ShardRoutingState.STARTED); |
| 194 | + return startedShards.stream() |
| 195 | + .filter(ShardRouting::primary) |
| 196 | + .filter(shardRouting -> indexName.equals(shardRouting.index().getName())) |
| 197 | + .map(ShardRouting::currentNodeId) |
| 198 | + .findFirst() |
| 199 | + .orElseThrow( |
| 200 | + () -> new AssertionError( |
| 201 | + new ParameterizedMessage( |
| 202 | + "could not find a primary shard of index [{}] in list of started shards [{}]", |
| 203 | + indexName, |
| 204 | + startedShards |
| 205 | + ) |
| 206 | + ) |
| 207 | + ); |
| 208 | + } |
| 209 | + |
137 | 210 | private String getNodeId(String nodeName) throws Exception {
|
138 | 211 | NodesInfoResponse nodes = client().admin().cluster().prepareNodesInfo().clear().get();
|
139 | 212 | return nodes.getNodes()
|
|
0 commit comments