IndicesServiceTests.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. /*
  2. * Licensed to Elasticsearch under one or more contributor
  3. * license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright
  5. * ownership. Elasticsearch licenses this file to you under
  6. * the Apache License, Version 2.0 (the "License"); you may
  7. * not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.elasticsearch.indices;
  20. import org.apache.lucene.search.similarities.BM25Similarity;
  21. import org.apache.lucene.search.similarities.Similarity;
  22. import org.apache.lucene.store.AlreadyClosedException;
  23. import org.elasticsearch.Version;
  24. import org.elasticsearch.action.ActionListener;
  25. import org.elasticsearch.action.admin.indices.stats.CommonStatsFlags;
  26. import org.elasticsearch.action.admin.indices.stats.IndexShardStats;
  27. import org.elasticsearch.cluster.ClusterName;
  28. import org.elasticsearch.cluster.ClusterState;
  29. import org.elasticsearch.cluster.metadata.IndexGraveyard;
  30. import org.elasticsearch.cluster.metadata.IndexMetaData;
  31. import org.elasticsearch.cluster.metadata.MetaData;
  32. import org.elasticsearch.cluster.node.DiscoveryNode;
  33. import org.elasticsearch.cluster.node.DiscoveryNodes;
  34. import org.elasticsearch.cluster.service.ClusterService;
  35. import org.elasticsearch.cluster.shards.ClusterShardLimitIT;
  36. import org.elasticsearch.common.UUIDs;
  37. import org.elasticsearch.common.collect.ImmutableOpenMap;
  38. import org.elasticsearch.common.io.FileSystemUtils;
  39. import org.elasticsearch.common.settings.Setting;
  40. import org.elasticsearch.common.settings.Settings;
  41. import org.elasticsearch.common.unit.TimeValue;
  42. import org.elasticsearch.env.NodeEnvironment;
  43. import org.elasticsearch.env.ShardLockObtainFailedException;
  44. import org.elasticsearch.gateway.GatewayMetaState;
  45. import org.elasticsearch.gateway.LocalAllocateDangledIndices;
  46. import org.elasticsearch.gateway.MetaStateService;
  47. import org.elasticsearch.index.Index;
  48. import org.elasticsearch.index.IndexModule;
  49. import org.elasticsearch.index.IndexService;
  50. import org.elasticsearch.index.IndexSettings;
  51. import org.elasticsearch.index.engine.Engine;
  52. import org.elasticsearch.index.engine.EngineConfig;
  53. import org.elasticsearch.index.engine.EngineFactory;
  54. import org.elasticsearch.index.engine.InternalEngine;
  55. import org.elasticsearch.index.engine.InternalEngineFactory;
  56. import org.elasticsearch.index.mapper.KeywordFieldMapper;
  57. import org.elasticsearch.index.mapper.Mapper;
  58. import org.elasticsearch.index.mapper.MapperService;
  59. import org.elasticsearch.index.shard.IllegalIndexShardStateException;
  60. import org.elasticsearch.index.shard.IndexShard;
  61. import org.elasticsearch.index.shard.IndexShardState;
  62. import org.elasticsearch.index.shard.ShardId;
  63. import org.elasticsearch.index.shard.ShardPath;
  64. import org.elasticsearch.index.similarity.NonNegativeScoresSimilarity;
  65. import org.elasticsearch.indices.IndicesService.ShardDeletionCheckResult;
  66. import org.elasticsearch.plugins.EnginePlugin;
  67. import org.elasticsearch.plugins.MapperPlugin;
  68. import org.elasticsearch.plugins.Plugin;
  69. import org.elasticsearch.test.ESSingleNodeTestCase;
  70. import org.elasticsearch.test.IndexSettingsModule;
  71. import org.elasticsearch.test.VersionUtils;
  72. import org.elasticsearch.test.hamcrest.RegexMatcher;
  73. import java.io.IOException;
  74. import java.util.ArrayList;
  75. import java.util.Arrays;
  76. import java.util.Collection;
  77. import java.util.Collections;
  78. import java.util.List;
  79. import java.util.Map;
  80. import java.util.Optional;
  81. import java.util.concurrent.CountDownLatch;
  82. import java.util.stream.Collectors;
  83. import java.util.stream.Stream;
  84. import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
  85. import static org.elasticsearch.cluster.shards.ClusterShardLimitIT.ShardCounts.forDataNodeCount;
  86. import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
  87. import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
  88. import static org.hamcrest.Matchers.containsString;
  89. import static org.hamcrest.Matchers.equalTo;
  90. import static org.hamcrest.Matchers.hasToString;
  91. import static org.hamcrest.Matchers.instanceOf;
  92. import static org.hamcrest.Matchers.not;
  93. import static org.mockito.Mockito.mock;
  94. import static org.mockito.Mockito.when;
  95. public class IndicesServiceTests extends ESSingleNodeTestCase {
  96. public IndicesService getIndicesService() {
  97. return getInstanceFromNode(IndicesService.class);
  98. }
  99. public NodeEnvironment getNodeEnvironment() {
  100. return getInstanceFromNode(NodeEnvironment.class);
  101. }
  102. @Override
  103. protected Collection<Class<? extends Plugin>> getPlugins() {
  104. return Stream.concat(
  105. super.getPlugins().stream(),
  106. Stream.of(TestPlugin.class, FooEnginePlugin.class, BarEnginePlugin.class))
  107. .collect(Collectors.toList());
  108. }
  109. public static class FooEnginePlugin extends Plugin implements EnginePlugin {
  110. static class FooEngineFactory implements EngineFactory {
  111. @Override
  112. public Engine newReadWriteEngine(final EngineConfig config) {
  113. return new InternalEngine(config);
  114. }
  115. }
  116. private static final Setting<Boolean> FOO_INDEX_SETTING =
  117. Setting.boolSetting("index.foo_index", false, Setting.Property.IndexScope);
  118. @Override
  119. public List<Setting<?>> getSettings() {
  120. return Collections.singletonList(FOO_INDEX_SETTING);
  121. }
  122. @Override
  123. public Optional<EngineFactory> getEngineFactory(final IndexSettings indexSettings) {
  124. if (FOO_INDEX_SETTING.get(indexSettings.getSettings())) {
  125. return Optional.of(new FooEngineFactory());
  126. } else {
  127. return Optional.empty();
  128. }
  129. }
  130. }
  131. public static class BarEnginePlugin extends Plugin implements EnginePlugin {
  132. static class BarEngineFactory implements EngineFactory {
  133. @Override
  134. public Engine newReadWriteEngine(final EngineConfig config) {
  135. return new InternalEngine(config);
  136. }
  137. }
  138. private static final Setting<Boolean> BAR_INDEX_SETTING =
  139. Setting.boolSetting("index.bar_index", false, Setting.Property.IndexScope);
  140. @Override
  141. public List<Setting<?>> getSettings() {
  142. return Collections.singletonList(BAR_INDEX_SETTING);
  143. }
  144. @Override
  145. public Optional<EngineFactory> getEngineFactory(final IndexSettings indexSettings) {
  146. if (BAR_INDEX_SETTING.get(indexSettings.getSettings())) {
  147. return Optional.of(new BarEngineFactory());
  148. } else {
  149. return Optional.empty();
  150. }
  151. }
  152. }
  153. public static class TestPlugin extends Plugin implements MapperPlugin {
  154. public TestPlugin() {}
  155. @Override
  156. public Map<String, Mapper.TypeParser> getMappers() {
  157. return Collections.singletonMap("fake-mapper", new KeywordFieldMapper.TypeParser());
  158. }
  159. @Override
  160. public void onIndexModule(IndexModule indexModule) {
  161. super.onIndexModule(indexModule);
  162. indexModule.addSimilarity("fake-similarity",
  163. (settings, indexCreatedVersion, scriptService) -> new BM25Similarity());
  164. }
  165. }
  166. @Override
  167. protected boolean resetNodeAfterTest() {
  168. return true;
  169. }
  170. public void testCanDeleteShardContent() {
  171. IndicesService indicesService = getIndicesService();
  172. IndexMetaData meta = IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(
  173. 1).build();
  174. IndexSettings indexSettings = IndexSettingsModule.newIndexSettings("test", meta.getSettings());
  175. ShardId shardId = new ShardId(meta.getIndex(), 0);
  176. assertEquals("no shard location", indicesService.canDeleteShardContent(shardId, indexSettings),
  177. ShardDeletionCheckResult.NO_FOLDER_FOUND);
  178. IndexService test = createIndex("test");
  179. shardId = new ShardId(test.index(), 0);
  180. assertTrue(test.hasShard(0));
  181. assertEquals("shard is allocated", indicesService.canDeleteShardContent(shardId, test.getIndexSettings()),
  182. ShardDeletionCheckResult.STILL_ALLOCATED);
  183. test.removeShard(0, "boom");
  184. assertEquals("shard is removed", indicesService.canDeleteShardContent(shardId, test.getIndexSettings()),
  185. ShardDeletionCheckResult.FOLDER_FOUND_CAN_DELETE);
  186. ShardId notAllocated = new ShardId(test.index(), 100);
  187. assertEquals("shard that was never on this node should NOT be deletable",
  188. indicesService.canDeleteShardContent(notAllocated, test.getIndexSettings()), ShardDeletionCheckResult.NO_FOLDER_FOUND);
  189. }
  190. public void testDeleteIndexStore() throws Exception {
  191. IndicesService indicesService = getIndicesService();
  192. IndexService test = createIndex("test");
  193. ClusterService clusterService = getInstanceFromNode(ClusterService.class);
  194. IndexMetaData firstMetaData = clusterService.state().metaData().index("test");
  195. assertTrue(test.hasShard(0));
  196. ShardPath firstPath = ShardPath.loadShardPath(logger, getNodeEnvironment(), new ShardId(test.index(), 0),
  197. test.getIndexSettings().customDataPath());
  198. expectThrows(IllegalStateException.class, () -> indicesService.deleteIndexStore("boom", firstMetaData));
  199. assertTrue(firstPath.exists());
  200. GatewayMetaState gwMetaState = getInstanceFromNode(GatewayMetaState.class);
  201. MetaData meta = gwMetaState.getMetaData();
  202. assertNotNull(meta);
  203. assertNotNull(meta.index("test"));
  204. assertAcked(client().admin().indices().prepareDelete("test"));
  205. assertFalse(firstPath.exists());
  206. meta = gwMetaState.getMetaData();
  207. assertNotNull(meta);
  208. assertNull(meta.index("test"));
  209. test = createIndex("test");
  210. client().prepareIndex("test").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  211. client().admin().indices().prepareFlush("test").get();
  212. assertHitCount(client().prepareSearch("test").get(), 1);
  213. IndexMetaData secondMetaData = clusterService.state().metaData().index("test");
  214. assertAcked(client().admin().indices().prepareClose("test"));
  215. ShardPath secondPath = ShardPath.loadShardPath(logger, getNodeEnvironment(), new ShardId(test.index(), 0),
  216. test.getIndexSettings().customDataPath());
  217. assertTrue(secondPath.exists());
  218. expectThrows(IllegalStateException.class, () -> indicesService.deleteIndexStore("boom", secondMetaData));
  219. assertTrue(secondPath.exists());
  220. assertAcked(client().admin().indices().prepareOpen("test"));
  221. ensureGreen("test");
  222. }
  223. public void testPendingTasks() throws Exception {
  224. final IndexService indexService = createIndex("test");
  225. final Index index = indexService.index();
  226. final IndexSettings indexSettings = indexService.getIndexSettings();
  227. final IndexShard indexShard = indexService.getShardOrNull(0);
  228. assertNotNull(indexShard);
  229. assertTrue(indexShard.routingEntry().started());
  230. final ShardPath shardPath = indexShard.shardPath();
  231. assertEquals(ShardPath.loadShardPath(logger, getNodeEnvironment(), indexShard.shardId(), indexSettings.customDataPath()),
  232. shardPath);
  233. final IndicesService indicesService = getIndicesService();
  234. expectThrows(ShardLockObtainFailedException.class, () ->
  235. indicesService.processPendingDeletes(index, indexSettings, TimeValue.timeValueMillis(0)));
  236. assertTrue(shardPath.exists());
  237. int numPending = 1;
  238. if (randomBoolean()) {
  239. indicesService.addPendingDelete(indexShard.shardId(), indexSettings);
  240. } else {
  241. if (randomBoolean()) {
  242. numPending++;
  243. indicesService.addPendingDelete(indexShard.shardId(), indexSettings);
  244. }
  245. indicesService.addPendingDelete(index, indexSettings);
  246. }
  247. assertAcked(client().admin().indices().prepareClose("test"));
  248. assertTrue(shardPath.exists());
  249. ensureGreen("test");
  250. assertEquals(indicesService.numPendingDeletes(index), numPending);
  251. assertTrue(indicesService.hasUncompletedPendingDeletes());
  252. expectThrows(ShardLockObtainFailedException.class, () ->
  253. indicesService.processPendingDeletes(index, indexSettings, TimeValue.timeValueMillis(0)));
  254. assertEquals(indicesService.numPendingDeletes(index), numPending);
  255. assertTrue(indicesService.hasUncompletedPendingDeletes());
  256. final boolean hasBogus = randomBoolean();
  257. if (hasBogus) {
  258. indicesService.addPendingDelete(new ShardId(index, 0), indexSettings);
  259. indicesService.addPendingDelete(new ShardId(index, 1), indexSettings);
  260. indicesService.addPendingDelete(new ShardId("bogus", "_na_", 1), indexSettings);
  261. assertEquals(indicesService.numPendingDeletes(index), numPending + 2);
  262. assertTrue(indicesService.hasUncompletedPendingDeletes());
  263. }
  264. assertAcked(client().admin().indices().prepareDelete("test"));
  265. assertBusy(() -> {
  266. try {
  267. indicesService.processPendingDeletes(index, indexSettings, TimeValue.timeValueMillis(0));
  268. assertEquals(indicesService.numPendingDeletes(index), 0);
  269. } catch (final Exception e) {
  270. fail(e.getMessage());
  271. }
  272. });
  273. assertBusy(() -> {
  274. assertThat(indicesService.hasUncompletedPendingDeletes(), equalTo(hasBogus)); // "bogus" index has not been removed
  275. assertFalse(shardPath.exists());
  276. });
  277. }
  278. public void testVerifyIfIndexContentDeleted() throws Exception {
  279. final Index index = new Index("test", UUIDs.randomBase64UUID());
  280. final IndicesService indicesService = getIndicesService();
  281. final NodeEnvironment nodeEnv = getNodeEnvironment();
  282. final MetaStateService metaStateService = getInstanceFromNode(MetaStateService.class);
  283. final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
  284. final Settings idxSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
  285. .put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID())
  286. .build();
  287. final IndexMetaData indexMetaData = new IndexMetaData.Builder(index.getName())
  288. .settings(idxSettings)
  289. .numberOfShards(1)
  290. .numberOfReplicas(0)
  291. .build();
  292. metaStateService.writeIndex("test index being created", indexMetaData);
  293. final MetaData metaData = MetaData.builder(clusterService.state().metaData()).put(indexMetaData, true).build();
  294. final ClusterState csWithIndex = new ClusterState.Builder(clusterService.state()).metaData(metaData).build();
  295. try {
  296. indicesService.verifyIndexIsDeleted(index, csWithIndex);
  297. fail("Should not be able to delete index contents when the index is part of the cluster state.");
  298. } catch (IllegalStateException e) {
  299. assertThat(e.getMessage(), containsString("Cannot delete index"));
  300. }
  301. final ClusterState withoutIndex = new ClusterState.Builder(csWithIndex)
  302. .metaData(MetaData.builder(csWithIndex.metaData()).remove(index.getName()))
  303. .build();
  304. indicesService.verifyIndexIsDeleted(index, withoutIndex);
  305. assertFalse("index files should be deleted", FileSystemUtils.exists(nodeEnv.indexPaths(index)));
  306. }
  307. public void testDanglingIndicesWithAliasConflict() throws Exception {
  308. final String indexName = "test-idx1";
  309. final String alias = "test-alias";
  310. final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
  311. createIndex(indexName);
  312. // create the alias for the index
  313. client().admin().indices().prepareAliases().addAlias(indexName, alias).get();
  314. final ClusterState originalState = clusterService.state();
  315. // try to import a dangling index with the same name as the alias, it should fail
  316. final LocalAllocateDangledIndices dangling = getInstanceFromNode(LocalAllocateDangledIndices.class);
  317. final Settings idxSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
  318. .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
  319. .build();
  320. final IndexMetaData indexMetaData = new IndexMetaData.Builder(alias)
  321. .settings(idxSettings)
  322. .numberOfShards(1)
  323. .numberOfReplicas(0)
  324. .build();
  325. CountDownLatch latch = new CountDownLatch(1);
  326. dangling.allocateDangled(Arrays.asList(indexMetaData), ActionListener.wrap(latch::countDown));
  327. latch.await();
  328. assertThat(clusterService.state(), equalTo(originalState));
  329. // remove the alias
  330. client().admin().indices().prepareAliases().removeAlias(indexName, alias).get();
  331. // now try importing a dangling index with the same name as the alias, it should succeed.
  332. latch = new CountDownLatch(1);
  333. dangling.allocateDangled(Arrays.asList(indexMetaData), ActionListener.wrap(latch::countDown));
  334. latch.await();
  335. assertThat(clusterService.state(), not(originalState));
  336. assertNotNull(clusterService.state().getMetaData().index(alias));
  337. }
  338. public void testDanglingIndicesWithLaterVersion() throws Exception {
  339. final String indexNameLater = "test-idxnewer";
  340. final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
  341. final ClusterState originalState = clusterService.state();
  342. //import an index with minor version incremented by one over cluster master version, it should be ignored
  343. final LocalAllocateDangledIndices dangling = getInstanceFromNode(LocalAllocateDangledIndices.class);
  344. final Settings idxSettingsLater = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED,
  345. Version.fromId(Version.CURRENT.id + 10000))
  346. .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
  347. .build();
  348. final IndexMetaData indexMetaDataLater = new IndexMetaData.Builder(indexNameLater)
  349. .settings(idxSettingsLater)
  350. .numberOfShards(1)
  351. .numberOfReplicas(0)
  352. .build();
  353. CountDownLatch latch = new CountDownLatch(1);
  354. dangling.allocateDangled(Arrays.asList(indexMetaDataLater), ActionListener.wrap(latch::countDown));
  355. latch.await();
  356. assertThat(clusterService.state(), equalTo(originalState));
  357. }
  358. /**
  359. * This test checks an edge case where, if a node had an index (lets call it A with UUID 1), then
  360. * deleted it (so a tombstone entry for A will exist in the cluster state), then created
  361. * a new index A with UUID 2, then shutdown, when the node comes back online, it will look at the
  362. * tombstones for deletions, and it should proceed with trying to delete A with UUID 1 and not
  363. * throw any errors that the index still exists in the cluster state. This is a case of ensuring
  364. * that tombstones that have the same name as current valid indices don't cause confusion by
  365. * trying to delete an index that exists.
  366. * See https://github.com/elastic/elasticsearch/issues/18054
  367. */
  368. public void testIndexAndTombstoneWithSameNameOnStartup() throws Exception {
  369. final String indexName = "test";
  370. final Index index = new Index(indexName, UUIDs.randomBase64UUID());
  371. final IndicesService indicesService = getIndicesService();
  372. final Settings idxSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
  373. .put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID())
  374. .build();
  375. final IndexMetaData indexMetaData = new IndexMetaData.Builder(index.getName())
  376. .settings(idxSettings)
  377. .numberOfShards(1)
  378. .numberOfReplicas(0)
  379. .build();
  380. final Index tombstonedIndex = new Index(indexName, UUIDs.randomBase64UUID());
  381. final IndexGraveyard graveyard = IndexGraveyard.builder().addTombstone(tombstonedIndex).build();
  382. final MetaData metaData = MetaData.builder().put(indexMetaData, true).indexGraveyard(graveyard).build();
  383. final ClusterState clusterState = new ClusterState.Builder(new ClusterName("testCluster")).metaData(metaData).build();
  384. // if all goes well, this won't throw an exception, otherwise, it will throw an IllegalStateException
  385. indicesService.verifyIndexIsDeleted(tombstonedIndex, clusterState);
  386. }
  387. /**
  388. * Tests that teh {@link MapperService} created by {@link IndicesService#createIndexMapperService(IndexMetaData)} contains
  389. * custom types and similarities registered by plugins
  390. */
  391. public void testStandAloneMapperServiceWithPlugins() throws IOException {
  392. final String indexName = "test";
  393. final Index index = new Index(indexName, UUIDs.randomBase64UUID());
  394. final IndicesService indicesService = getIndicesService();
  395. final Settings idxSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
  396. .put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID())
  397. .put(IndexModule.SIMILARITY_SETTINGS_PREFIX + ".test.type", "fake-similarity")
  398. .build();
  399. final IndexMetaData indexMetaData = new IndexMetaData.Builder(index.getName())
  400. .settings(idxSettings)
  401. .numberOfShards(1)
  402. .numberOfReplicas(0)
  403. .build();
  404. MapperService mapperService = indicesService.createIndexMapperService(indexMetaData);
  405. assertNotNull(mapperService.documentMapperParser().parserContext().typeParser("fake-mapper"));
  406. Similarity sim = mapperService.documentMapperParser().parserContext().getSimilarity("test").get();
  407. assertThat(sim, instanceOf(NonNegativeScoresSimilarity.class));
  408. sim = ((NonNegativeScoresSimilarity) sim).getDelegate();
  409. assertThat(sim, instanceOf(BM25Similarity.class));
  410. }
  411. public void testStatsByShardDoesNotDieFromExpectedExceptions() {
  412. final int shardCount = randomIntBetween(2, 5);
  413. final int failedShardId = randomIntBetween(0, shardCount - 1);
  414. final Index index = new Index("test-index", "abc123");
  415. // the shard that is going to fail
  416. final ShardId shardId = new ShardId(index, failedShardId);
  417. final List<IndexShard> shards = new ArrayList<>(shardCount);
  418. final List<IndexShardStats> shardStats = new ArrayList<>(shardCount - 1);
  419. final IndexShardState state = randomFrom(IndexShardState.values());
  420. final String message = "TEST - expected";
  421. final RuntimeException expectedException =
  422. randomFrom(new IllegalIndexShardStateException(shardId, state, message), new AlreadyClosedException(message));
  423. // this allows us to control the indices that exist
  424. final IndicesService mockIndicesService = mock(IndicesService.class);
  425. final IndexService indexService = mock(IndexService.class);
  426. // generate fake shards and their responses
  427. for (int i = 0; i < shardCount; ++i) {
  428. final IndexShard shard = mock(IndexShard.class);
  429. shards.add(shard);
  430. if (failedShardId != i) {
  431. final IndexShardStats successfulShardStats = mock(IndexShardStats.class);
  432. shardStats.add(successfulShardStats);
  433. when(mockIndicesService.indexShardStats(mockIndicesService, shard, CommonStatsFlags.ALL)).thenReturn(successfulShardStats);
  434. } else {
  435. when(mockIndicesService.indexShardStats(mockIndicesService, shard, CommonStatsFlags.ALL)).thenThrow(expectedException);
  436. }
  437. }
  438. when(mockIndicesService.iterator()).thenReturn(Collections.singleton(indexService).iterator());
  439. when(indexService.iterator()).thenReturn(shards.iterator());
  440. when(indexService.index()).thenReturn(index);
  441. // real one, which has a logger defined
  442. final IndicesService indicesService = getIndicesService();
  443. final Map<Index, List<IndexShardStats>> indexStats = indicesService.statsByShard(mockIndicesService, CommonStatsFlags.ALL);
  444. assertThat(indexStats.isEmpty(), equalTo(false));
  445. assertThat("index not defined", indexStats.containsKey(index), equalTo(true));
  446. assertThat("unexpected shard stats", indexStats.get(index), equalTo(shardStats));
  447. }
  448. public void testIsMetaDataField() {
  449. IndicesService indicesService = getIndicesService();
  450. final Version randVersion = VersionUtils.randomIndexCompatibleVersion(random());
  451. assertFalse(indicesService.isMetaDataField(randVersion, randomAlphaOfLengthBetween(10, 15)));
  452. for (String builtIn : IndicesModule.getBuiltInMetaDataFields()) {
  453. assertTrue(indicesService.isMetaDataField(randVersion, builtIn));
  454. }
  455. }
  456. public void testGetEngineFactory() throws IOException {
  457. final IndicesService indicesService = getIndicesService();
  458. final Boolean[] values = new Boolean[] { true, false, null };
  459. for (final Boolean value : values) {
  460. final String indexName = "foo-" + value;
  461. final Index index = new Index(indexName, UUIDs.randomBase64UUID());
  462. final Settings.Builder builder = Settings.builder()
  463. .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
  464. .put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID());
  465. if (value != null) {
  466. builder.put(FooEnginePlugin.FOO_INDEX_SETTING.getKey(), value);
  467. }
  468. final IndexMetaData indexMetaData = new IndexMetaData.Builder(index.getName())
  469. .settings(builder.build())
  470. .numberOfShards(1)
  471. .numberOfReplicas(0)
  472. .build();
  473. final IndexService indexService = indicesService.createIndex(indexMetaData, Collections.emptyList(), false);
  474. if (value != null && value) {
  475. assertThat(indexService.getEngineFactory(), instanceOf(FooEnginePlugin.FooEngineFactory.class));
  476. } else {
  477. assertThat(indexService.getEngineFactory(), instanceOf(InternalEngineFactory.class));
  478. }
  479. }
  480. }
  481. public void testConflictingEngineFactories() {
  482. final String indexName = "foobar";
  483. final Index index = new Index(indexName, UUIDs.randomBase64UUID());
  484. final Settings settings = Settings.builder()
  485. .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
  486. .put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID())
  487. .put(FooEnginePlugin.FOO_INDEX_SETTING.getKey(), true)
  488. .put(BarEnginePlugin.BAR_INDEX_SETTING.getKey(), true)
  489. .build();
  490. final IndexMetaData indexMetaData = new IndexMetaData.Builder(index.getName())
  491. .settings(settings)
  492. .numberOfShards(1)
  493. .numberOfReplicas(0)
  494. .build();
  495. final IndicesService indicesService = getIndicesService();
  496. final IllegalStateException e =
  497. expectThrows(IllegalStateException.class, () -> indicesService.createIndex(indexMetaData, Collections.emptyList(), false));
  498. final String pattern =
  499. ".*multiple engine factories provided for \\[foobar/.*\\]: \\[.*FooEngineFactory\\],\\[.*BarEngineFactory\\].*";
  500. assertThat(e, hasToString(new RegexMatcher(pattern)));
  501. }
  502. public void testOverShardLimit() {
  503. int nodesInCluster = randomIntBetween(1,90);
  504. ClusterShardLimitIT.ShardCounts counts = forDataNodeCount(nodesInCluster);
  505. Settings clusterSettings = Settings.builder()
  506. .put(MetaData.SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey(), counts.getShardsPerNode())
  507. .build();
  508. ClusterState state = createClusterForShardLimitTest(nodesInCluster, counts.getFirstIndexShards(), counts.getFirstIndexReplicas(),
  509. clusterSettings);
  510. int shardsToAdd = counts.getFailingIndexShards() * (1 + counts.getFailingIndexReplicas());
  511. Optional<String> errorMessage = IndicesService.checkShardLimit(shardsToAdd, state);
  512. int totalShards = counts.getFailingIndexShards() * (1 + counts.getFailingIndexReplicas());
  513. int currentShards = counts.getFirstIndexShards() * (1 + counts.getFirstIndexReplicas());
  514. int maxShards = counts.getShardsPerNode() * nodesInCluster;
  515. assertTrue(errorMessage.isPresent());
  516. assertEquals("this action would add [" + totalShards + "] total shards, but this cluster currently has [" + currentShards
  517. + "]/[" + maxShards + "] maximum shards open", errorMessage.get());
  518. }
  519. public void testUnderShardLimit() {
  520. int nodesInCluster = randomIntBetween(2,90);
  521. // Calculate the counts for a cluster 1 node smaller than we have to ensure we have headroom
  522. ClusterShardLimitIT.ShardCounts counts = forDataNodeCount(nodesInCluster - 1);
  523. Settings clusterSettings = Settings.builder()
  524. .put(MetaData.SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey(), counts.getShardsPerNode())
  525. .build();
  526. ClusterState state = createClusterForShardLimitTest(nodesInCluster, counts.getFirstIndexShards(), counts.getFirstIndexReplicas(),
  527. clusterSettings);
  528. int existingShards = counts.getFirstIndexShards() * (1 + counts.getFirstIndexReplicas());
  529. int shardsToAdd = randomIntBetween(1, (counts.getShardsPerNode() * nodesInCluster) - existingShards);
  530. Optional<String> errorMessage = IndicesService.checkShardLimit(shardsToAdd, state);
  531. assertFalse(errorMessage.isPresent());
  532. }
  533. public static ClusterState createClusterForShardLimitTest(int nodesInCluster, int shardsInIndex, int replicas,
  534. Settings clusterSettings) {
  535. ImmutableOpenMap.Builder<String, DiscoveryNode> dataNodes = ImmutableOpenMap.builder();
  536. for (int i = 0; i < nodesInCluster; i++) {
  537. dataNodes.put(randomAlphaOfLengthBetween(5,15), mock(DiscoveryNode.class));
  538. }
  539. DiscoveryNodes nodes = mock(DiscoveryNodes.class);
  540. when(nodes.getDataNodes()).thenReturn(dataNodes.build());
  541. IndexMetaData.Builder indexMetaData = IndexMetaData.builder(randomAlphaOfLengthBetween(5, 15))
  542. .settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT))
  543. .creationDate(randomLong())
  544. .numberOfShards(shardsInIndex)
  545. .numberOfReplicas(replicas);
  546. MetaData.Builder metaData = MetaData.builder().put(indexMetaData);
  547. if (randomBoolean()) {
  548. metaData.transientSettings(clusterSettings);
  549. } else {
  550. metaData.persistentSettings(clusterSettings);
  551. }
  552. return ClusterState.builder(ClusterName.DEFAULT)
  553. .metaData(metaData)
  554. .nodes(nodes)
  555. .build();
  556. }
  557. }