assert in.getNEntries() > 0;
DIN duplicateRoot = null;
boolean dupCountLNLocked = false;
DupCountLN dcl = null;
BasicLocker locker = new BasicLocker(env);
LogManager logManager =
database.getDbEnvironment().getLogManager();
try {
int index = in.findEntry(dupKey, false, true);
if (index >= 0) {
duplicateRoot = (DIN) in.fetchTarget(index);
duplicateRoot.latch();
ChildReference dclRef = duplicateRoot.getDupCountLNRef();
dcl = (DupCountLN)
dclRef.fetchTarget(database, duplicateRoot);
/* Read lock the dup count LN. */
if (locker.nonBlockingReadLock(dcl.getNodeId(), database) ==
LockGrantType.DENIED) {
return false;
} else {
dupCountLNLocked = true;
}
/*
* We don't release the latch on 'in' before we search the
* duplicate tree below because we might be deleting the whole
* subtree from the IN and we want to keep it latched until we
* know.
*/
IN subtreeRoot;
try {
subtreeRoot = searchSubTree(duplicateRoot,
idKey,
SearchType.DELETE,
-1,
null,
true /*updateGeneration*/);
} catch (NodeNotEmptyException NNEE) {
/*
* We can't delete the subtree because there are still
* cursors pointing to the lowest node on it.
*/
in.releaseLatch();
throw NNEE;
}
if (subtreeRoot == null) {
/* We're deleting the duplicate root. */
BIN bin = (BIN) in;
if (bin.nCursors() == 0) {
try {
/*
* duplicateRoot is not currently latched. Relatch
* it and recheck if it still is deletable.
*/
duplicateRoot.latch();
if (duplicateRoot.isValidForDelete()) {
boolean deleteOk =
bin.deleteEntry(index, true);
assert deleteOk;
logManager.log(new INDupDeleteInfo
(duplicateRoot.getNodeId(),
duplicateRoot.getMainTreeKey(),
duplicateRoot.getDupTreeKey(),
database.getId()));
/*
* Count obsolete nodes after logging the
* delete info.
*/
accountForSubtreeRemoval
(inMemoryINs, duplicateRoot, tracker);
if (bin.getNEntries() == 0) {
database.getDbEnvironment().
addToCompressorQueue(bin, null, false);
}
}
} finally {
duplicateRoot.releaseLatch();
}
} else {
/*
* Don't delete anything off this IN if there are
* cursors referring to it.
*/
ret = false;
}
in.releaseLatch();
} else {
try {
/* We're deleting a portion of the duplicate tree. */
in.releaseLatch();
int dupIndex =
subtreeRoot.findEntry(idKey, false, false);
IN rootIN = (IN) subtreeRoot.fetchTarget(dupIndex);
boolean deleteOk =
subtreeRoot.deleteEntry(dupIndex, true);
assert deleteOk;
/*
* Record in the log the nodeid of the highest node in
* the subtree that we're deleting. We'll use this
* later to navigate to the right place if we need to
* replay this delete.
*/
logManager.log(new INDupDeleteInfo
(rootIN.getNodeId(),
rootIN.getMainTreeKey(),
rootIN.getDupTreeKey(),
database.getId()));
/*
* Count obsolete nodes after logging the delete info.
*/
accountForSubtreeRemoval(inMemoryINs, rootIN, tracker);
} finally {
subtreeRoot.releaseLatch();
}
}
}
} finally {
/*
* Release this IN, either because it didn't prove to be the target
* IN, or because we threw out of the attempt to delete the
* subtree.
*/
in.releaseLatchIfOwner();
if (duplicateRoot != null) {
duplicateRoot.releaseLatchIfOwner();
}
if (dupCountLNLocked) {
locker.releaseLock(dcl.getNodeId());
}
}
return ret;
}