boolean anyLocksDenied = false;
DatabaseImpl db = getDatabase();
EnvironmentImpl envImpl = db.getDbEnvironment();
for (int i = 0; i < getNEntries(); i++) {
final BasicLocker lockingTxn =
BasicLocker.createBasicLocker(envImpl);
/* Don't allow this short-lived lock to be preempted/stolen. */
lockingTxn.setPreemptable(false);
try {
/*
* We have to be able to lock the LN before we can compress the
* entry. If we can't, then, skip over it.
*
* We must lock the LN even if isKnownDeleted is true, because
* locks protect the aborts. (Aborts may execute multiple
* operations, where each operation latches and unlatches. It's
* the LN lock that protects the integrity of the whole
* multi-step process.)
*
* For example, during abort, there may be cases where we have
* deleted and then added an LN during the same txn. This
* means that to undo/abort it, we first delete the LN (leaving
* knownDeleted set), and then add it back into the tree. We
* want to make sure the entry is in the BIN when we do the
* insert back in.
*/
boolean deleteEntry = false;
Node n = null;
if (binRef == null ||
isEntryPendingDeleted(i) ||
isEntryKnownDeleted(i) ||
binRef.hasDeletedKey(new Key(getKey(i)))) {
if (canFetch) {
if (db.isDeferredWriteMode() &&
getLsn(i) == DbLsn.NULL_LSN) {
/* Null LSNs are ok in DW. [#15588] */
n = getTarget(i);
} else {
n = fetchTarget(i);
}
} else {
n = getTarget(i);
if (n == null) {
/* Punt, we don't know the state of this child. */
continue;
}
}
if (n == null) {
/* Cleaner deleted the log file. Compress this LN. */
deleteEntry = true;
} else if (isEntryKnownDeleted(i)) {
LockResult lockRet = lockingTxn.nonBlockingLock
(n.getNodeId(), LockType.READ, db);
if (lockRet.getLockGrant() == LockGrantType.DENIED) {
anyLocksDenied = true;
continue;
}
deleteEntry = true;
} else {
if (!n.containsDuplicates()) {
LN ln = (LN) n;
LockResult lockRet = lockingTxn.nonBlockingLock
(ln.getNodeId(), LockType.READ, db);
if (lockRet.getLockGrant() ==
LockGrantType.DENIED) {
anyLocksDenied = true;
continue;
}
if (ln.isDeleted()) {
deleteEntry = true;
}
}
}
/* Remove key from BINReference in case we requeue it. */
if (binRef != null) {
binRef.removeDeletedKey(new Key(getKey(i)));
}
}
/* At this point, we know we can delete. */
if (deleteEntry) {
boolean entryIsIdentifierKey = Key.compareKeys
(getKey(i), getIdentifierKey(),
getKeyComparator()) == 0;
if (entryIsIdentifierKey) {
/*
* We're about to remove the entry with the idKey so
* the node will need a new idkey.
*/
setNewIdKey = true;
}
if (db.isDeferredWriteMode() &&
n instanceof LN) {
LN ln = (LN) n;
long lsn = getLsn(i);
if (ln.isDirty() && lsn != DbLsn.NULL_LSN) {
if (db.isTemporary()) {
/*
* When a previously logged LN in a temporary
* DB is dirty, we can count the LSN of the
* last logged LN as obsolete without logging.
* There it no requirement for the dirty
* deleted LN to be durable past recovery.
* There is no danger of the last logged LN
* being accessed again (after log cleaning,
* for example), since temporary DBs do not
* survive recovery.
*/
if (localTracker != null) {
localTracker.countObsoleteNode
(lsn, ln.getLogType(),
ln.getLastLoggedSize(), db);
}
} else {
/*
* When a previously logged deferred-write LN
* is dirty, we log the dirty deleted LN to
* make the deletion durable. The act of
* logging will also count the last logged LSN
* as obsolete.
*/
logDirtyLN(i, ln, false /*force*/);
}
}
}
boolean deleteSuccess = deleteEntry(i, true);
assert deleteSuccess;
/*
* Since we're deleting the current entry, bump the current
* index back down one.
*/
i--;
}
} finally {
lockingTxn.operationEnd();
}
}
if (anyLocksDenied && binRef != null) {
db.getDbEnvironment().addToCompressorQueue(binRef, false);