* root, IN.deleteEntry will traverse downwards.
*/
subtreeRoot = search
(idKey, SearchType.DELETE, -1, null, true /*updateGeneration*/);
LogManager logManager =
database.getDbEnvironment().getLogManager();
if (subtreeRoot == null) {
/*
* The root is the top of this subtree. If there are no more
* entries left in the root, delete the whole tree. There's a
* window on the rootLatch between the time that search releases
* the rootLatch and the acquire below. Something could insert
* into the tree. Use validateSubtreeForDelete to ensure that it's
* still empty.
*/
rootLatch.acquire();
try {
IN rootIN = (IN) root.fetchTarget(database, null);
DbConfigManager configManager =
database.getDbEnvironment().getConfigManager();
boolean purgeRoot = configManager.getBoolean
(EnvironmentParams.COMPRESSOR_PURGE_ROOT);
/**
* We've encountered the last empty subtree of the tree. In
* general, there's no reason to delete this last
* IN->...IN->BIN subtree since we're likely to to add more
* nodes to this tree again. Deleting the subtree also adds to
* the space used by the log since a MapLN needs to be written
* when the root is nulled, and a MapLN, IN (root), BIN needs
* to be written when the root is recreated.
*
* Consider a queue application which frequently inserts and
* deletes entries and often times leaves the tree empty, but
* will insert new records again.
*
* An optimization might be to prune the multiple IN path to
* the last BIN (if it even exists) to just a root IN pointing
* to the single BIN, but this doesn't feel like it's worth the
* trouble since the extra depth doesn't matter all that much.
*
* If je.compressor.purgeRoot is true, then we null the root.
*/
if (purgeRoot &&
(rootIN.getNEntries() <= 1) &&
(rootIN.validateSubtreeBeforeDelete(0))) {
/*
* The tree is empty, clear out the IN list. Can't just
* call clear() because there are IN's from more than one
* Database on the list.
*/
root = null;
treeEmpty = true;
/*
* Record the root deletion for recovery. Do this within
* the root latch. We need to put this log entry into the
* log before another thread comes in and creates a new
* rootIN for this database.
*
* For example,
* LSN 1000 IN delete info entry
* LSN 1010 new IN, for next set of inserts
* LSN 1020 new BIN, for next set of inserts.
*
* The entry at 1000 is needed so that LSN 1010 will
* properly supercede all previous IN entries in the tree.
*/
logManager.log(new INDeleteInfo
(rootIN.getNodeId(),
rootIN.getIdentifierKey(),
database.getId()));
/* Count obsolete nodes after logging the delete info. */
accountForSubtreeRemoval(inMemoryINs, rootIN, tracker);
}
} finally {
rootLatch.release();
}
} else {
try {
int index = subtreeRoot.findEntry(idKey, false, false);
IN subtreeRootIN = (IN) subtreeRoot.fetchTarget(index);
boolean deleteOk = subtreeRoot.deleteEntry(index, 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 INDeleteInfo
(subtreeRootIN.getNodeId(),
subtreeRootIN.getIdentifierKey(),
database.getId()));
/* Count obsolete nodes after logging the delete info. */