|
19 | 19 |
|
20 | 20 | package org.elasticsearch.action.admin.indices.create; |
21 | 21 |
|
| 22 | +import org.apache.lucene.index.CorruptIndexException; |
22 | 23 | import org.apache.lucene.search.Sort; |
23 | 24 | import org.apache.lucene.search.SortField; |
24 | 25 | import org.apache.lucene.search.SortedSetSelector; |
25 | 26 | import org.apache.lucene.search.SortedSetSortField; |
26 | 27 | import org.elasticsearch.Version; |
27 | 28 | import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteResponse; |
| 29 | +import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; |
28 | 30 | import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; |
29 | | -import org.elasticsearch.action.admin.indices.segments.IndexSegments; |
30 | | -import org.elasticsearch.action.admin.indices.segments.IndexShardSegments; |
31 | | -import org.elasticsearch.action.admin.indices.segments.IndicesSegmentResponse; |
32 | | -import org.elasticsearch.action.admin.indices.segments.ShardSegments; |
33 | 31 | import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; |
| 32 | +import org.elasticsearch.action.index.IndexRequest; |
34 | 33 | import org.elasticsearch.action.support.ActiveShardCount; |
| 34 | +import org.elasticsearch.client.Client; |
35 | 35 | import org.elasticsearch.cluster.ClusterInfoService; |
36 | 36 | import org.elasticsearch.cluster.InternalClusterInfoService; |
| 37 | +import org.elasticsearch.cluster.metadata.IndexMetaData; |
37 | 38 | import org.elasticsearch.cluster.node.DiscoveryNode; |
| 39 | +import org.elasticsearch.cluster.routing.IndexShardRoutingTable; |
| 40 | +import org.elasticsearch.cluster.routing.Murmur3HashFunction; |
38 | 41 | import org.elasticsearch.cluster.routing.RoutingTable; |
| 42 | +import org.elasticsearch.cluster.routing.ShardRouting; |
39 | 43 | import org.elasticsearch.cluster.routing.UnassignedInfo; |
40 | 44 | import org.elasticsearch.common.Priority; |
41 | 45 | import org.elasticsearch.common.collect.ImmutableOpenMap; |
42 | 46 | import org.elasticsearch.common.settings.Settings; |
43 | 47 | import org.elasticsearch.common.xcontent.XContentType; |
44 | | -import org.elasticsearch.index.engine.Segment; |
| 48 | +import org.elasticsearch.index.Index; |
| 49 | +import org.elasticsearch.index.IndexService; |
45 | 50 | import org.elasticsearch.index.query.TermsQueryBuilder; |
| 51 | +import org.elasticsearch.index.shard.IndexShard; |
| 52 | +import org.elasticsearch.index.shard.ShardId; |
| 53 | +import org.elasticsearch.indices.IndicesService; |
46 | 54 | import org.elasticsearch.plugins.Plugin; |
47 | 55 | import org.elasticsearch.test.ESIntegTestCase; |
48 | 56 | import org.elasticsearch.test.InternalSettingsPlugin; |
49 | 57 | import org.elasticsearch.test.VersionUtils; |
50 | 58 |
|
51 | 59 | import java.util.Arrays; |
52 | 60 | import java.util.Collection; |
| 61 | +import java.util.HashSet; |
| 62 | +import java.util.List; |
| 63 | +import java.util.Set; |
| 64 | +import java.util.stream.Collectors; |
| 65 | +import java.util.stream.IntStream; |
53 | 66 |
|
54 | 67 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; |
55 | 68 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; |
| 69 | +import static org.hamcrest.CoreMatchers.not; |
56 | 70 | import static org.hamcrest.Matchers.containsString; |
| 71 | +import static org.hamcrest.Matchers.equalTo; |
| 72 | +import static org.hamcrest.Matchers.greaterThanOrEqualTo; |
57 | 73 |
|
58 | 74 | public class ShrinkIndexIT extends ESIntegTestCase { |
59 | 75 |
|
@@ -135,6 +151,81 @@ public void testCreateShrinkIndexToN() { |
135 | 151 | assertHitCount(client().prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")).get(), 20); |
136 | 152 | } |
137 | 153 |
|
| 154 | + public void testShrinkIndexPrimaryTerm() throws Exception { |
| 155 | + final List<Integer> factors = Arrays.asList(2, 3, 5, 7); |
| 156 | + final List<Integer> numberOfShardsFactors = randomSubsetOf(scaledRandomIntBetween(1, factors.size()), factors); |
| 157 | + final int numberOfShards = numberOfShardsFactors.stream().reduce(1, (x, y) -> x * y); |
| 158 | + final int numberOfTargetShards = randomSubsetOf(numberOfShardsFactors).stream().reduce(1, (x, y) -> x * y); |
| 159 | + internalCluster().ensureAtLeastNumDataNodes(2); |
| 160 | + prepareCreate("source").setSettings(Settings.builder().put(indexSettings()).put("number_of_shards", numberOfShards)).get(); |
| 161 | + |
| 162 | + final ImmutableOpenMap<String, DiscoveryNode> dataNodes = |
| 163 | + client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); |
| 164 | + assertThat(dataNodes.size(), greaterThanOrEqualTo(2)); |
| 165 | + final DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(DiscoveryNode.class); |
| 166 | + final String mergeNode = discoveryNodes[0].getName(); |
| 167 | + ensureGreen(); |
| 168 | + |
| 169 | + // fail random primary shards to force primary terms to increase |
| 170 | + final Index source = resolveIndex("source"); |
| 171 | + final int iterations = scaledRandomIntBetween(0, 16); |
| 172 | + for (int i = 0; i < iterations; i++) { |
| 173 | + final String node = randomSubsetOf(1, internalCluster().nodesInclude("source")).get(0); |
| 174 | + final IndicesService indexServices = internalCluster().getInstance(IndicesService.class, node); |
| 175 | + final IndexService indexShards = indexServices.indexServiceSafe(source); |
| 176 | + for (final Integer shardId : indexShards.shardIds()) { |
| 177 | + final IndexShard shard = indexShards.getShard(shardId); |
| 178 | + if (shard.routingEntry().primary() && randomBoolean()) { |
| 179 | + disableAllocation("source"); |
| 180 | + shard.failShard("test", new Exception("test")); |
| 181 | + // this can not succeed until the shard is failed and a replica is promoted |
| 182 | + int id = 0; |
| 183 | + while (true) { |
| 184 | + // find an ID that routes to the right shard, we will only index to the shard that saw a primary failure |
| 185 | + final String s = Integer.toString(id); |
| 186 | + final int hash = Math.floorMod(Murmur3HashFunction.hash(s), numberOfShards); |
| 187 | + if (hash == shardId) { |
| 188 | + final IndexRequest request = |
| 189 | + new IndexRequest("source", "type", s).source("{ \"f\": \"" + s + "\"}", XContentType.JSON); |
| 190 | + client().index(request).get(); |
| 191 | + break; |
| 192 | + } else { |
| 193 | + id++; |
| 194 | + } |
| 195 | + } |
| 196 | + enableAllocation("source"); |
| 197 | + ensureGreen(); |
| 198 | + } |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + // relocate all shards to one node such that we can merge it. |
| 203 | + final Settings.Builder prepareShrinkSettings = |
| 204 | + Settings.builder().put("index.routing.allocation.require._name", mergeNode).put("index.blocks.write", true); |
| 205 | + client().admin().indices().prepareUpdateSettings("source").setSettings(prepareShrinkSettings).get(); |
| 206 | + ensureGreen(); |
| 207 | + |
| 208 | + final IndexMetaData indexMetaData = indexMetaData(client(), "source"); |
| 209 | + final long beforeShrinkPrimaryTerm = IntStream.range(0, numberOfShards).mapToLong(indexMetaData::primaryTerm).max().getAsLong(); |
| 210 | + |
| 211 | + // now merge source into target |
| 212 | + final Settings shrinkSettings = |
| 213 | + Settings.builder().put("index.number_of_replicas", 0).put("index.number_of_shards", numberOfTargetShards).build(); |
| 214 | + assertAcked(client().admin().indices().prepareShrinkIndex("source", "target").setSettings(shrinkSettings).get()); |
| 215 | + |
| 216 | + ensureGreen(); |
| 217 | + |
| 218 | + final IndexMetaData afterShrinkIndexMetaData = indexMetaData(client(), "target"); |
| 219 | + for (int shardId = 0; shardId < numberOfTargetShards; shardId++) { |
| 220 | + assertThat(afterShrinkIndexMetaData.primaryTerm(shardId), equalTo(beforeShrinkPrimaryTerm + 1)); |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + private static IndexMetaData indexMetaData(final Client client, final String index) { |
| 225 | + final ClusterStateResponse clusterStateResponse = client.admin().cluster().state(new ClusterStateRequest()).actionGet(); |
| 226 | + return clusterStateResponse.getState().metaData().index(index); |
| 227 | + } |
| 228 | + |
138 | 229 | public void testCreateShrinkIndex() { |
139 | 230 | internalCluster().ensureAtLeastNumDataNodes(2); |
140 | 231 | Version version = VersionUtils.randomVersion(random()); |
|
0 commit comments