remember next eviction time and skip eviction
This commit is contained in:
@@ -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();
|
||||||
final long oldestValuesToKeep = now - timeToLive;
|
|
||||||
LOGGER.trace("{}: oldestValuesToKeep = {} = {} - {}", name, oldestValuesToKeep, now, timeToLive);
|
if (nextProbableEvictionTime <= now) {
|
||||||
return evictInternal(oldestValuesToKeep, Integer.MAX_VALUE);
|
|
||||||
|
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) {
|
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);
|
||||||
|
|||||||
Reference in New Issue
Block a user