long firstActiveLsn = info.firstActiveLsn;
long lastUsedLsn = info.lastUsedLsn;
long endOfFileLsn = info.nextAvailableLsn;
/* Set up a reader to pick up target log entries from the log. */
LNFileReader reader =
new LNFileReader(envImpl, readBufferSize, lastUsedLsn,
false, endOfFileLsn, firstActiveLsn, null,
info.checkpointEndLsn);
for (LogEntryType lt: logTypes) {
reader.addTargetType(lt);
}
Map<TxnNodeId,Long> countedFileSummaries =
new HashMap<TxnNodeId,Long>(); // TxnNodeId -> file number
Set<TxnNodeId> countedAbortLsnNodes = new HashSet<TxnNodeId>();
DbTree dbMapTree = envImpl.getDbTree();
/*
* See RollbackTracker.java for details on replication rollback
* periods. Standalone recovery must handle replication rollback at
* recovery, because we might be opening a replicated environment in a
* read-only, non-replicated way for use by a command line utility.
* Even though the utility will not write invisible bits, it will need
* to ensure that all btree nodes are in the proper state, and reflect
* any rollback related changes.
*
* The rollbackScanner is a sort of cursor that acts with the known
* state of the rollback period detection.
*
* We let the tracker know if it is the first pass or not, in order
* to support some internal tracker assertions.
*/
rollbackTracker.setFirstPass(firstUndoPass);
final Scanner rollbackScanner = rollbackTracker.getScanner();
try {
/*
* Iterate over the target LNs and commit records, constructing the
* tree.
*/
while (reader.readNextEntry()) {
if (reader.isLN()) {
/* Get the txnId from the log entry. */
Long txnId = reader.getTxnId();
/* Skip past this, no need to undo non-txnal LNs. */
if (txnId == null) {
continue;
}
if (rollbackScanner.positionAndCheck(reader.getLastLsn(),
txnId)) {
/*
* If an LN is in the rollback period and was part of a
* rollback, let the rollback scanner decide how it
* should be handled. This does not include LNs that
* were explicitly aborted.
*/
rollbackScanner.rollback(txnId, reader, tracker);
continue;
}
/* This LN is part of a committed txn. */
if (committedTxnIds.containsKey(txnId)) {
continue;
}
/* This LN is part of a prepared txn. */
if (preparedTxns.get(txnId) != null) {
resurrectedLsns.add(reader.getLastLsn());
continue;
}
/*
* This LN is part of a uncommitted, unaborted
* replicated txn.
*/
if (isReplicatedUncommittedLN(reader, txnId)) {
createReplayTxn(txnId);
resurrectedLsns.add(reader.getLastLsn());
continue;
}
undoUncommittedLN(reader, dbMapTree, txnId,
countedFileSummaries,
countedAbortLsnNodes);
} else if (reader.isPrepare()) {
handlePrepare(reader);
} else if (reader.isAbort()) {
/* The entry just read is an abort record. */
abortedTxnIds.add(Long.valueOf(reader.getTxnAbortId()));
} else if (reader.isCommit()) {
/*
* Sanity check that the commit does not interfere with the
* rollback period. Since the reader includes commits only
* on the first pass, the cost of the check is confined to
* that pass, and is very low if there is no rollback
* period.
*/
rollbackTracker.checkCommit(reader.getLastLsn(),
reader.getTxnCommitId());
committedTxnIds.put(Long.valueOf(reader.getTxnCommitId()),
Long.valueOf(reader.getLastLsn()));
} else if (reader.isRollbackStart()) {
rollbackTracker.register
((RollbackStart) reader.getMainItem(),
reader.getLastLsn());
} else if (reader.isRollbackEnd()) {
rollbackTracker.register((RollbackEnd) reader.getMainItem(),
reader.getLastLsn());
} else {
throw new EnvironmentFailureException
(envImpl,
EnvironmentFailureReason.UNEXPECTED_STATE,
"LNreader should not have picked up type " +
reader.dumpCurrentHeader());
}
} /* while */
info.nRepeatIteratorReads += reader.getNRepeatIteratorReads();
rollbackTracker.singlePassSetInvisible();
} catch (RuntimeException e) {
traceAndThrowException(reader.getLastLsn(), "undoLNs", e);
}
}