/*
* Copyright (C) 2015 hops.io.
*
* Licensed 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.Log4JLogger;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.MiniDFSNNTopology;
import org.apache.log4j.Level;
import org.junit.Ignore;
import org.junit.Test;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ConnectException;
/**
* This test case tests the high availability of reads when ever reader
* namenodes goes down and even if all the readers go down (except the writer)
* It also tests if the system can be made highly available for reads even if
* the writer is down
*/
@Ignore(value = "The design of this test needs to be reconsidered. " +
"It fails most of the times because of race conditions.")
public class TestHARead extends junit.framework.TestCase {
public static final Log LOG = LogFactory.getLog(TestHARead.class);
{
((Log4JLogger) NameNode.stateChangeLog).getLogger().setLevel(Level.ALL);
((Log4JLogger) LeaseManager.LOG).getLogger().setLevel(Level.ALL);
((Log4JLogger) LogFactory.getLog(FSNamesystem.class)).getLogger()
.setLevel(Level.ALL);
}
final Path dir = new Path("/test/HA/");
int list(FileSystem fs) throws IOException {
int totalFiles = 0;
for (FileStatus s : fs.listStatus(dir)) {
FileSystem.LOG.info("" + s.getPath());
totalFiles++;
}
return totalFiles;
}
static void createFile(FileSystem fs, Path f) throws IOException {
DataOutputStream a_out = fs.create(f);
a_out.writeBytes("something");
a_out.close();
}
@Test
public void testHighAvailability() throws IOException {
Configuration conf = new HdfsConfiguration();
// Create cluster with 3 readers and 1 writer
MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf)
.nnTopology(MiniDFSNNTopology.simpleHOPSTopology(4)).numDataNodes(2)
.format(true).build();
cluster.waitActive();
try {
// Get the filesystem and create a directory
FileSystem fs = cluster.getFileSystem(0);
// Write operation should work since we have one writer
assertTrue(fs.mkdirs(dir));
// Write operation - Create a file and write something to it
Path file1 = new Path(dir, "file1");
createFile(fs, file1);
// Read operation - The file should exist.
assertTrue(fs.exists(file1));
// Read operation - List files in this directory
assertEquals(1, list(fs));
// Read operation - Get file status
FileStatus fileStatus = fs.listStatus(dir)[0];
// Read operation - Get block locations
assertNotSame(0, fs.getFileBlockLocations(file1, 0, 1).length);
// Now we kill all namenodes except the last two
cluster.getNameNode(0).stop();
cluster.getNameNode(1).stop();
// Now lets read again - These operations should be possible
assertTrue(fs.exists(file1));
// Writer operation - concat files
Path file2 = new Path(dir, "file2");
createFile(fs, file2);
assertTrue(fs.exists(file2));
Path file3 = new Path(dir, "file3");
createFile(fs, file3);
assertTrue(fs.exists(file3));
Path file4 = new Path(dir, "file4");
// Read operation - list files (3 files created now under this directory)
assertEquals(3, list(fs));
// Write operation - rename
// [S] commented out because rename is not yet supported
// ((DistributedFileSystem) fs).rename(file1, file4);
// Kill another namenode
cluster.getNameNode(2).stop();
// Read operation - File status
fs.getFileStatus(file2);
// Write operation - Delete
assertTrue(fs.delete(dir, true));
} catch (IOException ex) {
// In case we have any connectivity issues here, there is a problem
// All connectivitiy issues are handled in the above piece of code
LOG.error(ex);
ex.printStackTrace();
assertFalse("Cannot be any connectivity issues",
ex instanceof ConnectException);
fail();
} finally {
if (cluster != null) {
cluster.shutdown();
}
}
}
}