/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.internal.cache; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.logging.log4j.Logger; import org.apache.geode.InternalGemFireException; import org.apache.geode.cache.EvictionAction; import org.apache.geode.cache.EvictionAlgorithm; import org.apache.geode.cache.RegionDestroyedException; import org.apache.geode.internal.Assert; import org.apache.geode.internal.cache.control.InternalResourceManager; import org.apache.geode.internal.cache.lru.EnableLRU; import org.apache.geode.internal.cache.lru.HeapEvictor; import org.apache.geode.internal.cache.lru.HeapLRUCapacityController; import org.apache.geode.internal.cache.lru.LRUAlgorithm; import org.apache.geode.internal.cache.lru.LRUEntry; import org.apache.geode.internal.cache.lru.LRUStatistics; import org.apache.geode.internal.cache.lru.MemLRUCapacityController; import org.apache.geode.internal.cache.lru.NewLIFOClockHand; import org.apache.geode.internal.cache.lru.NewLRUClockHand; import org.apache.geode.internal.cache.versions.RegionVersionVector; import org.apache.geode.internal.cache.versions.VersionSource; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; import org.apache.geode.internal.logging.log4j.LocalizedMessage; import org.apache.geode.internal.logging.log4j.LogMarker; import org.apache.geode.internal.offheap.StoredObject; import org.apache.geode.internal.size.ReflectionSingleObjectSizer; /** * Abstract implementation of {@link RegionMap} that adds LRU behaviour. * * @since GemFire 3.5.1 * * */ public abstract class AbstractLRURegionMap extends AbstractRegionMap { private static final Logger logger = LogService.getLogger(); protected abstract void _setCCHelper(EnableLRU ccHelper); protected abstract EnableLRU _getCCHelper(); protected abstract void _setLruList(NewLRUClockHand lruList); protected abstract NewLRUClockHand _getLruList(); private LRUAlgorithm evictionController; protected AbstractLRURegionMap(InternalRegionArguments internalRegionArgs) { super(internalRegionArgs); } protected void initialize(Object owner, Attributes attr, InternalRegionArguments internalRegionArgs) { super.initialize(owner, attr, internalRegionArgs, true/* isLRU */); EvictionAlgorithm ea; LRUAlgorithm ec; if (owner instanceof LocalRegion) { ea = ((LocalRegion) owner).getEvictionAttributes().getAlgorithm(); ec = ((LocalRegion) owner).getEvictionController(); } else if (owner instanceof PlaceHolderDiskRegion) { PlaceHolderDiskRegion phdr = (PlaceHolderDiskRegion) owner; ea = phdr.getActualLruAlgorithm(); ec = phdr.getEvictionAttributes().createEvictionController(null, phdr.getOffHeap()); } else { throw new IllegalStateException("expected LocalRegion or PlaceHolderDiskRegion"); } this.evictionController = ec; if (ea.isLRUMemory()) { ((MemLRUCapacityController) ec).setEntryOverHead(getEntryOverHead()); } if (ea.isLRUHeap()) { ((HeapLRUCapacityController) ec).setEntryOverHead(getEntryOverHead()); } _setCCHelper(getHelper(ec)); /* * modification for LIFO Logic incubation * */ if (ea == EvictionAlgorithm.LIFO_ENTRY || ea == EvictionAlgorithm.LIFO_MEMORY) { _setLruList(new NewLIFOClockHand(owner, _getCCHelper(), internalRegionArgs)); } else { _setLruList(new NewLRUClockHand(owner, _getCCHelper(), internalRegionArgs)); } } @Override public void changeOwner(LocalRegion r) { super.changeOwner(r); _getLruList().setBucketRegion(r); this.evictionController.setBucketRegion(r); } /** * The delta produced during a put for activating LRU cannot be used while some outside party * (LocalRegion) has a segment locked... so we'll keep it in a thread local for a callback after * the segment is released. */ private final ThreadLocal lruDelta = new ThreadLocal(); private final ThreadLocal mustRemove = new ThreadLocal(); private final ThreadLocal callbackDisabled = new ThreadLocal(); private int getDelta() { Object d = lruDelta.get(); lruDelta.set(null); // We only want the delta consumed once if (d == null) return 0; return ((Integer) d).intValue(); } private void setDelta(int delta) { if (!getCallbackDisabled()) { if (getMustRemove()) { // after implementation of network-partition-detection we may // run into situations where a cache listener performs a cache // operation that needs to update LRU stats. In order to do this // we first have to execute the previous LRU actions. lruUpdateCallback(); } setMustRemove(true); } Integer delt = (Integer) lruDelta.get(); if (delt != null) { delta += delt.intValue(); } lruDelta.set(Integer.valueOf(delta)); } /** * Marker class to indicate that the wrapped value is owned by a CachedDeserializable and its form * is changing from serialized to deserialized. */ public static class CDValueWrapper { private final Object v; CDValueWrapper(Object v) { this.v = v; } public Object getValue() { return this.v; } } /** * Used when a CachedDeserializable's value changes form. PRECONDITION: caller has le synced * * @param le the entry whose CachedDeserializable's value changed. * @param cd the CachedDeserializable whose form has changed * @param v the new form of the CachedDeserializable's value. * @return true if finishExpandValue needs to be called * @since GemFire 6.1.2.9 */ public boolean beginChangeValueForm(LRUEntry le, CachedDeserializable cd, Object v) { // make sure this cached deserializable is still in the entry // @todo what if a clear is done and this entry is no longer in the region? { if (_getCCHelper().getEvictionAlgorithm().isLRUEntry()) { // no need to worry about the value changing form with entry LRU. return false; } Object curVal = le._getValue(); // OFFHEAP: _getValue ok if (curVal != cd) { if (cd instanceof StoredObject) { if (!cd.equals(curVal)) { return false; } } else { return false; } } } // TODO:KIRK:OK if (le.getValueInVM((RegionEntryContext) _getOwnerObject()) != cd) return false; boolean result = false; int delta = le.updateEntrySize(_getCCHelper(), new CDValueWrapper(v)); if (delta != 0) { result = true; boolean disabledLURCallbacks = disableLruUpdateCallback(); // by making sure that callbacks are disabled when we call // setDelta; it ensures that the setDelta will just inc the delta // value and not call lruUpdateCallback which we call in // finishChangeValueForm setDelta(delta); if (disabledLURCallbacks) { enableLruUpdateCallback(); } } // fix for bug 42090 if (_getCCHelper().getEvictionAlgorithm().isLRUHeap() && _isOwnerALocalRegion() && _getOwner() instanceof BucketRegion && HeapEvictor.EVICT_HIGH_ENTRY_COUNT_BUCKETS_FIRST) { result = false; } return result; } /** * @since GemFire 6.1.2.9 */ public void finishChangeValueForm() { lruUpdateCallback(); } private boolean getMustRemove() { Object d = mustRemove.get(); if (d == null) return false; return ((Boolean) d).booleanValue(); } private void setMustRemove(boolean b) { mustRemove.set(b ? Boolean.TRUE : null); } private boolean getCallbackDisabled() { Object d = callbackDisabled.get(); if (d == null) return false; return ((Boolean) d).booleanValue(); } private void setCallbackDisabled(boolean b) { callbackDisabled.set(b ? Boolean.TRUE : Boolean.FALSE); } /** * Returns the size of entries in this entries map */ public int getEntryOverHead() { RegionEntryFactory f = getEntryFactory(); if (f == null) { // fix for bug 31758 // SM initializes the entry factory late; when setOwner is called // because it is an unshared field. return 0; } else { return (int) ReflectionSingleObjectSizer.sizeof(f.getEntryClass()); } } /** unsafe audit code. */ public final void audit() { if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "Size of LRUMap = {}", sizeInVM()); } _getLruList().audit(); } /** * Evicts the given entry from the cache. Returns the total number of bytes evicted. 1. For action * local destroy, returns size(key + value) 2. For action evict to disk, returns size(value) * * @return number of bytes evicted, zero if no eviction took place */ protected int evictEntry(LRUEntry entry, LRUStatistics stats) throws RegionClearedException { EvictionAction action = _getCCHelper().getEvictionAction(); LocalRegion region = _getOwner(); if (action.isLocalDestroy()) { int size = entry.getEntrySize(); if (region.evictDestroy(entry)) { return size; } else { return 0; } } else if (action.isOverflowToDisk()) { Assert.assertTrue(entry instanceof DiskEntry); int change = 0; synchronized (entry) { if (entry.isInUseByTransaction()) { entry.unsetEvicted(); if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "No eviction of transactional entry for key={}", entry.getKey()); } return 0; } // Do the following check while synchronized to fix bug 31761 Token entryVal = entry.getValueAsToken(); if (entryVal == null) { if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "no need to evict already evicted key={}", entry.getKey()); } return 0; } if (Token.isInvalidOrRemoved(entryVal)) { // no need to evict these; it will not save any space // and the destroyed token needs to stay in memory if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "no need to evict {} token for key={}", entryVal, entry.getKey()); } return 0; } entry.setEvicted(); change = DiskEntry.Helper.overflowToDisk((DiskEntry) entry, region, _getCCHelper()); } boolean result = change < 0; if (result) { if (_getOwner() instanceof BucketRegion) { BucketRegion bucketRegion = (BucketRegion) _getOwner(); bucketRegion.updateCounter(change); // if(bucketRegion.getBucketAdvisor().isPrimary()){ stats.updateCounter(change); // } } else { stats.updateCounter(change); } } else { if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "no need to evict token for key={} because moving its value to disk resulted in a net change of {} bytes.", entry.getKey(), change); } } return change * -1; } else { throw new InternalGemFireException( LocalizedStrings.AbstractLRURegionMap_UNKNOWN_EVICTION_ACTION_0 .toLocalizedString(action)); } } /** * update the running counter of all the entries * * @param delta Description of the Parameter */ protected final void changeTotalEntrySize(int delta) { if (_isOwnerALocalRegion()) { if (_getOwner() instanceof BucketRegion) { BucketRegion bucketRegion = (BucketRegion) _getOwner(); bucketRegion.updateCounter(delta); } } _getLruList().stats().updateCounter(delta); if (delta > 0) { if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "total lru size is now: {}", getTotalEntrySize()); } } } /** * access the getHelper method on the eviction controller to initialize the ccHelper field. * * @param ec The governing eviction controller. * @return the helper instance from the eviction controller. */ private static EnableLRU getHelper(LRUAlgorithm ec) { return ec.getLRUHelper(); } @Override public void evictValue(Object key) { throw new IllegalStateException( "The evictValue is not supported on regions with eviction attributes."); } /** * Gets the total entry size limit for the map from the capacity controller helper. * * @return The total allowable size of this maps entries. */ protected final long getLimit() { if (_getOwner() instanceof BucketRegion) { BucketRegion bucketRegion = (BucketRegion) _getOwner(); return bucketRegion.getLimit(); } return _getLruList().stats().getLimit(); } public final LRUStatistics getLRUStatistics() { return _getLruList().stats(); } /** * return the current size of all the entries. * * @return The current size of all the entries. */ protected final long getTotalEntrySize() { if (_getOwnerObject() instanceof BucketRegion) { BucketRegion bucketRegion = (BucketRegion) _getOwner(); return bucketRegion.getCounter(); } return _getLruList().stats().getCounter(); } @Override public final void lruUpdateCallback() { final boolean isDebugEnabled_LRU = logger.isTraceEnabled(LogMarker.LRU); if (getCallbackDisabled()) { return; } final int delta = getDelta(); int bytesToEvict = delta; resetThreadLocals(); if (isDebugEnabled_LRU && _isOwnerALocalRegion()) { logger.trace(LogMarker.LRU, "lruUpdateCallback; list size is: {}; actual size is: {}; map size is: {}; delta is: {}; limit is: {}; tombstone count={}", getTotalEntrySize(), this._getLruList().getExpensiveListCount(), size(), delta, getLimit(), _getOwner().getTombstoneCount()); } LRUStatistics stats = _getLruList().stats(); if (!_isOwnerALocalRegion()) { changeTotalEntrySize(delta); // instead of evicting we just quit faulting values in } else if (_getCCHelper().getEvictionAlgorithm().isLRUHeap()) { changeTotalEntrySize(delta); try { while (bytesToEvict > 0 && _getCCHelper().mustEvict(stats, _getOwner(), bytesToEvict)) { boolean evictFromThisRegion = true; if (HeapEvictor.EVICT_HIGH_ENTRY_COUNT_BUCKETS_FIRST && _getOwner() instanceof BucketRegion) { long bytesEvicted = 0; long totalBytesEvicted = 0; List<BucketRegion> regions = ((BucketRegion) _getOwner()).getPartitionedRegion().getSortedBuckets(); Iterator<BucketRegion> iter = regions.iterator(); while (iter.hasNext()) { BucketRegion region = iter.next(); // only primaries can trigger inline eviction fix for 41814 if (!region.getBucketAdvisor().isPrimary()) { try { bytesEvicted = ((AbstractLRURegionMap) region.entries).centralizedLruUpdateCallback(); if (bytesEvicted == 0) { iter.remove(); } else { evictFromThisRegion = false; } totalBytesEvicted += bytesEvicted; bytesToEvict -= bytesEvicted; if (bytesEvicted > bytesToEvict) { bytesToEvict = 0; break; } if (totalBytesEvicted > bytesToEvict) { break; } } catch (RegionDestroyedException rd) { region.cache.getCancelCriterion().checkCancelInProgress(rd); } catch (Exception e) { region.cache.getCancelCriterion().checkCancelInProgress(e); logger.warn( LocalizedMessage.create(LocalizedStrings.Eviction_EVICTOR_TASK_EXCEPTION, new Object[] {e.getMessage()}), e); } } } } if (evictFromThisRegion) { LRUEntry removalEntry = (LRUEntry) _getLruList().getLRUEntry(); if (removalEntry != null) { int sizeOfValue = evictEntry(removalEntry, stats); if (sizeOfValue != 0) { bytesToEvict -= sizeOfValue; if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "evicted entry key={} total entry size is now: {} bytesToEvict :{}", removalEntry.getKey(), getTotalEntrySize(), bytesToEvict); } stats.incEvictions(); if (_isOwnerALocalRegion()) { if (_getOwner() instanceof BucketRegion) { ((BucketRegion) _getOwner()).incEvictions(1); } } if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "evictions={}", stats.getEvictions()); } _getCCHelper().afterEviction(); } } else { if (getTotalEntrySize() != 0) { if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "leaving evict loop early"); } } break; } } } } catch (RegionClearedException e) { // TODO Auto-generated catch block if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "exception ={}", e.getCause()); } } } else { try { // to fix bug 48285 do no evict if bytesToEvict <= 0. while (bytesToEvict > 0 && _getCCHelper().mustEvict(stats, _getOwner(), bytesToEvict)) { LRUEntry removalEntry = (LRUEntry) _getLruList().getLRUEntry(); if (removalEntry != null) { if (evictEntry(removalEntry, stats) != 0) { if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "evicted entry key(2)={} total entry size is now: {} bytesToEvict :", removalEntry.getKey(), getTotalEntrySize(), bytesToEvict); } stats.incEvictions(); if (_isOwnerALocalRegion()) { if (_getOwner() instanceof BucketRegion) { ((BucketRegion) _getOwner()).incEvictions(1); } } if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "evictions={}", stats.getEvictions()); } _getCCHelper().afterEviction(); } } else { if (getTotalEntrySize() != 0) { if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "leaving evict loop early"); } } break; } } changeTotalEntrySize(delta); } catch (RegionClearedException e) { // TODO Auto-generated catch block if (isDebugEnabled_LRU) { logger.debug("exception ={}", e.getCause()); } } } if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "callback complete. LRU size is now {}", _getLruList().stats().getCounter()); } // If in transaction context (either local or message) // reset the tx thread local } private boolean mustEvict() { LocalRegion owner = _getOwner(); InternalResourceManager resourceManager = owner.getCache().getResourceManager(); final boolean monitorStateIsEviction; if (!owner.getAttributes().getOffHeap()) { monitorStateIsEviction = resourceManager.getHeapMonitor().getState().isEviction(); } else { monitorStateIsEviction = resourceManager.getOffHeapMonitor().getState().isEviction(); } return monitorStateIsEviction && this.sizeInVM() > 0; } public final int centralizedLruUpdateCallback() { final boolean isDebugEnabled_LRU = logger.isTraceEnabled(LogMarker.LRU); int evictedBytes = 0; if (getCallbackDisabled()) { return evictedBytes; } getDelta(); resetThreadLocals(); if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "centralLruUpdateCallback: lru size is now {}, limit is: {}", getTotalEntrySize(), getLimit()); } LRUStatistics stats = _getLruList().stats(); try { while (mustEvict() && evictedBytes == 0) { LRUEntry removalEntry = (LRUEntry) _getLruList().getLRUEntry(); if (removalEntry != null) { evictedBytes = evictEntry(removalEntry, stats); if (evictedBytes != 0) { if (_getOwner() instanceof BucketRegion) { ((BucketRegion) _getOwner()).incEvictions(1); } stats.incEvictions(); if (isDebugEnabled_LRU) { logger.debug("evictions={}", stats.getEvictions()); } _getCCHelper().afterEviction(); } } else { if (getTotalEntrySize() != 0) { if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "leaving evict loop early"); } } break; } } } catch (RegionClearedException rce) { // Ignore if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "exception ={}", rce.getCause()); } } if (isDebugEnabled_LRU) { logger.trace(LogMarker.LRU, "callback complete"); } // If in transaction context (either local or message) // reset the tx thread local return evictedBytes; } /** * Update counter related to limit in list * * @since GemFire 5.7 */ // TODO this method acts as LRUupdateCallbacks // do we need to put it here are insert one level up public final void updateStats() { final int delta = getDelta(); resetThreadLocals(); if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "updateStats - delta is: {} total is: {} limit is: {}", delta, getTotalEntrySize(), getLimit()); } if (delta != 0) { changeTotalEntrySize(delta); } } @Override public final boolean disableLruUpdateCallback() { if (getCallbackDisabled()) { return false; } else { setCallbackDisabled(true); return true; } } @Override public final void enableLruUpdateCallback() { setCallbackDisabled(false); } // TODO rebalancing these methods are new on the // rebalancing branch but never used??? public final void disableLruUpdateCallbackForInline() { setCallbackDisabled(true); } public final void enableLruUpdateCallbackForInline() { setCallbackDisabled(false); } @Override public final void resetThreadLocals() { mustRemove.set(null); lruDelta.set(null); callbackDisabled.set(null); } @Override public final Set<VersionSource> clear(RegionVersionVector rvv) { _getLruList().clear(rvv); return super.clear(rvv); } /* * Asif : Motivation: An entry which is no longer existing in the system due to clear operation, * should not be present the LRUList being used by the region. * * Case1 : An entry has been written to disk & on its return code path, it invokes lruCreate or * lruUpdate. Before starting the operation of writing to disk, the HTree reference is set in the * threadlocal. A clear operation changes the Htree reference in a write lock. Thus if the htree * reference has not changed till this point, it would mean either the entry is still valid or a * clear operation is in progress but has not changed the Htree Reference . Since we store the * LRUList in a local variable, it implies that if clear occurs , it will go in the stale list & * if not it goes in the right list. Both ways we are safe. * * Case 2: The Htree reference has changed ( implying a clear conflic with put) but the entry is * valid. This is possible as we first set the Htree Ref in thread local. Now before the update * operation has acquired the entry , clear happens. As a result the update operation has become * create. Since the clear changes the Htree Ref & the LRUList in a write lock & hence by the time * the original update operation acquires the read lock, the LRUList has already been changed by * clear. Now in the update operation's return path the List which it stores in local variable is * bound to be the new List. Since our code checks if the entry reference exists in the region in * case of conflict & if yes, we append the entry to the List. It is guaranteed to be added to the * new List. * * Also it is necessary that when we clear the region, first the concurrent map of the region * containing entries needs to be cleared. The Htree Reference should be reset after that. And * then we should be resetting the LRUList. Previously the Htree reference was being set before * clearing the Map. This caused Bug 37606. If the order of clear operation on disk region is ( * incorrect ) 1) map.clear 2) Resetting the LRUList 3) Changing the Htree ref Then following bug * can occur., During entry operation on its return path, invokes lruUpdate/lruCreate. By that * time the clear proceeds & it has reset the LRUList & cleared the entries. But as the Htree ref * has not changed, we would take the locally available LRUList ( which may be the new List) & * append the entry to the List. * * * */ @Override protected final void lruEntryCreate(RegionEntry re) { LRUEntry e = (LRUEntry) re; // Assert.assertFalse(e._getValue() instanceof DiskEntry.RecoveredEntry) if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "lruEntryCreate for key={}; list size is: {}; actual size is: {}; map size is: {}; entry size: {}; in lru clock: {}", re.getKey(), getTotalEntrySize(), this._getLruList().getExpensiveListCount(), size(), e.getEntrySize(), !e.testEvicted()); } // this.lruCreatedKey = re.getKey(); // [ bruce ] for DEBUGGING only e.unsetEvicted(); NewLRUClockHand lruList = _getLruList(); DiskRegion disk = _getOwner().getDiskRegion(); boolean possibleClear = disk != null && disk.didClearCountChange(); if (!possibleClear || this._getOwner().basicGetEntry(re.getKey()) == re) { lruList.appendEntry(e); lruEntryUpdate(e); } } @Override protected final void lruEntryUpdate(RegionEntry re) { final LRUEntry e = (LRUEntry) re; setDelta(e.updateEntrySize(_getCCHelper())); if (logger.isDebugEnabled()) { logger.debug("lruEntryUpdate for key={} size={}", re.getKey(), e.getEntrySize()); } NewLRUClockHand lruList = _getLruList(); if (_isOwnerALocalRegion()) { DiskRegion disk = _getOwner().getDiskRegion(); boolean possibleClear = disk != null && disk.didClearCountChange(); if (!possibleClear || this._getOwner().basicGetEntry(re.getKey()) == re) { if (e instanceof DiskEntry) { if (!e.testEvicted()) { lruList.appendEntry(e); } } // Why reset the refcount? All the txs that currently reference // this region entry still do but they will now fail with conflicts. // But they also have logic in them to dec the refcount. // I think we did the resetRefCount thinking it was safe // to drop it back to zero since the RE was modified and give // us a chance to evict it. But if the txs that are going to fail // with a conflict still do refCountDecs then after the reset any // new txs that inc the refcount may have their count decd by one of // the old txs allowing the entry to be evicted and causing a tx conflict. // TODO: this should now be safe but why the odd condition for this block // and why call lruList.appendEntry twice (once above and once in resetRefCount. // Also lruEntryUpdate only happens on an lru. Do we need to call reset for the non-lru // (expiration) case? e.resetRefCount(lruList); } } else { // We are recovering the region so it is a DiskEntry. // Also clear is not yet possible and this entry will be in the region. // No need to call resetRefCount since tx are not yet possible. if (!e.testEvicted()) { lruList.appendEntry(e); } } } @Override protected final void lruEntryDestroy(RegionEntry re) { final LRUEntry e = (LRUEntry) re; if (logger.isTraceEnabled(LogMarker.LRU)) { logger.trace(LogMarker.LRU, "lruEntryDestroy for key={}; list size is: {}; actual size is: {}; map size is: {}; entry size: {}; in lru clock: {}", re.getKey(), getTotalEntrySize(), this._getLruList().getExpensiveListCount(), size(), e.getEntrySize(), !e.testEvicted()); } // if (this.lruCreatedKey == re.getKey()) { // String method = Thread.currentThread().getStackTrace()[5].getMethodName(); // } // boolean wasEvicted = e.testEvicted(); /* boolean removed = */_getLruList().unlinkEntry(e); // if (removed || wasEvicted) { // evicted entries have already been removed from the list changeTotalEntrySize(-1 * e.getEntrySize());// subtract the size. Token vTok = re.getValueAsToken(); if (vTok == Token.DESTROYED || vTok == Token.TOMBSTONE) { // OFFHEAP noop TODO: use // re.isDestroyedOrTombstone // if in token mode we need to recalculate the size of the entry since it's // staying in the map and may be resurrected e.updateEntrySize(_getCCHelper()); } // } else if (debug) { // debugLogging("entry not removed from LRU list"); // } } /** * Called by DiskEntry.Helper.faultInValue */ @Override public final void lruEntryFaultIn(LRUEntry e) { if (logger.isDebugEnabled()) { logger.debug("lruEntryFaultIn for key={} size={}", e.getKey(), e.getEntrySize()); } NewLRUClockHand lruList = _getLruList(); if (_isOwnerALocalRegion()) { DiskRegion disk = _getOwner().getDiskRegion(); boolean possibleClear = disk != null && disk.didClearCountChange(); if (!possibleClear || this._getOwner().basicGetEntry(e.getKey()) == e) { lruEntryUpdate(e); e.unsetEvicted(); lruList.appendEntry(e); } } else { lruEntryUpdate(e); lruList.appendEntry(e); } } @Override public final void decTxRefCount(RegionEntry re) { LocalRegion lr = null; if (_isOwnerALocalRegion()) { lr = _getOwner(); } ((LRUEntry) re).decRefCount(_getLruList(), lr); } @Override public final boolean lruLimitExceeded() { return _getCCHelper().mustEvict(_getLruList().stats(), null, 0); } @Override public void lruCloseStats() { _getLruList().closeStats(); } @Override final boolean confirmEvictionDestroy(RegionEntry re) { // We assume here that a LRURegionMap contains LRUEntries LRUEntry lruRe = (LRUEntry) re; if (lruRe.isInUseByTransaction() || lruRe.isDestroyed()) { lruRe.unsetEvicted(); return false; } else { return true; } } }