|
@@ -37,7 +37,8 @@ import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature;
|
|
|
import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo;
|
|
|
import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoUnderConstruction;
|
|
|
import org.apache.hadoop.hdfs.server.common.Storage;
|
|
|
-import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.LogHeader;
|
|
|
+import org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream.LogHeaderCorruptException;
|
|
|
+import org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.EditLogValidation;
|
|
|
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.Reader;
|
|
|
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.AddCloseOp;
|
|
|
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.CancelDelegationTokenOp;
|
|
@@ -60,6 +61,7 @@ import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.SymlinkOp;
|
|
|
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.TimesOp;
|
|
|
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.UpdateMasterKeyOp;
|
|
|
import org.apache.hadoop.hdfs.server.namenode.LeaseManager.Lease;
|
|
|
+import org.apache.hadoop.io.IOUtils;
|
|
|
|
|
|
public class FSEditLogLoader {
|
|
|
private final FSNamesystem fsNamesys;
|
|
@@ -84,29 +86,25 @@ public class FSEditLogLoader {
|
|
|
}
|
|
|
|
|
|
int loadFSEdits(EditLogInputStream edits, boolean closeOnExit,
|
|
|
- long expectedStartingTxId)
|
|
|
- throws IOException {
|
|
|
- BufferedInputStream bin = new BufferedInputStream(edits);
|
|
|
- DataInputStream in = new DataInputStream(bin);
|
|
|
-
|
|
|
+ long expectedStartingTxId)
|
|
|
+ throws IOException {
|
|
|
int numEdits = 0;
|
|
|
+ int logVersion = edits.getVersion();
|
|
|
|
|
|
try {
|
|
|
- LogHeader header = LogHeader.read(in);
|
|
|
- numEdits = loadEditRecords(
|
|
|
- header.logVersion, in, header.checksum, false,
|
|
|
- expectedStartingTxId);
|
|
|
+ numEdits = loadEditRecords(logVersion, edits, false,
|
|
|
+ expectedStartingTxId);
|
|
|
} finally {
|
|
|
- if(closeOnExit)
|
|
|
- in.close();
|
|
|
+ if(closeOnExit) {
|
|
|
+ edits.close();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return numEdits;
|
|
|
}
|
|
|
|
|
|
@SuppressWarnings("deprecation")
|
|
|
- int loadEditRecords(int logVersion, DataInputStream in,
|
|
|
- Checksum checksum, boolean closeOnExit,
|
|
|
+ int loadEditRecords(int logVersion, EditLogInputStream in, boolean closeOnExit,
|
|
|
long expectedStartingTxId)
|
|
|
throws IOException {
|
|
|
FSDirectory fsDir = fsNamesys.dir;
|
|
@@ -123,10 +121,6 @@ public class FSEditLogLoader {
|
|
|
fsNamesys.writeLock();
|
|
|
fsDir.writeLock();
|
|
|
|
|
|
- // Keep track of the file offsets of the last several opcodes.
|
|
|
- // This is handy when manually recovering corrupted edits files.
|
|
|
- PositionTrackingInputStream tracker = new PositionTrackingInputStream(in);
|
|
|
- in = new DataInputStream(tracker);
|
|
|
long recentOpcodeOffsets[] = new long[4];
|
|
|
Arrays.fill(recentOpcodeOffsets, -1);
|
|
|
|
|
@@ -134,12 +128,10 @@ public class FSEditLogLoader {
|
|
|
long txId = expectedStartingTxId - 1;
|
|
|
|
|
|
try {
|
|
|
- FSEditLogOp.Reader reader = new FSEditLogOp.Reader(in, logVersion,
|
|
|
- checksum);
|
|
|
FSEditLogOp op;
|
|
|
- while ((op = reader.readOp()) != null) {
|
|
|
+ while ((op = in.readOp()) != null) {
|
|
|
recentOpcodeOffsets[numEdits % recentOpcodeOffsets.length] =
|
|
|
- tracker.getPos();
|
|
|
+ in.getPosition();
|
|
|
if (LayoutVersion.supports(Feature.STORED_TXIDS, logVersion)) {
|
|
|
long thisTxId = op.txid;
|
|
|
if (thisTxId != txId + 1) {
|
|
@@ -421,7 +413,7 @@ public class FSEditLogLoader {
|
|
|
// Catch Throwable because in the case of a truly corrupt edits log, any
|
|
|
// sort of error might be thrown (NumberFormat, NullPointer, EOF, etc.)
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
- sb.append("Error replaying edit log at offset " + tracker.getPos());
|
|
|
+ sb.append("Error replaying edit log at offset " + in.getPosition());
|
|
|
if (recentOpcodeOffsets[0] != -1) {
|
|
|
Arrays.sort(recentOpcodeOffsets);
|
|
|
sb.append("\nRecent opcode offsets:");
|
|
@@ -480,49 +472,50 @@ public class FSEditLogLoader {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ static EditLogValidation validateEditLog(File file) throws IOException {
|
|
|
+ EditLogFileInputStream in;
|
|
|
+ try {
|
|
|
+ in = new EditLogFileInputStream(file);
|
|
|
+ } catch (LogHeaderCorruptException corrupt) {
|
|
|
+ // If it's missing its header, this is equivalent to no transactions
|
|
|
+ FSImage.LOG.warn("Log at " + file + " has no valid header",
|
|
|
+ corrupt);
|
|
|
+ return new EditLogValidation(0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ return validateEditLog(in);
|
|
|
+ } finally {
|
|
|
+ IOUtils.closeStream(in);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * Return the number of valid transactions in the file. If the file is
|
|
|
+ * Return the number of valid transactions in the stream. If the stream is
|
|
|
* truncated during the header, returns a value indicating that there are
|
|
|
- * 0 valid transactions.
|
|
|
- * @throws IOException if the file cannot be read due to an IO error (eg
|
|
|
+ * 0 valid transactions. This reads through the stream but does not close
|
|
|
+ * it.
|
|
|
+ * @throws IOException if the stream cannot be read due to an IO error (eg
|
|
|
* if the log does not exist)
|
|
|
*/
|
|
|
- static EditLogValidation validateEditLog(File f) throws IOException {
|
|
|
- FileInputStream fis = new FileInputStream(f);
|
|
|
+ static EditLogValidation validateEditLog(EditLogInputStream in) {
|
|
|
+ long numValid = 0;
|
|
|
+ long lastPos = 0;
|
|
|
try {
|
|
|
- PositionTrackingInputStream tracker = new PositionTrackingInputStream(
|
|
|
- new BufferedInputStream(fis));
|
|
|
- DataInputStream dis = new DataInputStream(tracker);
|
|
|
- LogHeader header;
|
|
|
- try {
|
|
|
- header = LogHeader.read(dis);
|
|
|
- } catch (Throwable t) {
|
|
|
- FSImage.LOG.debug("Unable to read header from " + f +
|
|
|
- " -> no valid transactions in this file.");
|
|
|
- return new EditLogValidation(0, 0);
|
|
|
- }
|
|
|
-
|
|
|
- Reader reader = new FSEditLogOp.Reader(dis, header.logVersion, header.checksum);
|
|
|
- long numValid = 0;
|
|
|
- long lastPos = 0;
|
|
|
- try {
|
|
|
- while (true) {
|
|
|
- lastPos = tracker.getPos();
|
|
|
- if (reader.readOp() == null) {
|
|
|
- break;
|
|
|
- }
|
|
|
- numValid++;
|
|
|
+ while (true) {
|
|
|
+ lastPos = in.getPosition();
|
|
|
+ if (in.readOp() == null) {
|
|
|
+ break;
|
|
|
}
|
|
|
- } catch (Throwable t) {
|
|
|
- // Catch Throwable and not just IOE, since bad edits may generate
|
|
|
- // NumberFormatExceptions, AssertionErrors, OutOfMemoryErrors, etc.
|
|
|
- FSImage.LOG.debug("Caught exception after reading " + numValid +
|
|
|
- " ops from " + f + " while determining its valid length.", t);
|
|
|
+ numValid++;
|
|
|
}
|
|
|
- return new EditLogValidation(lastPos, numValid);
|
|
|
- } finally {
|
|
|
- fis.close();
|
|
|
+ } catch (Throwable t) {
|
|
|
+ // Catch Throwable and not just IOE, since bad edits may generate
|
|
|
+ // NumberFormatExceptions, AssertionErrors, OutOfMemoryErrors, etc.
|
|
|
+ FSImage.LOG.debug("Caught exception after reading " + numValid +
|
|
|
+ " ops from " + in + " while determining its valid length.", t);
|
|
|
}
|
|
|
+ return new EditLogValidation(lastPos, numValid);
|
|
|
}
|
|
|
|
|
|
static class EditLogValidation {
|
|
@@ -536,9 +529,9 @@ public class FSEditLogLoader {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Stream wrapper that keeps track of the current file position.
|
|
|
+ * Stream wrapper that keeps track of the current stream position.
|
|
|
*/
|
|
|
- private static class PositionTrackingInputStream extends FilterInputStream {
|
|
|
+ static class PositionTrackingInputStream extends FilterInputStream {
|
|
|
private long curPos = 0;
|
|
|
private long markPos = -1;
|
|
|
|
|
@@ -582,4 +575,5 @@ public class FSEditLogLoader {
|
|
|
return curPos;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|