blob: 352d734df0f2b81dcfc29f6d64a23099508824ab [file] [log] [blame]
/**
* 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.IOException;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.mapred.JobConf;
/**
* This program executes a specified operation that applies load to
* the NameNode. Possible operations include create/writing files,
* opening/reading files, renaming files, and deleting files.
*
* When run simultaneously on multiple nodes, this program functions
* as a stress-test and benchmark for namenode, especially when
* the number of bytes written to each file is small.
*
* This version does not use the map reduce framework
*
*/
public class NNBenchWithoutMR {
private static final Log LOG = LogFactory.getLog(
"org.apache.hadoop.hdfs.NNBench");
// variable initialzed from command line arguments
private static long startTime = 0;
private static int numFiles = 0;
private static long bytesPerBlock = 1;
private static long blocksPerFile = 0;
private static long bytesPerFile = 1;
private static Path baseDir = null;
// variables initialized in main()
private static FileSystem fileSys = null;
private static Path taskDir = null;
private static byte[] buffer;
private static long maxExceptionsPerFile = 200;
/**
* Returns when the current number of seconds from the epoch equals
* the command line argument given by <code>-startTime</code>.
* This allows multiple instances of this program, running on clock
* synchronized nodes, to start at roughly the same time.
*/
static void barrier() {
long sleepTime;
while ((sleepTime = startTime - System.currentTimeMillis()) > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException ex) {
//This left empty on purpose
}
}
}
static private void handleException(String operation, Throwable e,
int singleFileExceptions) {
LOG.warn("Exception while " + operation + ": " +
StringUtils.stringifyException(e));
if (singleFileExceptions >= maxExceptionsPerFile) {
throw new RuntimeException(singleFileExceptions +
" exceptions for a single file exceeds threshold. Aborting");
}
}
/**
* Create and write to a given number of files. Repeat each remote
* operation until is suceeds (does not throw an exception).
*
* @return the number of exceptions caught
*/
static int createWrite() {
int totalExceptions = 0;
FSDataOutputStream out = null;
boolean success;
for (int index = 0; index < numFiles; index++) {
int singleFileExceptions = 0;
do { // create file until is succeeds or max exceptions reached
try {
out = fileSys.create(
new Path(taskDir, "" + index), false, 512,
(short)1, bytesPerBlock);
success = true;
} catch (IOException ioe) {
success=false;
totalExceptions++;
handleException("creating file #" + index, ioe,
++singleFileExceptions);
}
} while (!success);
long toBeWritten = bytesPerFile;
while (toBeWritten > 0) {
int nbytes = (int) Math.min(buffer.length, toBeWritten);
toBeWritten -= nbytes;
try { // only try once
out.write(buffer, 0, nbytes);
} catch (IOException ioe) {
totalExceptions++;
handleException("writing to file #" + index, ioe,
++singleFileExceptions);
}
}
do { // close file until is succeeds
try {
out.close();
success = true;
} catch (IOException ioe) {
success=false;
totalExceptions++;
handleException("closing file #" + index, ioe,
++singleFileExceptions);
}
} while (!success);
}
return totalExceptions;
}
/**
* Open and read a given number of files.
*
* @return the number of exceptions caught
*/
static int openRead() {
int totalExceptions = 0;
FSDataInputStream in;
for (int index = 0; index < numFiles; index++) {
int singleFileExceptions = 0;
try {
in = fileSys.open(new Path(taskDir, "" + index), 512);
long toBeRead = bytesPerFile;
while (toBeRead > 0) {
int nbytes = (int) Math.min(buffer.length, toBeRead);
toBeRead -= nbytes;
try { // only try once && we don't care about a number of bytes read
in.read(buffer, 0, nbytes);
} catch (IOException ioe) {
totalExceptions++;
handleException("reading from file #" + index, ioe,
++singleFileExceptions);
}
}
in.close();
} catch (IOException ioe) {
totalExceptions++;
handleException("opening file #" + index, ioe, ++singleFileExceptions);
}
}
return totalExceptions;
}
/**
* Rename a given number of files. Repeat each remote
* operation until is suceeds (does not throw an exception).
*
* @return the number of exceptions caught
*/
static int rename() {
int totalExceptions = 0;
boolean success;
for (int index = 0; index < numFiles; index++) {
int singleFileExceptions = 0;
do { // rename file until is succeeds
try {
// Possible result of this operation is at no interest to us for it
// can return false only if the namesystem
// could rename the path from the name
// space (e.g. no Exception has been thrown)
fileSys.rename(new Path(taskDir, "" + index),
new Path(taskDir, "A" + index));
success = true;
} catch (IOException ioe) {
success = false;
totalExceptions++;
handleException("creating file #" + index, ioe, ++singleFileExceptions);
}
} while (!success);
}
return totalExceptions;
}
/**
* Delete a given number of files. Repeat each remote
* operation until is suceeds (does not throw an exception).
*
* @return the number of exceptions caught
*/
static int delete() {
int totalExceptions = 0;
boolean success;
for (int index = 0; index < numFiles; index++) {
int singleFileExceptions = 0;
do { // delete file until is succeeds
try {
// Possible result of this operation is at no interest to us for it
// can return false only if namesystem
// delete could remove the path from the name
// space (e.g. no Exception has been thrown)
fileSys.delete(new Path(taskDir, "A" + index), true);
success = true;
} catch (IOException ioe) {
success=false;
totalExceptions++;
handleException("creating file #" + index, ioe, ++singleFileExceptions);
}
} while (!success);
}
return totalExceptions;
}
/**
* This launches a given namenode operation (<code>-operation</code>),
* starting at a given time (<code>-startTime</code>). The files used
* by the openRead, rename, and delete operations are the same files
* created by the createWrite operation. Typically, the program
* would be run four times, once for each operation in this order:
* createWrite, openRead, rename, delete.
*
* <pre>
* Usage: nnbench
* -operation <one of createWrite, openRead, rename, or delete>
* -baseDir <base output/input DFS path>
* -startTime <time to start, given in seconds from the epoch>
* -numFiles <number of files to create, read, rename, or delete>
* -blocksPerFile <number of blocks to create per file>
* [-bytesPerBlock <number of bytes to write to each block, default is 1>]
* [-bytesPerChecksum <value for io.bytes.per.checksum>]
* </pre>
*
* @param args is an array of the program command line arguments
* @throws IOException indicates a problem with test startup
*/
public static void main(String[] args) throws IOException {
String version = "NameNodeBenchmark.0.3";
System.out.println(version);
int bytesPerChecksum = -1;
String usage =
"Usage: nnbench " +
" -operation <one of createWrite, openRead, rename, or delete> " +
" -baseDir <base output/input DFS path> " +
" -startTime <time to start, given in seconds from the epoch> " +
" -numFiles <number of files to create> " +
" -blocksPerFile <number of blocks to create per file> " +
" [-bytesPerBlock <number of bytes to write to each block, default is 1>] " +
" [-bytesPerChecksum <value for io.bytes.per.checksum>]" +
"Note: bytesPerBlock MUST be a multiple of bytesPerChecksum";
String operation = null;
for (int i = 0; i < args.length; i++) { // parse command line
if (args[i].equals("-baseDir")) {
baseDir = new Path(args[++i]);
} else if (args[i].equals("-numFiles")) {
numFiles = Integer.parseInt(args[++i]);
} else if (args[i].equals("-blocksPerFile")) {
blocksPerFile = Integer.parseInt(args[++i]);
} else if (args[i].equals("-bytesPerBlock")) {
bytesPerBlock = Long.parseLong(args[++i]);
} else if (args[i].equals("-bytesPerChecksum")) {
bytesPerChecksum = Integer.parseInt(args[++i]);
} else if (args[i].equals("-startTime")) {
startTime = Long.parseLong(args[++i]) * 1000;
} else if (args[i].equals("-operation")) {
operation = args[++i];
} else {
System.out.println(usage);
System.exit(-1);
}
}
bytesPerFile = bytesPerBlock * blocksPerFile;
JobConf jobConf = new JobConf(new Configuration(), NNBench.class);
if ( bytesPerChecksum < 0 ) { // if it is not set in cmdline
bytesPerChecksum = jobConf.getInt("io.bytes.per.checksum", 512);
}
jobConf.set("io.bytes.per.checksum", Integer.toString(bytesPerChecksum));
System.out.println("Inputs: ");
System.out.println(" operation: " + operation);
System.out.println(" baseDir: " + baseDir);
System.out.println(" startTime: " + startTime);
System.out.println(" numFiles: " + numFiles);
System.out.println(" blocksPerFile: " + blocksPerFile);
System.out.println(" bytesPerBlock: " + bytesPerBlock);
System.out.println(" bytesPerChecksum: " + bytesPerChecksum);
if (operation == null || // verify args
baseDir == null ||
numFiles < 1 ||
blocksPerFile < 1 ||
bytesPerBlock < 0 ||
bytesPerBlock % bytesPerChecksum != 0)
{
System.err.println(usage);
System.exit(-1);
}
fileSys = FileSystem.get(jobConf);
String uniqueId = java.net.InetAddress.getLocalHost().getHostName();
taskDir = new Path(baseDir, uniqueId);
// initialize buffer used for writing/reading file
buffer = new byte[(int) Math.min(bytesPerFile, 32768L)];
Date execTime;
Date endTime;
long duration;
int exceptions = 0;
barrier(); // wait for coordinated start time
execTime = new Date();
System.out.println("Job started: " + startTime);
if (operation.equals("createWrite")) {
if (!fileSys.mkdirs(taskDir)) {
throw new IOException("Mkdirs failed to create " + taskDir.toString());
}
exceptions = createWrite();
} else if (operation.equals("openRead")) {
exceptions = openRead();
} else if (operation.equals("rename")) {
exceptions = rename();
} else if (operation.equals("delete")) {
exceptions = delete();
} else {
System.err.println(usage);
System.exit(-1);
}
endTime = new Date();
System.out.println("Job ended: " + endTime);
duration = (endTime.getTime() - execTime.getTime()) /1000;
System.out.println("The " + operation + " job took " + duration + " seconds.");
System.out.println("The job recorded " + exceptions + " exceptions.");
}
}