| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hdfs; |
| |
| import java.io.File; |
| import java.util.List; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.hdfs.protocol.HdfsConstants.RollingUpgradeAction; |
| import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction; |
| import org.apache.hadoop.hdfs.protocol.RollingUpgradeInfo; |
| import org.apache.hadoop.hdfs.qjournal.MiniJournalCluster; |
| import org.apache.hadoop.hdfs.qjournal.MiniQJMHACluster; |
| import org.apache.hadoop.hdfs.server.namenode.INode; |
| import org.apache.hadoop.hdfs.server.namenode.NNStorage; |
| import org.apache.hadoop.hdfs.server.namenode.NameNode; |
| import org.apache.hadoop.hdfs.tools.DFSAdmin; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| /** |
| * This class tests rollback for rolling upgrade. |
| */ |
| public class TestRollingUpgradeRollback { |
| |
| private static final int NUM_JOURNAL_NODES = 3; |
| private static final String JOURNAL_ID = "myjournal"; |
| |
| private static boolean fileExists(List<File> files) { |
| for (File file : files) { |
| if (file.exists()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void checkNNStorage(NNStorage storage, long imageTxId, |
| long trashEndTxId) { |
| List<File> finalizedEdits = storage.getFiles( |
| NNStorage.NameNodeDirType.EDITS, |
| NNStorage.getFinalizedEditsFileName(1, imageTxId)); |
| Assert.assertTrue(fileExists(finalizedEdits)); |
| List<File> inprogressEdits = storage.getFiles( |
| NNStorage.NameNodeDirType.EDITS, |
| NNStorage.getInProgressEditsFileName(imageTxId + 1)); |
| // For rollback case we will have an inprogress file for future transactions |
| Assert.assertTrue(fileExists(inprogressEdits)); |
| if (trashEndTxId > 0) { |
| List<File> trashedEdits = storage.getFiles( |
| NNStorage.NameNodeDirType.EDITS, |
| NNStorage.getFinalizedEditsFileName(imageTxId + 1, trashEndTxId) |
| + ".trash"); |
| Assert.assertTrue(fileExists(trashedEdits)); |
| } |
| String imageFileName = trashEndTxId > 0 ? NNStorage |
| .getImageFileName(imageTxId) : NNStorage |
| .getRollbackImageFileName(imageTxId); |
| List<File> imageFiles = storage.getFiles( |
| NNStorage.NameNodeDirType.IMAGE, imageFileName); |
| Assert.assertTrue(fileExists(imageFiles)); |
| } |
| |
| private void checkJNStorage(File dir, long discardStartTxId, |
| long discardEndTxId) { |
| File finalizedEdits = new File(dir, NNStorage.getFinalizedEditsFileName(1, |
| discardStartTxId - 1)); |
| Assert.assertTrue(finalizedEdits.exists()); |
| File trashEdits = new File(dir, NNStorage.getFinalizedEditsFileName( |
| discardStartTxId, discardEndTxId) + ".trash"); |
| Assert.assertTrue(trashEdits.exists()); |
| } |
| |
| @Test |
| public void testRollbackCommand() throws Exception { |
| final Configuration conf = new HdfsConfiguration(); |
| MiniDFSCluster cluster = null; |
| final Path foo = new Path("/foo"); |
| final Path bar = new Path("/bar"); |
| try { |
| cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build(); |
| cluster.waitActive(); |
| |
| final DistributedFileSystem dfs = cluster.getFileSystem(); |
| final DFSAdmin dfsadmin = new DFSAdmin(conf); |
| dfs.mkdirs(foo); |
| |
| // start rolling upgrade |
| dfs.setSafeMode(SafeModeAction.SAFEMODE_ENTER); |
| Assert.assertEquals(0, |
| dfsadmin.run(new String[] { "-rollingUpgrade", "prepare" })); |
| dfs.setSafeMode(SafeModeAction.SAFEMODE_LEAVE); |
| // create new directory |
| dfs.mkdirs(bar); |
| |
| // check NNStorage |
| NNStorage storage = cluster.getNamesystem().getFSImage().getStorage(); |
| checkNNStorage(storage, 3, -1); // (startSegment, mkdir, endSegment) |
| } finally { |
| if (cluster != null) { |
| cluster.shutdown(); |
| } |
| } |
| |
| NameNode nn = null; |
| try { |
| nn = NameNode.createNameNode(new String[] { "-rollingUpgrade", |
| "rollback" }, conf); |
| // make sure /foo is still there, but /bar is not |
| INode fooNode = nn.getNamesystem().getFSDirectory() |
| .getINode4Write(foo.toString()); |
| Assert.assertNotNull(fooNode); |
| INode barNode = nn.getNamesystem().getFSDirectory() |
| .getINode4Write(bar.toString()); |
| Assert.assertNull(barNode); |
| |
| // check the details of NNStorage |
| NNStorage storage = nn.getNamesystem().getFSImage().getStorage(); |
| // (startSegment, upgrade marker, mkdir, endSegment) |
| checkNNStorage(storage, 3, 7); |
| } finally { |
| if (nn != null) { |
| nn.stop(); |
| nn.join(); |
| } |
| } |
| } |
| |
| @Test |
| public void testRollbackWithQJM() throws Exception { |
| final Configuration conf = new HdfsConfiguration(); |
| MiniJournalCluster mjc = null; |
| MiniDFSCluster cluster = null; |
| final Path foo = new Path("/foo"); |
| final Path bar = new Path("/bar"); |
| |
| try { |
| mjc = new MiniJournalCluster.Builder(conf).numJournalNodes( |
| NUM_JOURNAL_NODES).build(); |
| mjc.waitActive(); |
| conf.set(DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY, mjc |
| .getQuorumJournalURI(JOURNAL_ID).toString()); |
| cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build(); |
| cluster.waitActive(); |
| |
| DistributedFileSystem dfs = cluster.getFileSystem(); |
| final DFSAdmin dfsadmin = new DFSAdmin(conf); |
| dfs.mkdirs(foo); |
| |
| // start rolling upgrade |
| dfs.setSafeMode(SafeModeAction.SAFEMODE_ENTER); |
| Assert.assertEquals(0, |
| dfsadmin.run(new String[] { "-rollingUpgrade", "prepare" })); |
| dfs.setSafeMode(SafeModeAction.SAFEMODE_LEAVE); |
| // create new directory |
| dfs.mkdirs(bar); |
| dfs.close(); |
| |
| // rollback |
| cluster.restartNameNode("-rollingUpgrade", "rollback"); |
| // make sure /foo is still there, but /bar is not |
| dfs = cluster.getFileSystem(); |
| Assert.assertTrue(dfs.exists(foo)); |
| Assert.assertFalse(dfs.exists(bar)); |
| |
| // check storage in JNs |
| for (int i = 0; i < NUM_JOURNAL_NODES; i++) { |
| File dir = mjc.getCurrentDir(0, JOURNAL_ID); |
| // segments:(startSegment, mkdir, endSegment), (startSegment, upgrade |
| // marker, mkdir, endSegment) |
| checkJNStorage(dir, 4, 7); |
| } |
| } finally { |
| if (cluster != null) { |
| cluster.shutdown(); |
| } |
| if (mjc != null) { |
| mjc.shutdown(); |
| } |
| } |
| } |
| |
| /** |
| * Test rollback scenarios where StandbyNameNode does checkpoints during |
| * rolling upgrade. |
| */ |
| @Test |
| public void testRollbackWithHAQJM() throws Exception { |
| final Configuration conf = new HdfsConfiguration(); |
| MiniQJMHACluster cluster = null; |
| final Path foo = new Path("/foo"); |
| final Path bar = new Path("/bar"); |
| |
| try { |
| cluster = new MiniQJMHACluster.Builder(conf).build(); |
| MiniDFSCluster dfsCluster = cluster.getDfsCluster(); |
| dfsCluster.waitActive(); |
| |
| // let NN1 tail editlog every 1s |
| dfsCluster.getConfiguration(1).setInt( |
| DFSConfigKeys.DFS_HA_TAILEDITS_PERIOD_KEY, 1); |
| dfsCluster.restartNameNode(1); |
| |
| dfsCluster.transitionToActive(0); |
| DistributedFileSystem dfs = dfsCluster.getFileSystem(0); |
| dfs.mkdirs(foo); |
| |
| // start rolling upgrade |
| RollingUpgradeInfo info = dfs.rollingUpgrade(RollingUpgradeAction.PREPARE); |
| Assert.assertTrue(info.isStarted()); |
| |
| // create new directory |
| dfs.mkdirs(bar); |
| dfs.close(); |
| |
| dfs = dfsCluster.getFileSystem(0); |
| TestRollingUpgrade.queryForPreparation(dfs); |
| |
| // If the query returns true, both active and the standby NN should have |
| // rollback fsimage ready. |
| Assert.assertTrue(dfsCluster.getNameNode(0).getFSImage() |
| .hasRollbackFSImage()); |
| Assert.assertTrue(dfsCluster.getNameNode(1).getFSImage() |
| .hasRollbackFSImage()); |
| |
| // rollback NN0 |
| dfsCluster.restartNameNode(0, true, "-rollingUpgrade", |
| "rollback"); |
| // shutdown NN1 |
| dfsCluster.shutdownNameNode(1); |
| dfsCluster.transitionToActive(0); |
| |
| // make sure /foo is still there, but /bar is not |
| dfs = dfsCluster.getFileSystem(0); |
| Assert.assertTrue(dfs.exists(foo)); |
| Assert.assertFalse(dfs.exists(bar)); |
| |
| // check the details of NNStorage |
| NNStorage storage = dfsCluster.getNamesystem(0).getFSImage() |
| .getStorage(); |
| // segments:(startSegment, mkdir, start upgrade endSegment), |
| // (startSegment, mkdir, endSegment) |
| checkNNStorage(storage, 4, 7); |
| |
| // check storage in JNs |
| for (int i = 0; i < NUM_JOURNAL_NODES; i++) { |
| File dir = cluster.getJournalCluster().getCurrentDir(0, |
| MiniQJMHACluster.NAMESERVICE); |
| checkJNStorage(dir, 5, 7); |
| } |
| |
| // restart NN0 again to make sure we can start using the new fsimage and |
| // the corresponding md5 checksum |
| dfsCluster.restartNameNode(0); |
| // start the rolling upgrade again to make sure we do not load upgrade |
| // status after the rollback |
| dfsCluster.transitionToActive(0); |
| dfs.rollingUpgrade(RollingUpgradeAction.PREPARE); |
| } finally { |
| if (cluster != null) { |
| cluster.shutdown(); |
| } |
| } |
| } |
| |
| // TODO: rollback could not succeed in all JN |
| } |