boolean completed = false; // This method completed.
long nodeId = ln.getNodeId();
BasicLocker locker = null;
BIN bin = null;
DIN parentDIN = null; // for DupCountLNs
try {
nLNsCleanedThisRun++;
/* The whole database is gone, so this LN is obsolete. */
if (db == null || db.getIsDeleted()) {
nLNsDeadThisRun++;
completed = true;
return;
}
Tree tree = db.getTree();
assert tree != null;
/*
* Search down to the bottom most level for the parent of this LN.
*/
boolean parentFound = tree.getParentBINForChildLN
(location, key, dupKey, ln,
false, // splitsAllowed
true, // findDeletedEntries
false, // searchDupTree
false); // updateGeneration
bin = location.bin;
int index = location.index;
if (!parentFound) {
nLNsDeadThisRun++;
completed = true;
return;
}
/*
* Now we're at the parent for this LN, whether BIN, DBIN or DIN.
* If knownDeleted, LN is deleted and can be purged.
*/
if (bin.isEntryKnownDeleted(index)) {
nLNsDeadThisRun++;
obsolete = true;
completed = true;
return;
}
/*
* Determine whether the parent is the current BIN, or in the case
* of a DupCountLN, a DIN. Get the tree LSN in either case.
*/
boolean lnIsDupCountLN = ln.containsDuplicates();
long treeLsn;
if (lnIsDupCountLN) {
parentDIN = (DIN) bin.fetchTarget(index);
parentDIN.latch(false);
ChildReference dclRef = parentDIN.getDupCountLNRef();
treeLsn = dclRef.getLsn();
} else {
treeLsn = bin.getLsn(index);
}
/*
* Check to see whether the LN being migrated is locked elsewhere.
* Do that by attempting to lock it. We can hold the latch on the
* BIN (and DIN) since we always attempt to acquire a non-blocking
* read lock. Holding the latch ensures that the INs won't change
* underneath us because of splits or eviction.
*/
locker = new BasicLocker(env);
LockGrantType lock = locker.nonBlockingReadLock(nodeId, db);
if (lock == LockGrantType.DENIED) {
/*
* LN is currently locked by another Locker, so we can't assume
* anything about the value of the LSN in the bin. However,
* we can check whether the lock owner's abort LSN is greater
* than the log LSN; if so, the log LSN is obsolete. Before
* doing this we must release all latches to avoid a deadlock.
*/
if (parentDIN != null) {
parentDIN.releaseLatch();
parentDIN = null;
}
bin.releaseLatch();
long abortLsn = locker.getOwnerAbortLsn(nodeId);
if (abortLsn != DbLsn.NULL_LSN &&
DbLsn.compareTo(abortLsn, logLsn) > 0) {
nLNsDeadThisRun++;
obsolete = true;
} else {
nLNsLockedThisRun++;
lockDenied = true;
}
completed = true;
return;
}
/*
* We were able to lock this LN in the tree. Try to migrate this
* LN to the end of the log file so we can throw away the old log
* entry.
*
* 1. If the LSN in the tree and in the log are the same,
* we can migrate it, or discard it if the LN is deleted.
*
* 2. If the LSN in the tree is < the LSN in the log, the
* log entry is obsolete, because this LN has been rolled
* back to a previous version by a txn that aborted.
*
* 3. If the LSN in the tree is > the LSN in the log, the
* log entry is obsolete, because the LN was advanced
* forward by some now-committed txn.
*/
if (treeLsn == logLsn) {
if (ln.isDeleted()) {
/*
* If the LN is deleted, we must set knownDeleted to
* prevent fetching it later. This could occur when
* scanning over deleted entries that have not been
* compressed away [10553].
*/
assert !lnIsDupCountLN;
bin.setKnownDeletedLeaveTarget(index);
nLNsDeadThisRun++;
obsolete = true;
} else {
if (lnIsDupCountLN) {
/*
* Migrate the DupCountLN now to avoid having to
* process the migrate flag for DupCountLNs in all
* other places.
*/
long newLNLsn = ln.log
(env, db.getId(), key, logLsn, locker);
parentDIN.updateDupCountLNRef(newLNLsn);
nLNsMigratedThisRun++;
} else {
/*
* Set the migrate flag and dirty the BIN. The evictor
* or checkpointer will migrate the LN later.
*/
bin.setMigrate(index, true);
/*
* Update the generation so that the BIN is not evicted
* immediately. This allows the cleaner to fill in as
* many entries as possible before eviction, as
* to-be-cleaned files are processed.
*/
bin.setGeneration();
/*
* Set the target node so it does not have to be
* fetched when it is migrated. Must call postFetchInit
* to initialize MapLNs that have not been fully
* initialized yet [#13191].
*/
if (bin.getTarget(index) == null) {
ln.postFetchInit(db, logLsn);
bin.updateEntry(index, ln);
}
nLNsMarkedThisRun++;
}
migrated = true;
}
} else {
/* LN is obsolete and can be purged. */
nLNsDeadThisRun++;
obsolete = true;
}
completed = true;
return;
} finally {
if (parentDIN != null) {
parentDIN.releaseLatchIfOwner();
}
if (bin != null) {
bin.releaseLatchIfOwner();
}