/** * Converts the given DIN and its descendants. * * <p>Enter/leave with bin field latched, although bin field will change to last inserted slot. */ private void convertDin(final DIN din, final byte[] binKey) { din.latch(); try { for (int i = 0; i < din.getNEntries(); i += 1) { final IN child = din.fetchIN(i, CacheMode.DEFAULT); assert (!child.isBINDelta(false)); if (child instanceof DBIN) { final DBIN dbin = (DBIN) child; dbin.latch(); try { for (int j = 0; j < dbin.getNEntries(); j += 1) { if (!isLNDeleted(dbin, j)) { convertDbinSlot(dbin, j, binKey); } } assert dbin.verifyMemorySize(); /* Count DBIN obsolete. */ if (dbin.getLastLoggedLsn() != DbLsn.NULL_LSN) { localTracker.countObsoleteNodeInexact( dbin.getLastLoggedLsn(), dbin.getLogType(), 0, dbin.getDatabase()); } } finally { dbin.releaseLatch(); } } else { convertDin((DIN) child, binKey); } /* Evict DIN child. */ din.detachNode(i, false /*updateLsn*/, -1 /*lsn*/); } assert din.verifyMemorySize(); /* Count DIN and DupCountLN obsolete. */ if (din.getLastLoggedLsn() != DbLsn.NULL_LSN) { localTracker.countObsoleteNodeInexact( din.getLastLoggedLsn(), din.getLogType(), 0, din.getDatabase()); } final ChildReference dupCountRef = din.getDupCountLNRef(); if (dupCountRef != null && dupCountRef.getLsn() != DbLsn.NULL_LSN) { localTracker.countObsoleteNodeInexact( dupCountRef.getLsn(), LogEntryType.LOG_DUPCOUNTLN, 0, din.getDatabase()); } } finally { din.releaseLatch(); } }
/** * Converts the given DIN and its descendants. * * <p>Enter/leave with bin field latched, although bin field will change to last inserted slot. */ private void convertDin(final DIN din, final byte[] binKey) { din.latch(); try { for (int i = 0; i < din.getNEntries(); i += 1) { final IN child = (IN) din.fetchTargetWithExclusiveLatch(i); if (child instanceof DBIN) { final DBIN dbin = (DBIN) child; dbin.latch(); try { for (int j = 0; j < dbin.getNEntries(); j += 1) { if (!isLNDeleted(dbin, j)) { convertDbinSlot(dbin, j, binKey); } } assert dbin.verifyMemorySize(); /* Count DBIN obsolete. */ if (dbin.getLastLoggedVersion() != DbLsn.NULL_LSN) { localTracker.countObsoleteNodeInexact( dbin.getLastLoggedVersion(), dbin.getLogType(), 0, dbin.getDatabase()); } } finally { dbin.releaseLatch(); } } else { convertDin((DIN) child, binKey); } /* Evict DIN child. */ din.updateNode(i, null, null); envImpl.getInMemoryINs().remove(child); } assert din.verifyMemorySize(); /* Count DIN and DupCountLN obsolete. */ if (din.getLastLoggedVersion() != DbLsn.NULL_LSN) { localTracker.countObsoleteNodeInexact( din.getLastLoggedVersion(), din.getLogType(), 0, din.getDatabase()); } final ChildReference dupCountRef = din.getDupCountLNRef(); if (dupCountRef != null && dupCountRef.getLsn() != DbLsn.NULL_LSN) { localTracker.countObsoleteNodeInexact( dupCountRef.getLsn(), LogEntryType.LOG_DUPCOUNTLN, 0, din.getDatabase()); } } finally { din.releaseLatch(); } }
/** * Compress this BIN by removing any entries that are deleted. Deleted entries are those that have * LN's marked deleted or if the knownDeleted flag is set. Caller is responsible for latching and * unlatching this node. * * @param binRef is used to determine the set of keys to be checked for deletedness, or is null to * check all keys. * @param canFetch if false, don't fetch any non-resident children. We don't want some callers of * compress, such as the evictor, to fault in other nodes. * @return true if we had to requeue the entry because we were unable to get locks, false if all * entries were processed and therefore any remaining deleted keys in the BINReference must * now be in some other BIN because of a split. */ @Override public boolean compress( BINReference binRef, boolean canFetch, LocalUtilizationTracker localTracker) throws DatabaseException { boolean ret = false; boolean setNewIdKey = false; boolean anyLocksDenied = false; DatabaseImpl db = getDatabase(); EnvironmentImpl envImpl = db.getDbEnvironment(); BasicLocker lockingTxn = BasicLocker.createBasicLocker(envImpl); try { for (int i = 0; i < getNEntries(); i++) { /* * 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; } /* * When deleting a deferred-write LN entry, we count the * last logged LSN as obsolete. */ if (localTracker != null && db.isDeferredWriteMode() && n instanceof LN) { LN ln = (LN) n; long lsn = getLsn(i); if (ln.isDirty() && lsn != DbLsn.NULL_LSN) { localTracker.countObsoleteNode(lsn, ln.getLogType(), ln.getLastLoggedSize(), db); } } boolean deleteSuccess = deleteEntry(i, true); assert deleteSuccess; /* * Since we're deleting the current entry, bump the current * index back down one. */ i--; } } } finally { if (lockingTxn != null) { lockingTxn.operationEnd(); } } if (anyLocksDenied && binRef != null) { db.getDbEnvironment().addToCompressorQueue(binRef, false); ret = true; } if (getNEntries() != 0 && setNewIdKey) { setIdentifierKey(getKey(0)); } /* This BIN is empty and expendable. */ if (getNEntries() == 0) { setGeneration(0); } return ret; }