long sharedLogTxId = FSEditLogLoader.TXID_IGNORE;
long localLogTxId = FSEditLogLoader.TXID_IGNORE;
FSEditLogOp op = null;
FSEditLog localEditLog = fsDir.fsImage.getEditLog();
while (running && !quitAfterScan) {
// if the application requested that we make a final pass over
// the transaction log, then we remember it here. We close and
// reopen the file to ensure that we can see all the data in the
// file, one reason being that NFS has open-to-close cache
// coherancy and the edit log could be stored in NFS.
//
if (reopen || lastScan) {
inputEditStream.close();
inputEditStream = standby.setupIngestStreamWithRetries(startTxId);
if (lastScan) {
// QUIESCE requested by Standby thread
LOG.info("Ingest: Starting last scan of transaction log: " + this.toString());
quitAfterScan = true;
}
// discard older buffers and start a fresh one.
inputEditStream.refresh(currentPosition, localEditLog.getLastWrittenTxId());
setCatchingUp();
reopen = false;
}
//
// Process all existing transactions till end of file
//
while (running) {
if (lastScan && !quitAfterScan) {
// Standby thread informed the ingest to quiesce
// we should refresh the input stream as soon as possible
// then quitAfterScan will be true
break;
}
// record the current file offset.
currentPosition = inputEditStream.getPosition();
InjectionHandler.processEvent(InjectionEvent.INGEST_BEFORE_LOAD_EDIT);
fsNamesys.writeLock();
try {
error = false;
op = ingestFSEdit(inputEditStream);
/*
* In the case of segments recovered on primary namenode startup, we
* have segments that are finalized (by name), but not containing the
* ending transaction. Without this check, we will keep looping until
* the next checkpoint to discover this situation.
*/
if (!inputEditStream.isInProgress()
&& standby.getLastCorrectTxId() == inputEditStream.getLastTxId()) {
// this is a correct segment with no end segment transaction
LOG.info("Ingest: Reached finalized log segment end with no end marker. "
+ this.toString());
tearDown(localEditLog, false, true);
break;
}
if (op == null) {
FSNamesystem.LOG.debug("Ingest: Invalid opcode, reached end of log " +
"Number of transactions found " +
numEdits);
break; // No more transactions.
}
sharedLogTxId = op.txid;
// Verify transaction ids match.
localLogTxId = localEditLog.getLastWrittenTxId() + 1;
// Fatal error only when the log contains transactions from the future
// we allow to process a transaction with smaller txid than local
// we will simply skip it later after reading from the ingest edits
if (localLogTxId < sharedLogTxId
|| InjectionHandler
.falseCondition(InjectionEvent.INGEST_TXID_CHECK)) {
String message = "The transaction id in the edit log : "
+ sharedLogTxId
+ " does not match the transaction id inferred"
+ " from FSIMAGE : " + localLogTxId;
LOG.fatal(message);
throw new RuntimeException(message);
}
// skip previously loaded transactions
if (!canApplyTransaction(sharedLogTxId, localLogTxId, op))
continue;
// for recovery, we do not want to re-load transactions,
// but we want to populate local log with them
if (shouldLoad(sharedLogTxId)) {
FSEditLogLoader.loadEditRecord(
logVersion,
inputEditStream,
recentOpcodeOffsets,
opCounts,
fsNamesys,
fsDir,
numEdits,
op);
}
LOG.info("Ingest: " + this.toString()
+ ", size: " + inputEditStream.length()
+ ", processing transaction at offset: " + currentPosition
+ ", txid: " + op.txid
+ ", opcode: " + op.opCode);
if (op.opCode == FSEditLogOpCodes.OP_START_LOG_SEGMENT) {
LOG.info("Ingest: Opening log segment: " + this.toString());
localEditLog.open();
} else if (op.opCode == FSEditLogOpCodes.OP_END_LOG_SEGMENT) {
InjectionHandler
.processEventIO(InjectionEvent.INGEST_CLEAR_STANDBY_STATE);
LOG.info("Ingest: Closing log segment: " + this.toString());
tearDown(localEditLog, true, true);
numEdits++;
LOG.info("Ingest: Reached log segment end. " + this.toString());
break;
} else {
localEditLog.logEdit(op);
if (inputEditStream.getReadChecksum() != FSEditLog
.getChecksumForWrite().getValue()) {
throw new IOException(
"Ingest: mismatched r/w checksums for transaction #"
+ numEdits);
}
}
numEdits++;
standby.setLastCorrectTxId(op.txid);
}
catch (ChecksumException cex) {
LOG.info("Checksum error reading the transaction #" + numEdits +
" reopening the file");
reopen = true;
break;
}
catch (IOException e) {
LOG.info("Encountered error reading transaction", e);
error = true; // if we haven't reached eof, then error.
break;
} finally {
if (localEditLog.isOpen()) {
localEditLog.logSyncIfNeeded();
}
fsNamesys.writeUnlock();
}
} // end inner while(running) -- all breaks come here
// if we failed to read the entire transaction from disk,
// then roll back to the offset where there was a last good
// read, sleep for sometime for new transaction to
// appear in the file and then continue;
if (error || running) {
// discard older buffers and start a fresh one.
inputEditStream.refresh(currentPosition, localEditLog.getLastWrittenTxId());
setCatchingUp();
if (error) {
LOG.info("Ingest: Incomplete transaction record at offset " +
inputEditStream.getPosition() +
" but the file is of size " + inputEditStream.length() +
". Continuing....");
}
if (running && !lastScan) {
try {
Thread.sleep(100); // sleep for a second
} catch (InterruptedException e) {
// break out of waiting if we receive an interrupt.
}
}
}
} //end outer while(running)
///////////////////// FINAL ACTIONS /////////////////////
// This was the last scan of the file but we could not read a full
// transaction from disk. If we proceed this will corrupt the image
if (error) {
String errorMessage = FSEditLogLoader.getErrorMessage(recentOpcodeOffsets, currentPosition);
LOG.error(errorMessage);
throw new IOException("Failed to read the edits log. " +
"Incomplete transaction at " + currentPosition);
}
// If the last Scan was completed, then stop the Ingest thread.
if (lastScan && quitAfterScan) {
LOG.info("Ingest: lastScan completed. " + this.toString());
running = false;
if(localEditLog.isOpen()) {
// quiesced non-finalized segment
LOG.info("Ingest: Reached non-finalized log segment end. "+ this.toString());
tearDown(localEditLog, false, localLogTxId != startTxId);
}
}