diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index 523353b652c..93a5dccbb83 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -237,6 +237,9 @@ Release 2.6.0 - UNRELEASED HDFS-7138. Fix hftp to work with encryption. (clamb via wang) + HDFS-7118. Improve diagnostics on storage directory rename operations by + using NativeIO#renameTo in Storage#rename. (cnauroth) + OPTIMIZATIONS HDFS-6690. Deduplicate xattr names in memory. (wang) diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java index 73ab8372a34..0661026b6f9 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java @@ -36,6 +36,8 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.NodeType; import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.StartupOption; +import org.apache.hadoop.io.nativeio.NativeIO; +import org.apache.hadoop.io.nativeio.NativeIOException; import org.apache.hadoop.util.ToolRunner; import org.apache.hadoop.util.VersionInfo; @@ -985,9 +987,13 @@ public static void writeProperties(File to, StorageDirectory sd, } public static void rename(File from, File to) throws IOException { - if (!from.renameTo(to)) - throw new IOException("Failed to rename " - + from.getCanonicalPath() + " to " + to.getCanonicalPath()); + try { + NativeIO.renameTo(from, to); + } catch (NativeIOException e) { + throw new IOException("Failed to rename " + from.getCanonicalPath() + + " to " + to.getCanonicalPath() + " due to failure in native rename. " + + e.toString()); + } } /** diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileJournalManager.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileJournalManager.java index 1d77622410e..eb20df819f0 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileJournalManager.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileJournalManager.java @@ -21,6 +21,7 @@ import static org.apache.hadoop.hdfs.server.namenode.TestEditLog.TXNS_PER_ROLL; import static org.apache.hadoop.hdfs.server.namenode.TestEditLog.setupEdits; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; @@ -42,8 +43,11 @@ import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeDirType; import org.apache.hadoop.hdfs.server.namenode.TestEditLog.AbortSpec; import org.apache.hadoop.io.IOUtils; +import org.apache.hadoop.util.NativeCodeLoader; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; @@ -60,6 +64,9 @@ public class TestFileJournalManager { EditLogFileOutputStream.setShouldSkipFsyncForTesting(true); } + @Rule + public ExpectedException exception = ExpectedException.none(); + @Before public void setUp() { conf = new Configuration(); @@ -473,6 +480,36 @@ public void testExcludeInProgressStreams() throws CorruptionException, } } + /** + * Tests that internal renames are done using native code on platforms that + * have it. The native rename includes more detailed information about the + * failure, which can be useful for troubleshooting. + */ + @Test + public void testDoPreUpgradeIOError() throws IOException { + File storageDir = new File(TestEditLog.TEST_DIR, "preupgradeioerror"); + List editUris = Collections.singletonList(storageDir.toURI()); + NNStorage storage = setupEdits(editUris, 5); + StorageDirectory sd = storage.dirIterator(NameNodeDirType.EDITS).next(); + assertNotNull(sd); + // Change storage directory so that renaming current to previous.tmp fails. + FileUtil.setWritable(storageDir, false); + FileJournalManager jm = null; + try { + jm = new FileJournalManager(conf, sd, storage); + exception.expect(IOException.class); + if (NativeCodeLoader.isNativeCodeLoaded()) { + exception.expectMessage("failure in native rename"); + } + jm.doPreUpgrade(); + } finally { + IOUtils.cleanup(LOG, jm); + // Restore permissions on storage directory and make sure we can delete. + FileUtil.setWritable(storageDir, true); + FileUtil.fullyDelete(storageDir); + } + } + private static String getLogsAsString( FileJournalManager fjm, long firstTxId) throws IOException { return Joiner.on(",").join(fjm.getRemoteEditLogs(firstTxId, false));