You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by sh...@apache.org on 2008/08/30 01:13:13 UTC

svn commit: r690418 [2/2] - in /hadoop/core/trunk: ./ conf/ src/hdfs/org/apache/hadoop/hdfs/server/common/ src/hdfs/org/apache/hadoop/hdfs/server/datanode/ src/hdfs/org/apache/hadoop/hdfs/server/namenode/ src/test/org/apache/hadoop/hdfs/ src/test/org/a...

Added: hadoop/core/trunk/src/test/org/apache/hadoop/hdfs/server/namenode/TestNameEditsConfigs.java
URL: http://svn.apache.org/viewvc/hadoop/core/trunk/src/test/org/apache/hadoop/hdfs/server/namenode/TestNameEditsConfigs.java?rev=690418&view=auto
==============================================================================
--- hadoop/core/trunk/src/test/org/apache/hadoop/hdfs/server/namenode/TestNameEditsConfigs.java (added)
+++ hadoop/core/trunk/src/test/org/apache/hadoop/hdfs/server/namenode/TestNameEditsConfigs.java Fri Aug 29 16:13:12 2008
@@ -0,0 +1,372 @@
+/**
+ * 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.server.namenode;
+
+import junit.framework.TestCase;
+import java.io.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Random;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hdfs.MiniDFSCluster;
+import org.apache.hadoop.hdfs.protocol.FSConstants.StartupOption;
+import org.apache.hadoop.hdfs.server.common.Storage;
+import org.apache.hadoop.hdfs.server.namenode.FSImage.NameNodeFile;
+import org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode.ErrorSimulator;
+import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.fs.Path;
+
+/**
+ * This class tests various combinations of dfs.name.dir 
+ * and dfs.name.edits.dir configurations.
+ */
+public class TestNameEditsConfigs extends TestCase {
+  static final long seed = 0xDEADBEEFL;
+  static final int blockSize = 4096;
+  static final int fileSize = 8192;
+  static final int numDatanodes = 3;
+  short replication = 3;
+
+  private void writeFile(FileSystem fileSys, Path name, int repl)
+    throws IOException {
+    FSDataOutputStream stm = fileSys.create(name, true,
+                                            fileSys.getConf().getInt("io.file.buffer.size", 4096),
+                                            (short)repl, (long)blockSize);
+    byte[] buffer = new byte[fileSize];
+    Random rand = new Random(seed);
+    rand.nextBytes(buffer);
+    stm.write(buffer);
+    stm.close();
+  }
+  
+  
+  private void checkFile(FileSystem fileSys, Path name, int repl)
+    throws IOException {
+    assertTrue(fileSys.exists(name));
+    int replication = fileSys.getFileStatus(name).getReplication();
+    assertEquals("replication for " + name, repl, replication);
+    long size = fileSys.getContentSummary(name).getLength();
+    assertEquals("file size for " + name, size, (long)fileSize);
+  }
+  
+  private void cleanupFile(FileSystem fileSys, Path name)
+    throws IOException {
+    assertTrue(fileSys.exists(name));
+    fileSys.delete(name, true);
+    assertTrue(!fileSys.exists(name));
+  }
+
+  SecondaryNameNode startSecondaryNameNode(Configuration conf
+                                          ) throws IOException {
+    conf.set("dfs.secondary.http.address", "0.0.0.0:0");
+    return new SecondaryNameNode(conf);
+  }
+
+  /**
+   * Test various configuration options of dfs.name.dir and dfs.name.edits.dir
+   * The test creates files and restarts cluster with different configs.
+   * 1. Starts cluster with shared name and edits dirs
+   * 2. Restarts cluster by adding additional (different) name and edits dirs
+   * 3. Restarts cluster by removing shared name and edits dirs by allowing to 
+   *    start using separate name and edits dirs
+   * 4. Restart cluster by adding shared directory again, but make sure we 
+   *    do not read any stale image or edits. 
+   * All along the test, we create and delete files at reach restart to make
+   * sure we are reading proper edits and image.
+   */
+  public void testNameEditsConfigs() throws IOException {
+    Path file1 = new Path("TestNameEditsConfigs1");
+    Path file2 = new Path("TestNameEditsConfigs2");
+    Path file3 = new Path("TestNameEditsConfigs3");
+    MiniDFSCluster cluster = null;
+    SecondaryNameNode secondary = null;
+    Configuration conf = null;
+    FileSystem fileSys = null;
+    File base_dir = new File(System.getProperty("test.build.data"), "dfs/");
+    File newNameDir = new File(base_dir, "name");
+    File newEditsDir = new File(base_dir, "edits");
+    File nameAndEdits = new File(base_dir, "name_and_edits");
+    File checkpointNameDir = new File(base_dir, "secondname");
+    File checkpointEditsDir = new File(base_dir, "secondedits");
+    File checkpointNameAndEdits = new File(base_dir, "second_name_and_edits");
+    
+    // Start namenode with same dfs.name.dir and dfs.name.edits.dir
+    conf = new Configuration();
+    conf.set("dfs.name.dir", nameAndEdits.getPath());
+    conf.set("dfs.name.edits.dir", nameAndEdits.getPath());
+    conf.set("fs.checkpoint.dir", checkpointNameAndEdits.getPath());
+    conf.set("fs.checkpoint.edits.dir", checkpointNameAndEdits.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    // Manage our own dfs directories
+    cluster = new MiniDFSCluster(0, conf, numDatanodes, true, false, true, null,
+                                  null, null, null);
+    cluster.waitActive();
+    secondary = startSecondaryNameNode(conf);
+    fileSys = cluster.getFileSystem();
+
+    try {
+      assertTrue(!fileSys.exists(file1));
+      writeFile(fileSys, file1, replication);
+      checkFile(fileSys, file1, replication);
+      secondary.doCheckpoint();
+    } finally {
+      fileSys.close();
+      cluster.shutdown();
+      secondary.shutdown();
+    }
+
+    // Start namenode with additional dfs.name.dir and dfs.name.edits.dir
+    conf =  new Configuration();
+    assertTrue(newNameDir.mkdir());
+    assertTrue(newEditsDir.mkdir());
+
+    conf.set("dfs.name.dir", nameAndEdits.getPath() +
+              "," + newNameDir.getPath());
+    conf.set("dfs.name.edits.dir", nameAndEdits.getPath() + 
+             "," + newEditsDir.getPath());
+    conf.set("fs.checkpoint.dir", checkpointNameDir.getPath() +
+             "," + checkpointNameAndEdits.getPath());
+    conf.set("fs.checkpoint.edits.dir", checkpointEditsDir.getPath() +
+             "," + checkpointNameAndEdits.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    // Manage our own dfs directories. Do not format.
+    cluster = new MiniDFSCluster(0, conf, numDatanodes, false, false, true, 
+                                  null, null, null, null);
+    cluster.waitActive();
+    secondary = startSecondaryNameNode(conf);
+    fileSys = cluster.getFileSystem();
+
+    try {
+      assertTrue(fileSys.exists(file1));
+      checkFile(fileSys, file1, replication);
+      cleanupFile(fileSys, file1);
+      writeFile(fileSys, file2, replication);
+      checkFile(fileSys, file2, replication);
+      secondary.doCheckpoint();
+    } finally {
+      fileSys.close();
+      cluster.shutdown();
+      secondary.shutdown();
+    }
+    
+    // Now remove common directory both have and start namenode with 
+    // separate name and edits dirs
+    conf =  new Configuration();
+    conf.set("dfs.name.dir", newNameDir.getPath());
+    conf.set("dfs.name.edits.dir", newEditsDir.getPath());
+    conf.set("fs.checkpoint.dir", checkpointNameDir.getPath());
+    conf.set("fs.checkpoint.edits.dir", checkpointEditsDir.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    cluster = new MiniDFSCluster(0, conf, numDatanodes, false, false, true,
+                                  null, null, null, null);
+    cluster.waitActive();
+    secondary = startSecondaryNameNode(conf);
+    fileSys = cluster.getFileSystem();
+
+    try {
+      assertTrue(!fileSys.exists(file1));
+      assertTrue(fileSys.exists(file2));
+      checkFile(fileSys, file2, replication);
+      cleanupFile(fileSys, file2);
+      writeFile(fileSys, file3, replication);
+      checkFile(fileSys, file3, replication);
+      secondary.doCheckpoint();
+    } finally {
+      fileSys.close();
+      cluster.shutdown();
+      secondary.shutdown();
+    }
+    
+    // Add old name_and_edits dir. File system should not read image or edits
+    // from old dir
+    conf = new Configuration();
+    conf.set("dfs.name.dir", nameAndEdits.getPath() +
+              "," + newNameDir.getPath());
+    conf.set("dfs.name.edits.dir", nameAndEdits +
+              "," + newEditsDir.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    cluster = new MiniDFSCluster(0, conf, numDatanodes, false, false, true,
+                                  null, null, null, null);
+    cluster.waitActive();
+    fileSys = cluster.getFileSystem();
+
+    try {
+      assertTrue(!fileSys.exists(file1));
+      assertTrue(!fileSys.exists(file2));
+      assertTrue(fileSys.exists(file3));
+      checkFile(fileSys, file3, replication);
+    } finally {
+      fileSys.close();
+      cluster.shutdown();
+    }
+
+    // Cleanup
+    if (!FileUtil.fullyDelete(newNameDir)) 
+      throw new IOException("Cannot remove directory " + newNameDir);
+    if (!FileUtil.fullyDelete(newEditsDir)) 
+      throw new IOException("Cannot remove directory " + newEditsDir);
+    if (!FileUtil.fullyDelete(nameAndEdits))
+      throw new IOException("Cannot remove directory " + nameAndEdits);
+    if (!FileUtil.fullyDelete(checkpointNameDir))
+      throw new IOException("Cannot remove directory " + checkpointNameDir);
+    if (!FileUtil.fullyDelete(checkpointEditsDir))
+      throw new IOException("Cannot remove directory " + checkpointEditsDir);
+    if (!FileUtil.fullyDelete(checkpointNameAndEdits))
+      throw new IOException("Cannot remove directory " + 
+                             checkpointNameAndEdits);
+  }
+
+  /**
+   * Test various configuration options of dfs.name.dir and dfs.name.edits.dir
+   * This test tries to simulate failure scenarios.
+   * 1. Start cluster with shared name and edits dir
+   * 2. Restart cluster by adding separate name and edits dirs
+   * 3. Restart cluster by removing shared name and edits dir
+   * 4. Restart cluster with old shared name and edits dir, but only latest 
+   *    name dir. This should fail since we dont have latest edits dir
+   * 5. Restart cluster with old shared name and edits dir, but only latest
+   *    edits dir. This should fail since we dont have latest name dir
+   */
+  public void testNameEditsConfigsFailure() throws IOException {
+    Path file1 = new Path("TestNameEditsConfigs1");
+    Path file2 = new Path("TestNameEditsConfigs2");
+    Path file3 = new Path("TestNameEditsConfigs3");
+    MiniDFSCluster cluster = null;
+    Configuration conf = null;
+    FileSystem fileSys = null;
+    File base_dir = new File(System.getProperty("test.build.data"), "dfs/");
+    File newNameDir = new File(base_dir, "name");
+    File newEditsDir = new File(base_dir, "edits");
+    File nameAndEdits = new File(base_dir, "name_and_edits");
+    
+    // Start namenode with same dfs.name.dir and dfs.name.edits.dir
+    conf = new Configuration();
+    conf.set("dfs.name.dir", nameAndEdits.getPath());
+    conf.set("dfs.name.edits.dir", nameAndEdits.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    // Manage our own dfs directories
+    cluster = new MiniDFSCluster(0, conf, numDatanodes, true, false, true, null,
+                                  null, null, null);
+    cluster.waitActive();
+    fileSys = cluster.getFileSystem();
+
+    try {
+      assertTrue(!fileSys.exists(file1));
+      writeFile(fileSys, file1, replication);
+      checkFile(fileSys, file1, replication);
+    } finally  {
+      fileSys.close();
+      cluster.shutdown();
+    }
+
+    // Start namenode with additional dfs.name.dir and dfs.name.edits.dir
+    conf =  new Configuration();
+    assertTrue(newNameDir.mkdir());
+    assertTrue(newEditsDir.mkdir());
+
+    conf.set("dfs.name.dir", nameAndEdits.getPath() +
+              "," + newNameDir.getPath());
+    conf.set("dfs.name.edits.dir", nameAndEdits.getPath() +
+              "," + newEditsDir.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    // Manage our own dfs directories. Do not format.
+    cluster = new MiniDFSCluster(0, conf, numDatanodes, false, false, true, 
+                                  null, null, null, null);
+    cluster.waitActive();
+    fileSys = cluster.getFileSystem();
+
+    try {
+      assertTrue(fileSys.exists(file1));
+      checkFile(fileSys, file1, replication);
+      cleanupFile(fileSys, file1);
+      writeFile(fileSys, file2, replication);
+      checkFile(fileSys, file2, replication);
+    } finally {
+      fileSys.close();
+      cluster.shutdown();
+    }
+    
+    // Now remove common directory both have and start namenode with 
+    // separate name and edits dirs
+    conf =  new Configuration();
+    conf.set("dfs.name.dir", newNameDir.getPath());
+    conf.set("dfs.name.edits.dir", newEditsDir.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    cluster = new MiniDFSCluster(0, conf, numDatanodes, false, false, true,
+                                  null, null, null, null);
+    cluster.waitActive();
+    fileSys = cluster.getFileSystem();
+
+    try {
+      assertTrue(!fileSys.exists(file1));
+      assertTrue(fileSys.exists(file2));
+      checkFile(fileSys, file2, replication);
+      cleanupFile(fileSys, file2);
+      writeFile(fileSys, file3, replication);
+      checkFile(fileSys, file3, replication);
+    } finally {
+      fileSys.close();
+      cluster.shutdown();
+    }
+
+    // Add old shared directory for name and edits along with latest name
+    conf = new Configuration();
+    conf.set("dfs.name.dir", newNameDir.getPath() + "," + 
+             nameAndEdits.getPath());
+    conf.set("dfs.name.edits.dir", nameAndEdits.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    try {
+      cluster = new MiniDFSCluster(0, conf, numDatanodes, false, false, true,
+                                  null, null, null, null);
+      assertTrue(false);
+    } catch (IOException e) { // expect to fail
+      System.out.println("cluster start failed due to missing " +
+                         "latest edits dir");
+    } finally {
+      cluster = null;
+    }
+
+    // Add old shared directory for name and edits along with latest edits
+    conf = new Configuration();
+    conf.set("dfs.name.dir", nameAndEdits.getPath());
+    conf.set("dfs.name.edits.dir", newEditsDir.getPath() +
+             "," + nameAndEdits.getPath());
+    replication = (short)conf.getInt("dfs.replication", 3);
+    try {
+      cluster = new MiniDFSCluster(0, conf, numDatanodes, false, false, true,
+                                   null, null, null, null);
+      assertTrue(false);
+    } catch (IOException e) { // expect to fail
+      System.out.println("cluster start failed due to missing latest name dir");
+    } finally {
+      cluster = null;
+    }
+
+    // Cleanup
+    if (!FileUtil.fullyDelete(newNameDir)) 
+      throw new IOException("Cannot remove directory " + newNameDir);
+    if (!FileUtil.fullyDelete(newEditsDir)) 
+      throw new IOException("Cannot remove directory " + newEditsDir);
+    if (!FileUtil.fullyDelete(nameAndEdits))
+      throw new IOException("Cannot remove directory " + nameAndEdits);
+  }
+}