HDFS-3372. offlineEditsViewer should be able to read a binary edits file with recovery mode. Contributed by Colin Patrick McCabe

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1349628 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Eli Collins 2012-06-13 03:31:47 +00:00
parent 07b8584431
commit 6702d5dbd4
6 changed files with 153 additions and 60 deletions

View File

@ -226,6 +226,9 @@ Branch-2 ( Unreleased changes )
connections and RPC calls, and add MultipleLinearRandomRetry, a new retry connections and RPC calls, and add MultipleLinearRandomRetry, a new retry
policy. (szetszwo) policy. (szetszwo)
HDFS-3372. offlineEditsViewer should be able to read a binary
edits file with recovery mode. (Colin Patrick McCabe via eli)
OPTIMIZATIONS OPTIMIZATIONS
HDFS-2982. Startup performance suffers when there are many edit log HDFS-2982. Startup performance suffers when there are many edit log

View File

@ -18,9 +18,12 @@
package org.apache.hadoop.hdfs.tools.offlineEditsViewer; package org.apache.hadoop.hdfs.tools.offlineEditsViewer;
import java.io.IOException; import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsViewer;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp; import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp;
import org.apache.hadoop.hdfs.server.namenode.EditLogInputStream; import org.apache.hadoop.hdfs.server.namenode.EditLogInputStream;
@ -33,17 +36,21 @@ import org.apache.hadoop.hdfs.server.namenode.EditLogInputStream;
class OfflineEditsBinaryLoader implements OfflineEditsLoader { class OfflineEditsBinaryLoader implements OfflineEditsLoader {
private OfflineEditsVisitor visitor; private OfflineEditsVisitor visitor;
private EditLogInputStream inputStream; private EditLogInputStream inputStream;
private boolean fixTxIds; private final boolean fixTxIds;
private final boolean recoveryMode;
private long nextTxId; private long nextTxId;
public static final Log LOG =
LogFactory.getLog(OfflineEditsBinaryLoader.class.getName());
/** /**
* Constructor * Constructor
*/ */
public OfflineEditsBinaryLoader(OfflineEditsVisitor visitor, public OfflineEditsBinaryLoader(OfflineEditsVisitor visitor,
EditLogInputStream inputStream) { EditLogInputStream inputStream, OfflineEditsViewer.Flags flags) {
this.visitor = visitor; this.visitor = visitor;
this.inputStream = inputStream; this.inputStream = inputStream;
this.fixTxIds = false; this.fixTxIds = flags.getFixTxIds();
this.recoveryMode = flags.getRecoveryMode();
this.nextTxId = -1; this.nextTxId = -1;
} }
@ -51,9 +58,9 @@ class OfflineEditsBinaryLoader implements OfflineEditsLoader {
* Loads edits file, uses visitor to process all elements * Loads edits file, uses visitor to process all elements
*/ */
public void loadEdits() throws IOException { public void loadEdits() throws IOException {
try { visitor.start(inputStream.getVersion());
visitor.start(inputStream.getVersion()); while (true) {
while (true) { try {
FSEditLogOp op = inputStream.readOp(); FSEditLogOp op = inputStream.readOp();
if (op == null) if (op == null)
break; break;
@ -68,16 +75,24 @@ class OfflineEditsBinaryLoader implements OfflineEditsLoader {
nextTxId++; nextTxId++;
} }
visitor.visitOp(op); visitor.visitOp(op);
} catch (IOException e) {
if (!recoveryMode) {
// Tell the visitor to clean up, then re-throw the exception
visitor.close(e);
throw e;
}
LOG.error("Got IOException while reading stream! Resyncing.", e);
inputStream.resync();
} catch (RuntimeException e) {
if (!recoveryMode) {
// Tell the visitor to clean up, then re-throw the exception
visitor.close(e);
throw e;
}
LOG.error("Got RuntimeException while reading stream! Resyncing.", e);
inputStream.resync();
} }
visitor.close(null);
} catch(IOException e) {
// Tell the visitor to clean up, then re-throw the exception
visitor.close(e);
throw e;
} }
} visitor.close(null);
public void setFixTxIds() {
fixTxIds = true;
} }
} }

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsViewer;
import org.apache.hadoop.hdfs.protocol.HdfsConstants; import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream; import org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream;
@ -36,13 +37,12 @@ interface OfflineEditsLoader {
abstract public void loadEdits() throws IOException; abstract public void loadEdits() throws IOException;
public abstract void setFixTxIds();
static class OfflineEditsLoaderFactory { static class OfflineEditsLoaderFactory {
static OfflineEditsLoader createLoader(OfflineEditsVisitor visitor, static OfflineEditsLoader createLoader(OfflineEditsVisitor visitor,
String inputFileName, boolean xmlInput) throws IOException { String inputFileName, boolean xmlInput,
OfflineEditsViewer.Flags flags) throws IOException {
if (xmlInput) { if (xmlInput) {
return new OfflineEditsXmlLoader(visitor, new File(inputFileName)); return new OfflineEditsXmlLoader(visitor, new File(inputFileName), flags);
} else { } else {
File file = null; File file = null;
EditLogInputStream elis = null; EditLogInputStream elis = null;
@ -51,7 +51,7 @@ interface OfflineEditsLoader {
file = new File(inputFileName); file = new File(inputFileName);
elis = new EditLogFileInputStream(file, HdfsConstants.INVALID_TXID, elis = new EditLogFileInputStream(file, HdfsConstants.INVALID_TXID,
HdfsConstants.INVALID_TXID, false); HdfsConstants.INVALID_TXID, false);
loader = new OfflineEditsBinaryLoader(visitor, elis); loader = new OfflineEditsBinaryLoader(visitor, elis, flags);
} finally { } finally {
if ((loader == null) && (elis != null)) { if ((loader == null) && (elis != null)) {
elis.close(); elis.close();

View File

@ -17,16 +17,10 @@
*/ */
package org.apache.hadoop.hdfs.tools.offlineEditsViewer; package org.apache.hadoop.hdfs.tools.offlineEditsViewer;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configured; import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream;
import org.apache.hadoop.hdfs.server.namenode.EditLogInputStream;
import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsLoader.OfflineEditsLoaderFactory; import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsLoader.OfflineEditsLoaderFactory;
import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; import org.apache.hadoop.util.ToolRunner;
@ -37,7 +31,6 @@ import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser; import org.apache.commons.cli.PosixParser;
import org.xml.sax.SAXParseException;
/** /**
* This class implements an offline edits viewer, tool that * This class implements an offline edits viewer, tool that
@ -78,6 +71,9 @@ public class OfflineEditsViewer extends Configured implements Tool {
"-f,--fix-txids Renumber the transaction IDs in the input,\n" + "-f,--fix-txids Renumber the transaction IDs in the input,\n" +
" so that there are no gaps or invalid " + " so that there are no gaps or invalid " +
" transaction IDs.\n" + " transaction IDs.\n" +
"-r,--recover When reading binary edit logs, use recovery \n" +
" mode. This will give you the chance to skip \n" +
" corrupt parts of the edit log.\n" +
"-v,--verbose More verbose output, prints the input and\n" + "-v,--verbose More verbose output, prints the input and\n" +
" output filenames, for processors that write\n" + " output filenames, for processors that write\n" +
" to a file, also output to screen. On large\n" + " to a file, also output to screen. On large\n" +
@ -113,6 +109,7 @@ public class OfflineEditsViewer extends Configured implements Tool {
options.addOption("p", "processor", true, ""); options.addOption("p", "processor", true, "");
options.addOption("v", "verbose", false, ""); options.addOption("v", "verbose", false, "");
options.addOption("f", "fix-txids", false, ""); options.addOption("f", "fix-txids", false, "");
options.addOption("r", "recover", false, "");
options.addOption("h", "help", false, ""); options.addOption("h", "help", false, "");
return options; return options;
@ -128,23 +125,20 @@ public class OfflineEditsViewer extends Configured implements Tool {
* @return 0 on success; error code otherwise * @return 0 on success; error code otherwise
*/ */
public int go(String inputFileName, String outputFileName, String processor, public int go(String inputFileName, String outputFileName, String processor,
boolean printToScreen, boolean fixTxIds, OfflineEditsVisitor visitor) Flags flags, OfflineEditsVisitor visitor)
{ {
if (printToScreen) { if (flags.getPrintToScreen()) {
System.out.println("input [" + inputFileName + "]"); System.out.println("input [" + inputFileName + "]");
System.out.println("output [" + outputFileName + "]"); System.out.println("output [" + outputFileName + "]");
} }
try { try {
if (visitor == null) { if (visitor == null) {
visitor = OfflineEditsVisitorFactory.getEditsVisitor( visitor = OfflineEditsVisitorFactory.getEditsVisitor(
outputFileName, processor, printToScreen); outputFileName, processor, flags.getPrintToScreen());
} }
boolean xmlInput = inputFileName.endsWith(".xml"); boolean xmlInput = inputFileName.endsWith(".xml");
OfflineEditsLoader loader = OfflineEditsLoaderFactory. OfflineEditsLoader loader = OfflineEditsLoaderFactory.
createLoader(visitor, inputFileName, xmlInput); createLoader(visitor, inputFileName, xmlInput, flags);
if (fixTxIds) {
loader.setFixTxIds();
}
loader.loadEdits(); loader.loadEdits();
} catch(Exception e) { } catch(Exception e) {
System.err.println("Encountered exception. Exiting: " + e.getMessage()); System.err.println("Encountered exception. Exiting: " + e.getMessage());
@ -154,6 +148,39 @@ public class OfflineEditsViewer extends Configured implements Tool {
return 0; return 0;
} }
public static class Flags {
private boolean printToScreen = false;
private boolean fixTxIds = false;
private boolean recoveryMode = false;
public Flags() {
}
public boolean getPrintToScreen() {
return printToScreen;
}
public void setPrintToScreen() {
printToScreen = true;
}
public boolean getFixTxIds() {
return fixTxIds;
}
public void setFixTxIds() {
fixTxIds = true;
}
public boolean getRecoveryMode() {
return recoveryMode;
}
public void setRecoveryMode() {
recoveryMode = true;
}
}
/** /**
* Main entry point for ToolRunner (see ToolRunner docs) * Main entry point for ToolRunner (see ToolRunner docs)
* *
@ -177,6 +204,7 @@ public class OfflineEditsViewer extends Configured implements Tool {
printHelp(); printHelp();
return -1; return -1;
} }
if(cmd.hasOption("h")) { // print help and exit if(cmd.hasOption("h")) { // print help and exit
printHelp(); printHelp();
return -1; return -1;
@ -187,10 +215,17 @@ public class OfflineEditsViewer extends Configured implements Tool {
if(processor == null) { if(processor == null) {
processor = defaultProcessor; processor = defaultProcessor;
} }
boolean printToScreen = cmd.hasOption("v"); Flags flags = new Flags();
boolean fixTxIds = cmd.hasOption("f"); if (cmd.hasOption("r")) {
return go(inputFileName, outputFileName, processor, flags.setRecoveryMode();
printToScreen, fixTxIds, null); }
if (cmd.hasOption("f")) {
flags.setFixTxIds();
}
if (cmd.hasOption("v")) {
flags.setPrintToScreen();
}
return go(inputFileName, outputFileName, processor, flags, null);
} }
/** /**

View File

@ -29,7 +29,7 @@ import org.apache.hadoop.hdfs.util.XMLUtils.InvalidXmlException;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp; import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes; import org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.OpInstanceCache; import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.OpInstanceCache;
import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsViewer;
import org.apache.hadoop.hdfs.util.XMLUtils.Stanza; import org.apache.hadoop.hdfs.util.XMLUtils.Stanza;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
@ -46,9 +46,9 @@ import org.xml.sax.helpers.XMLReaderFactory;
@InterfaceStability.Unstable @InterfaceStability.Unstable
class OfflineEditsXmlLoader class OfflineEditsXmlLoader
extends DefaultHandler implements OfflineEditsLoader { extends DefaultHandler implements OfflineEditsLoader {
private boolean fixTxIds; private final boolean fixTxIds;
private OfflineEditsVisitor visitor; private final OfflineEditsVisitor visitor;
private FileReader fileReader; private final FileReader fileReader;
private ParseState state; private ParseState state;
private Stanza stanza; private Stanza stanza;
private Stack<Stanza> stanzaStack; private Stack<Stanza> stanzaStack;
@ -68,9 +68,10 @@ class OfflineEditsXmlLoader
} }
public OfflineEditsXmlLoader(OfflineEditsVisitor visitor, public OfflineEditsXmlLoader(OfflineEditsVisitor visitor,
File inputFile) throws FileNotFoundException { File inputFile, OfflineEditsViewer.Flags flags) throws FileNotFoundException {
this.visitor = visitor; this.visitor = visitor;
this.fileReader = new FileReader(inputFile); this.fileReader = new FileReader(inputFile);
this.fixTxIds = flags.getFixTxIds();
} }
/** /**
@ -250,9 +251,4 @@ class OfflineEditsXmlLoader
public void characters (char ch[], int start, int length) { public void characters (char ch[], int start, int length) {
cbuf.append(ch, start, length); cbuf.append(ch, start, length);
} }
@Override
public void setFixTxIds() {
fixTxIds = true;
}
} }

View File

@ -22,11 +22,14 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.File; import java.io.File;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import org.junit.Test; import org.junit.Test;
import org.junit.Before; import org.junit.Before;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -34,12 +37,12 @@ import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes; import org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes;
import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsViewer; import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsViewer;
import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsViewer.Flags;
import org.apache.hadoop.hdfs.DFSTestUtil; import org.apache.hadoop.hdfs.DFSTestUtil;
import org.apache.hadoop.hdfs.server.namenode.OfflineEditsViewerHelper; import org.apache.hadoop.hdfs.server.namenode.OfflineEditsViewerHelper;
public class TestOfflineEditsViewer { public class TestOfflineEditsViewer {
private static final Log LOG = LogFactory.getLog(TestOfflineEditsViewer.class); private static final Log LOG = LogFactory.getLog(TestOfflineEditsViewer.class);
private static final Map<FSEditLogOpCodes, Boolean> obsoleteOpCodes = private static final Map<FSEditLogOpCodes, Boolean> obsoleteOpCodes =
@ -97,8 +100,8 @@ public class TestOfflineEditsViewer {
String editsReparsed = cacheDir + "/editsReparsed"; String editsReparsed = cacheDir + "/editsReparsed";
// parse to XML then back to binary // parse to XML then back to binary
runOev(edits, editsParsedXml, "xml"); assertEquals(0, runOev(edits, editsParsedXml, "xml", false));
runOev(editsParsedXml, editsReparsed, "binary"); assertEquals(0, runOev(editsParsedXml, editsReparsed, "binary", false));
// judgment time // judgment time
assertTrue( assertTrue(
@ -114,6 +117,42 @@ public class TestOfflineEditsViewer {
LOG.info("END"); LOG.info("END");
} }
@Test
public void testRecoveryMode() throws IOException {
LOG.info("START - testing with generated edits");
nnHelper.startCluster(buildDir + "/dfs/");
// edits generated by nnHelper (MiniDFSCluster), should have all op codes
// binary, XML, reparsed binary
String edits = nnHelper.generateEdits();
// Corrupt the file by truncating the end
FileChannel editsFile = new FileOutputStream(edits, true).getChannel();
editsFile.truncate(editsFile.size() - 5);
String editsParsedXml = cacheDir + "/editsRecoveredParsed.xml";
String editsReparsed = cacheDir + "/editsRecoveredReparsed";
String editsParsedXml2 = cacheDir + "/editsRecoveredParsed2.xml";
// Can't read the corrupted file without recovery mode
assertEquals(-1, runOev(edits, editsParsedXml, "xml", false));
// parse to XML then back to binary
assertEquals(0, runOev(edits, editsParsedXml, "xml", true));
assertEquals(0, runOev(editsParsedXml, editsReparsed, "binary", false));
assertEquals(0, runOev(editsReparsed, editsParsedXml2, "xml", false));
// judgment time
assertTrue("Test round trip",
filesEqualIgnoreTrailingZeros(editsParsedXml, editsParsedXml2));
// removes edits so do this at the end
nnHelper.shutdownCluster();
LOG.info("END");
}
@Test @Test
public void testStored() throws IOException { public void testStored() throws IOException {
@ -128,8 +167,9 @@ public class TestOfflineEditsViewer {
String editsStoredXml = cacheDir + "/editsStored.xml"; String editsStoredXml = cacheDir + "/editsStored.xml";
// parse to XML then back to binary // parse to XML then back to binary
runOev(editsStored, editsStoredParsedXml, "xml"); assertEquals(0, runOev(editsStored, editsStoredParsedXml, "xml", false));
runOev(editsStoredParsedXml, editsStoredReparsed, "binary"); assertEquals(0, runOev(editsStoredParsedXml, editsStoredReparsed,
"binary", false));
// judgement time // judgement time
assertTrue( assertTrue(
@ -151,14 +191,18 @@ public class TestOfflineEditsViewer {
* @param inFilename input edits filename * @param inFilename input edits filename
* @param outFilename oputput edits filename * @param outFilename oputput edits filename
*/ */
private void runOev(String inFilename, String outFilename, String processor) private int runOev(String inFilename, String outFilename, String processor,
throws IOException { boolean recovery) throws IOException {
LOG.info("Running oev [" + inFilename + "] [" + outFilename + "]"); LOG.info("Running oev [" + inFilename + "] [" + outFilename + "]");
OfflineEditsViewer oev = new OfflineEditsViewer(); OfflineEditsViewer oev = new OfflineEditsViewer();
if (oev.go(inFilename, outFilename, processor, true, false, null) != 0) Flags flags = new Flags();
throw new RuntimeException("oev failed"); flags.setPrintToScreen();
if (recovery) {
flags.setRecoveryMode();
}
return oev.go(inFilename, outFilename, processor, flags, null);
} }
/** /**
@ -172,7 +216,7 @@ public class TestOfflineEditsViewer {
FileOutputStream fout = new FileOutputStream(outFilename); FileOutputStream fout = new FileOutputStream(outFilename);
StatisticsEditsVisitor visitor = new StatisticsEditsVisitor(fout); StatisticsEditsVisitor visitor = new StatisticsEditsVisitor(fout);
OfflineEditsViewer oev = new OfflineEditsViewer(); OfflineEditsViewer oev = new OfflineEditsViewer();
if (oev.go(inFilename, outFilename, "stats", false, false, visitor) != 0) if (oev.go(inFilename, outFilename, "stats", new Flags(), visitor) != 0)
return false; return false;
LOG.info("Statistics for " + inFilename + "\n" + LOG.info("Statistics for " + inFilename + "\n" +
visitor.getStatisticsString()); visitor.getStatisticsString());