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