diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index db4f5652830..40ef24c0a2c 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -1677,6 +1677,9 @@ Release 2.7.3 - UNRELEASED HDFS-9516. Truncate file fails with data dirs on multiple disks. (Plamen Jeliazkov via shv) + HDFS-9533. seen_txid in the shared edits directory is modified during + bootstrapping (kihwal) + Release 2.7.2 - UNRELEASED INCOMPATIBLE CHANGES diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java index 9b63e728b1c..ed36b27d2e1 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java @@ -482,8 +482,24 @@ long getMostRecentCheckpointTime() { * @param txid the txid that has been reached */ public void writeTransactionIdFileToStorage(long txid) { + writeTransactionIdFileToStorage(txid, null); + } + + /** + * Write a small file in all available storage directories that + * indicates that the namespace has reached some given transaction ID. + * + * This is used when the image is loaded to avoid accidental rollbacks + * in the case where an edit log is fully deleted but there is no + * checkpoint. See TestNameEditsConfigs.testNameEditsConfigsFailure() + * @param txid the txid that has been reached + * @param type the type of directory + */ + public void writeTransactionIdFileToStorage(long txid, + NameNodeDirType type) { // Write txid marker in all storage directories - for (StorageDirectory sd : storageDirs) { + for (Iterator it = dirIterator(type); it.hasNext();) { + StorageDirectory sd = it.next(); try { writeTransactionIdFile(sd, txid); } catch(IOException e) { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/BootstrapStandby.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/BootstrapStandby.java index 031170822c6..213141c2183 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/BootstrapStandby.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/BootstrapStandby.java @@ -52,6 +52,7 @@ import org.apache.hadoop.hdfs.server.namenode.FSNamesystem; import org.apache.hadoop.hdfs.server.namenode.NNStorage; import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeFile; +import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeDirType; import org.apache.hadoop.hdfs.server.namenode.NNUpgradeUtil; import org.apache.hadoop.hdfs.server.namenode.NameNode; import org.apache.hadoop.hdfs.server.namenode.TransferFsImage; @@ -313,13 +314,14 @@ private int downloadImage(NNStorage storage, NamenodeProtocol proxy) return ERR_CODE_LOGS_UNAVAILABLE; } - image.getStorage().writeTransactionIdFileToStorage(curTxId); - // Download that checkpoint into our storage directories. MD5Hash hash = TransferFsImage.downloadImageToStorage( otherHttpAddr, imageTxId, storage, true, true); image.saveDigestAndRenameCheckpointImage(NameNodeFile.IMAGE, imageTxId, hash); + + // Write seen_txid to the formatted image directories. + storage.writeTransactionIdFileToStorage(imageTxId, NameNodeDirType.IMAGE); } catch (IOException ioe) { throw ioe; } finally { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java index 7ee6f12c42b..fafeae2fc90 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java @@ -568,4 +568,11 @@ public static void assertNNFilesMatch(MiniDFSCluster cluster) throws Exception { FSImageTestUtil.assertParallelFilesAreIdentical(curDirs, ignoredFiles); } + + public static long getStorageTxId(NameNode node, URI storageUri) + throws IOException { + StorageDirectory sDir = getFSImage(node).getStorage(). + getStorageDirectory(storageUri); + return NNStorage.readTransactionIdFile(sDir); + } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java index 6bcfb496cc6..9dfad12040d 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java @@ -135,6 +135,13 @@ public void testDownloadingLaterCheckpoint() throws Exception { .getFSImage().getMostRecentCheckpointTxId(); assertEquals(6, expectedCheckpointTxId); + // advance the current txid + cluster.getFileSystem(0).create(new Path("/test_txid"), (short)1).close(); + + // obtain the content of seen_txid + URI editsUri = cluster.getSharedEditsDir(0, 1); + long seen_txid_shared = FSImageTestUtil.getStorageTxId(nn0, editsUri); + int rc = BootstrapStandby.run( new String[]{"-force"}, cluster.getConfiguration(1)); @@ -145,6 +152,10 @@ public void testDownloadingLaterCheckpoint() throws Exception { ImmutableList.of((int)expectedCheckpointTxId)); FSImageTestUtil.assertNNFilesMatch(cluster); + // Make sure the seen_txid was not modified by the standby + assertEquals(seen_txid_shared, + FSImageTestUtil.getStorageTxId(nn0, editsUri)); + // We should now be able to start the standby successfully. cluster.restartNameNode(1); }