diff --git a/server/src/test/java/org/elasticsearch/index/shard/ShardPathTests.java b/server/src/test/java/org/elasticsearch/index/shard/ShardPathTests.java index 2afcb8ed0cf1f..3630c00935b27 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/ShardPathTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/ShardPathTests.java @@ -7,16 +7,29 @@ */ package org.elasticsearch.index.shard; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.routing.AllocationId; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.env.ShardLock; import org.elasticsearch.index.Index; +import org.elasticsearch.index.IndexSettings; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.IndexSettingsModule; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.TimeUnit; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -128,4 +141,113 @@ public void testGetRootPaths() throws IOException { } } + public void testLoadShardMultiPath() throws IOException { + try (NodeEnvironment env = newNodeEnvironment(Settings.builder().build())) { + ShardId shardId = new ShardId("foo", "0xDEADBEEF", 0); + Path[] paths = new Path[4]; + for (int i = 0; i < paths.length; i++) { + paths[i] = createTempDir(); + } + Path[] envPaths = env.availableShardPaths(shardId); + paths[between(0, paths.length - 1)] = envPaths[0]; + ShardStateMetadata.FORMAT.writeAndCleanup( + new ShardStateMetadata(true, "0xDEADBEEF", AllocationId.newInitializing()), envPaths); + + // Doesn't matter which of the paths contains shard data, we should be able to load it + ShardPath shardPath = ShardPath.loadShardPath(logger, shardId, "", paths, env.sharedDataPath()); + assertNotNull(shardPath.getDataPath()); + assertEquals(envPaths[0], shardPath.getDataPath()); + assertEquals("0xDEADBEEF", shardPath.getShardId().getIndex().getUUID()); + assertEquals("foo", shardPath.getShardId().getIndexName()); + assertEquals(envPaths[0].resolve("translog"), shardPath.resolveTranslog()); + assertEquals(envPaths[0].resolve("index"), shardPath.resolveIndex()); + + // Ensure we validate all paths regardless of successful load + Path badPath = createTempDir(); + ShardStateMetadata.FORMAT.writeAndCleanup( + new ShardStateMetadata(true, "0xDEADF00D", AllocationId.newInitializing()), badPath); + + Path[] extendedPaths = Arrays.copyOf(paths, paths.length + 1); + extendedPaths[paths.length] = badPath; + Exception e = expectThrows(IllegalStateException.class, () -> + ShardPath.loadShardPath(logger, shardId, "", extendedPaths, env.sharedDataPath())); + assertThat(e.getMessage(), + is("[foo][0] index UUID in shard state was: 0xDEADF00D expected: 0xDEADBEEF on shard path: " + badPath)); + } + } + + public void testLoadEmptyShards() throws IOException { + ShardId shardId = new ShardId("foo", "0xDEADBEEF", 0); + Path[] paths = new Path[4]; + for (int i = 0; i < paths.length; i++) { + paths[i] = createTempDir(); + } + + assertNull(ShardPath.loadShardPath(logger, shardId, "", paths, createTempDir())); + } + + public void testShardPathSelection() throws IOException { + try (NodeEnvironment env = newNodeEnvironment(Settings.builder().build())) { + NodeEnvironment.NodePath path = env.nodePaths()[0]; + assertEquals(path, ShardPath.getPathWithMostFreeSpace(env)); + ShardId shardId = new ShardId("foo", "0xDEADBEEF", 0); + + Settings indexSettings = Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT).build(); + IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAlphaOfLengthBetween(1, 10), indexSettings); + + ShardPath shardPath = ShardPath.selectNewPathForShard(env, shardId, idxSettings, 1L, new HashMap<>()); + assertNotNull(shardPath.getDataPath()); + assertEquals(path.indicesPath.resolve("0xDEADBEEF").resolve("0"), shardPath.getDataPath()); + assertEquals("0xDEADBEEF", shardPath.getShardId().getIndex().getUUID()); + assertEquals("foo", shardPath.getShardId().getIndexName()); + assertFalse(shardPath.isCustomDataPath()); + } + } + + public void testDeleteLeftoverShardDirs() throws IOException { + try (NodeEnvironment env = newNodeEnvironment(Settings.builder().build())) { + ShardId shardId = new ShardId("foo", "0xDEADBEEF", 0); + ShardLock lock = env.shardLock(shardId, "starting shard", TimeUnit.SECONDS.toMillis(5)); + try { + Path[] envPaths = env.availableShardPaths(shardId); + ShardStateMetadata.FORMAT.writeAndCleanup( + new ShardStateMetadata(true, "0xDEADBEEF", AllocationId.newInitializing()), envPaths); + + Path badPath = createTempDir(); + // Cause a failure by writing metadata with UUID that doesn't match + ShardStateMetadata.FORMAT.writeAndCleanup( + new ShardStateMetadata(true, "0xDEADF00D", AllocationId.newInitializing()), badPath); + + List temp = new ArrayList<>(Arrays.asList(envPaths)); + temp.add(0, badPath); + Path[] paths = temp.toArray(new Path[0]); + + Exception e = expectThrows(IllegalStateException.class, () -> + ShardPath.loadShardPath(logger, shardId, "", paths, env.sharedDataPath())); + assertThat(e.getMessage(), + is("[foo][0] index UUID in shard state was: 0xDEADF00D expected: 0xDEADBEEF on shard path: " + badPath)); + + Settings indexSettings = Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT).build(); + IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAlphaOfLengthBetween(1, 10), indexSettings); + + for (Path path : envPaths) { + assertTrue(Files.exists(path)); + } + ShardPath.deleteLeftoverShardDirectory(logger, env, lock, idxSettings, shardPaths -> { + List envPathList = Arrays.asList(envPaths); + assertEquals(envPaths.length, shardPaths.length); + for (Path path : shardPaths) { + assertThat(envPathList, contains(path)); + } + }); + for (Path path : envPaths) { + assertFalse(Files.exists(path)); + } + } finally { + IOUtils.closeWhileHandlingException(lock); + } + } + } }