remember next eviction time and skip eviction

This commit is contained in:
2019-08-24 19:39:59 +02:00
parent 6eaf4e10fc
commit 3a7688d1ae

View File

@@ -218,7 +218,6 @@ public class HotEntryCache<K, V> {
caches.addAll(weakCaches.keySet()); caches.addAll(weakCaches.keySet());
} }
for (final HotEntryCache<?, ?> cache : caches) { for (final HotEntryCache<?, ?> cache : caches) {
// TODO remember nextEvictionTime on cache so that we can skip a cache
final long nextEvictionTime = cache.evict(); final long nextEvictionTime = cache.evict();
minNextEvictionTime = Math.min(minNextEvictionTime, nextEvictionTime); minNextEvictionTime = Math.min(minNextEvictionTime, nextEvictionTime);
} }
@@ -291,6 +290,8 @@ public class HotEntryCache<K, V> {
private int maxSize; private int maxSize;
private long nextProbableEvictionTime;
HotEntryCache(final Duration timeToLive, final int maxSize, final Clock clock, final String name) { HotEntryCache(final Duration timeToLive, final int maxSize, final Clock clock, final String name) {
this.timeToLive = timeToLive.toMillis(); this.timeToLive = timeToLive.toMillis();
this.maxSize = maxSize; this.maxSize = maxSize;
@@ -333,6 +334,7 @@ public class HotEntryCache<K, V> {
static void setMaxSleepPeriod(final long maxSleepPeriodInMs) { static void setMaxSleepPeriod(final long maxSleepPeriodInMs) {
EVICTER.setMaxSleepPeriod(maxSleepPeriodInMs); EVICTER.setMaxSleepPeriod(maxSleepPeriodInMs);
TIME_UPDATER.setUpdateInterval(EVICTER.getMinSleepPeriod());
} }
static long getMinSleepPeriod() { static long getMinSleepPeriod() {
@@ -353,7 +355,7 @@ public class HotEntryCache<K, V> {
public V put(final K key, final V value) { public V put(final K key, final V value) {
removeEldestCacheTooBig(); removeEldestIfCacheTooBig();
final boolean wasEmptyBefore = cache.isEmpty(); final boolean wasEmptyBefore = cache.isEmpty();
final AtomicReference<V> oldValueAtomicReference = new AtomicReference<>(); final AtomicReference<V> oldValueAtomicReference = new AtomicReference<>();
@@ -368,6 +370,7 @@ public class HotEntryCache<K, V> {
} else { } else {
final long creationTime = now(); final long creationTime = now();
entry = new Entry<>(value, creationTime); entry = new Entry<>(value, creationTime);
nextProbableEvictionTime = Math.min(nextProbableEvictionTime, now + timeToLive);
} }
touch(k, entry); touch(k, entry);
return entry; return entry;
@@ -396,13 +399,14 @@ public class HotEntryCache<K, V> {
*/ */
public V putIfAbsent(final K key, final Function<K, V> mappingFunction) { public V putIfAbsent(final K key, final Function<K, V> mappingFunction) {
removeEldestCacheTooBig(); removeEldestIfCacheTooBig();
final boolean wasEmptyBefore = cache.isEmpty(); final boolean wasEmptyBefore = cache.isEmpty();
final Entry<V> entry = cache.computeIfAbsent(key, (k) -> { final Entry<V> entry = cache.computeIfAbsent(key, (k) -> {
final V value = mappingFunction.apply(k); final V value = mappingFunction.apply(k);
final long creationTime = now(); final long creationTime = now;
final Entry<V> e = new Entry<>(value, creationTime); final Entry<V> e = new Entry<>(value, creationTime);
nextProbableEvictionTime = Math.min(nextProbableEvictionTime, now + timeToLive);
touch(key, e); touch(key, e);
return e; return e;
}); });
@@ -440,7 +444,7 @@ public class HotEntryCache<K, V> {
}); });
} }
private void removeEldestCacheTooBig() { private void removeEldestIfCacheTooBig() {
if (cache.size() >= maxSize) { if (cache.size() >= maxSize) {
removeEldest(); removeEldest();
} }
@@ -461,22 +465,29 @@ public class HotEntryCache<K, V> {
final int numEntriesToRemove = Math.max((int) (maxSize * 0.2), 1); final int numEntriesToRemove = Math.max((int) (maxSize * 0.2), 1);
final long oldestValuesToKeep = lastAccessTimes.get(numEntriesToRemove - 1) + 1; final long oldestValuesToKeep = lastAccessTimes.get(numEntriesToRemove - 1) + 1;
evictInternal(oldestValuesToKeep, numEntriesToRemove); nextProbableEvictionTime = evictInternal(oldestValuesToKeep, numEntriesToRemove);
} }
} }
private long evict() { private long evict() {
final long now = now(); final long now = now();
if (nextProbableEvictionTime <= now) {
final long oldestValuesToKeep = now - timeToLive; final long oldestValuesToKeep = now - timeToLive;
LOGGER.trace("{}: oldestValuesToKeep = {} = {} - {}", name, oldestValuesToKeep, now, timeToLive);
return evictInternal(oldestValuesToKeep, Integer.MAX_VALUE); nextProbableEvictionTime = evictInternal(oldestValuesToKeep, Integer.MAX_VALUE);
} else {
LOGGER.trace("{}: skip eviction - next eviction at {} (now: {})", name, nextProbableEvictionTime, now);
}
return nextProbableEvictionTime;
} }
private long evictInternal(final long oldestValuesToKeep, final int maxEntriesToRemove) { private long evictInternal(final long oldestValuesToKeep, final int maxEntriesToRemove) {
long oldestAccessTime = Long.MAX_VALUE;
LOGGER.trace("{}: cache size before eviction {}", name, cache.size());
LOGGER.trace("{}: oldest value to keep {}", name, oldestValuesToKeep);
LOGGER.trace("{}: cache size before eviction {}", name, cache.size());
long oldestAccessTime = Long.MAX_VALUE;
final AtomicInteger removedEntries = new AtomicInteger(); final AtomicInteger removedEntries = new AtomicInteger();
for (final java.util.Map.Entry<K, Entry<V>> mapEntry : cache.entrySet()) { for (final java.util.Map.Entry<K, Entry<V>> mapEntry : cache.entrySet()) {
@@ -487,29 +498,21 @@ public class HotEntryCache<K, V> {
if (removedEntries.get() >= maxEntriesToRemove) { if (removedEntries.get() >= maxEntriesToRemove) {
// finish iterating over all entries so that this method return the correct // finish iterating over all entries so that this method return the correct
// nextEvictionTime // nextEvictionTime
LOGGER.trace("{}: removedEntries >= maxEntriesToRemove = {} >= {}", name, removedEntries.get(),
maxEntriesToRemove);
continue; continue;
} }
if (lastAccessed >= oldestValuesToKeep) { if (lastAccessed >= oldestValuesToKeep) {
LOGGER.trace("{}: lastAccessed >= oldestValuesToKeep = {} >= {}", name, lastAccessed,
oldestValuesToKeep);
continue; continue;
} }
final K keyToBeRemoved = mapEntry.getKey(); final K keyToBeRemoved = mapEntry.getKey();
LOGGER.trace("{}: atomically removing key {}", name, keyToBeRemoved);
cache.computeIfPresent(keyToBeRemoved, (k, e) -> { cache.computeIfPresent(keyToBeRemoved, (k, e) -> {
if (entry.getLastAccessed() < oldestValuesToKeep) { if (entry.getLastAccessed() < oldestValuesToKeep) {
LOGGER.trace("{}: removing value from {}", name, entry.getLastAccessed());
removedEntries.incrementAndGet(); removedEntries.incrementAndGet();
handleEvent(k, e.getValue()); handleEvent(k, e.getValue());
return null; return null;
} else {
LOGGER.trace("{}: keeping value from {}", name, entry.getLastAccessed());
} }
return e; return e;
}); });
@@ -529,7 +532,6 @@ public class HotEntryCache<K, V> {
// visible for test // visible for test
void updateTime() { void updateTime() {
now = clock.millis(); now = clock.millis();
LOGGER.trace("{}: update time to {}", name, now);
} }
private void touch(final K key, final Entry<V> entry) { private void touch(final K key, final Entry<V> entry) {
@@ -541,8 +543,6 @@ public class HotEntryCache<K, V> {
} }
private void handleEvent(final K key, final V value) { private void handleEvent(final K key, final V value) {
LOGGER.trace("{}: calling {} listeners for {} -> {}", name, listeners.size(), key, value);
for (final EventListener<K, V> eventSubscribers : listeners) { for (final EventListener<K, V> eventSubscribers : listeners) {
eventSubscribers.onRemove(key, value); eventSubscribers.onRemove(key, value);