* For example, suppose an index has mappings for VLSN 1, 5, 10,
* and the rollback is going to matchpoint 7. A pure truncation
* would lop off VLSN 10, making VLSN 5 the last mapping. We
* would then need to add on VLSN 7.
*/
VLSN lastMatchpointVLSN = recoveryTracker.getLastMatchpointVLSN();
if (lastMatchpointVLSN.isNull()) {
return;
}
/*
* Use a MATCHPOINT log entry to indicate that this is a syncable
* entry. This purposefully leaves the recovery tracker's range's
* lastTxnEnd null, so it will not overwrite the on disk
* tracker. This assumes that we will never rollback past the last
* txn end.
*/
recoveryTracker.track(lastMatchpointVLSN,
recoveryTracker.getLastMatchpointLsn(),
LogEntryType.LOG_MATCHPOINT.getTypeNum());
}
/*
* The mappings held in the recoveryTracker must either overlap what's
* on disk or immediately follow the last mapping on disk. If there
* is a gap between what is on disk and the recovery tracker, something
* went awry with the checkpoint scheme, which flushes the VLSN index
* at each checkpoint. We're in danger of losing some mappings. Most
* importantly, the last txnEnd VLSN in the range might not be right.
*
* The one exception is when the Environment has been converted from
* non-replicated and there are no VLSN entries in the VLSNIndex. In
* that case, it's valid that the entries seen from the recovery
* tracker may have a gap in VLSNs. For example, in a newly converted
* environment, the VLSN index range has NULL_VLSN as its last entry,
* but the first replicated log entry will start with 2.
*
* Note: EnvironmentImpl.needRepConvert() would more accurately convey
* the fact that this is the very first recovery following a
* conversion. But needRepConvert() on a replica is never true, and we
* need to disable this check on the replica's first recovery too.
*/
VLSN persistentLast = tracker.getRange().getLast();
VLSN recoveryFirst = recoveryTracker.getRange().getFirst();
if ((!(envImpl.isRepConverted() && persistentLast.isNull()) ||
!envImpl.isRepConverted()) &&
recoveryFirst.compareTo(persistentLast.getNext()) > 0) {
throw EnvironmentFailureException.unexpectedState
(envImpl, "recoveryTracker should overlap or follow on disk " +
"last VLSN of " + persistentLast + " recoveryFirst= " +
recoveryFirst);
}
VLSNRange currentRange = tracker.getRange();
if (currentRange.getLast().getNext().equals(recoveryFirst)) {
/* No overlap, just append mappings found at recovery. */
tracker.append(recoveryTracker);
flushToDatabase(Durability.COMMIT_SYNC);
return;
}
/*
* The mappings in the recovery tracker should overwrite those in the
* VLSN index.
*/
TransactionConfig config = new TransactionConfig();
config.setDurability(Durability.COMMIT_SYNC);
Txn txn = Txn.createLocalTxn(envImpl, config);
boolean success = false;
VLSN lastOnDiskVLSN;
try {
lastOnDiskVLSN = pruneDatabaseTail(recoveryFirst, DbLsn.NULL_LSN,
txn);
tracker.merge(lastOnDiskVLSN, recoveryTracker);
flushToDatabase(txn);