/*
* If wasCleaned is false we don't count statistics unless we migrate
* the LN. This avoids double counting.
*/
BasicLocker locker = null;
LN ln = null;
try {
if (lsn == DbLsn.NULL_LSN) {
/* This node was never written, no need to migrate. */
completed = true;
return;
}
/*
* Fetch the node, if necessary. If it was not resident and it is
* an evictable LN, we will clear it after we migrate it.
*/
if (!bin.isEntryKnownDeleted(index)) {
ln = (LN) bin.getTarget(index);
if (ln == null) {
/* If fetchTarget returns null, a deleted LN was cleaned.*/
ln = (LN) bin.fetchTarget(index);
clearTarget = !db.getId().equals(DbTree.ID_DB_ID);
}
}
/* Don't migrate knownDeleted or deleted cleaned LNs. */
if (ln == null) {
if (wasCleaned) {
nLNsDead.increment();
}
obsolete = true;
completed = true;
return;
}
/*
* Get a non-blocking read lock on the LN. A pending node is
* already locked, but the original pending LSN may have changed.
* We must lock the current LSN to guard against aborts.
*/
if (lockedPendingLsn != lsn) {
locker = BasicLocker.createBasicLocker(env, false /*noWait*/);
/* Don't allow this short-lived lock to be preempted/stolen. */
locker.setPreemptable(false);
LockResult lockRet = locker.nonBlockingLock
(lsn, LockType.READ, false /*jumpAheadOfWaiters*/, db);
if (lockRet.getLockGrant() == LockGrantType.DENIED) {
/*
* LN is currently locked by another Locker, so we can't
* assume anything about the value of the LSN in the bin.
*/
if (wasCleaned) {
nLNsLocked.increment();
}
lockDenied = true;
completed = true;
return;
}
}
/* Don't migrate deleted LNs. */
if (ln.isDeleted()) {
bin.setKnownDeleted(index);
if (wasCleaned) {
nLNsDead.increment();
}
obsolete = true;
completed = true;
return;
}
/*
* Once we have a lock, check whether the current LSN needs to be
* migrated. There is no need to migrate it if the LSN no longer
* qualifies for cleaning. The LSN could have been changed by an
* update or delete after we set the MIGRATE flag.
*
* Note that we do not perform this optimization if the MIGRATE
* flag is not set, i.e, for clustering and proactive migration of
* resident LNs. For these cases, we checked the conditions for
* migration immediately before calling this method. Although the
* condition could change after locking, the window is small and
* a second check is not worthwhile.
*/
if (bin.getMigrate(index)) {
Long fileNum = Long.valueOf(DbLsn.getFileNumber(lsn));
if (!fileSelector.isFileCleaningInProgress(fileNum)) {
obsolete = true;
completed = true;
if (wasCleaned) {
nLNsDead.increment();
}
return;
}
}
/*
* Migrate the LN.
*
* Do not pass a locker, because there is no need to lock the new
* LSN, as done for user operations. Another locker cannot attempt
* to lock the new LSN until we're done, because we release the
* lock before we release the BIN latch.
*/
long newLNLsn = ln.log(env, db, bin.getKey(index), lsn,
backgroundIO,
getMigrationRepContext(ln));
bin.updateEntry(index, newLNLsn);
nLNsMigrated.increment();
/* Lock new LSN on behalf of existing lockers. */