add maxSize parameter to HotEntryCache
This commit is contained in:
@@ -2,9 +2,12 @@ package org.lucares.utils.cache;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -15,6 +18,8 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@@ -25,11 +30,17 @@ public class HotEntryCacheTest {
|
||||
Configurator.setRootLevel(Level.TRACE);
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HotEntryCacheTest.class);
|
||||
|
||||
private int cacheId = 0;
|
||||
|
||||
@Test(invocationCount = 1)
|
||||
public void testRemovalListenerCalledOnExpire() throws InterruptedException {
|
||||
|
||||
LOGGER.info("");
|
||||
LOGGER.info("");
|
||||
LOGGER.info("start: testRemovalListenerCalledOnExpire");
|
||||
|
||||
final long originalMinSleepPeriod = HotEntryCache.getMinSleepPeriod();
|
||||
final long originalMaxSleepPeriod = HotEntryCache.getMaxSleepPeriod();
|
||||
try {
|
||||
@@ -37,9 +48,10 @@ public class HotEntryCacheTest {
|
||||
final String value = "value";
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofMillis(1), "cache-" + ++cacheId);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofMillis(1), 10,
|
||||
"cache-" + ++cacheId);
|
||||
HotEntryCache.setMinSleepPeriod(1);
|
||||
HotEntryCache.setMaxSleepPeriod(10);
|
||||
HotEntryCache.setMaxSleepPeriod(2);
|
||||
cache.addListener((k, v) -> {
|
||||
Assert.assertEquals(k, key);
|
||||
Assert.assertEquals(v, value);
|
||||
@@ -56,7 +68,7 @@ public class HotEntryCacheTest {
|
||||
}
|
||||
|
||||
public void testPutAndGet() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10));
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
|
||||
|
||||
final String replacedNull = cache.put("key", "value1");
|
||||
Assert.assertEquals(replacedNull, null);
|
||||
@@ -74,7 +86,7 @@ public class HotEntryCacheTest {
|
||||
public void testPutTouches() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
|
||||
final Duration timeToLive = Duration.ofSeconds(10);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, clock);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
|
||||
|
||||
cache.put("key", "value1");
|
||||
|
||||
@@ -102,7 +114,7 @@ public class HotEntryCacheTest {
|
||||
public void testGetTouches() throws Exception {
|
||||
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
|
||||
final Duration timeToLive = Duration.ofSeconds(10);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, clock);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
|
||||
|
||||
cache.put("key", "value1");
|
||||
|
||||
@@ -121,7 +133,7 @@ public class HotEntryCacheTest {
|
||||
public void testEvictionByBackgroundThread() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
|
||||
final Duration timeToLive = Duration.ofSeconds(10);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, clock);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
|
||||
|
||||
final CompletableFuture<String> evictionEventFuture = new CompletableFuture<>();
|
||||
cache.addListener((key, value) -> {
|
||||
@@ -142,7 +154,11 @@ public class HotEntryCacheTest {
|
||||
}
|
||||
|
||||
public void testRemove() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10));
|
||||
LOGGER.info("");
|
||||
LOGGER.info("");
|
||||
LOGGER.info("start: testRemove");
|
||||
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
|
||||
|
||||
final List<String> removedValues = new ArrayList<>();
|
||||
cache.addListener((key, value) -> removedValues.add(value));
|
||||
@@ -158,7 +174,7 @@ public class HotEntryCacheTest {
|
||||
}
|
||||
|
||||
public void testClear() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10));
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
|
||||
|
||||
final List<String> removedValues = new ArrayList<>();
|
||||
cache.addListener((key, value) -> removedValues.add(value));
|
||||
@@ -177,7 +193,7 @@ public class HotEntryCacheTest {
|
||||
public void testForEachTouches() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
|
||||
final Duration timeToLive = Duration.ofSeconds(10);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, clock);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10, clock);
|
||||
|
||||
final CompletableFuture<String> evictionEventFuture = new CompletableFuture<>();
|
||||
cache.addListener((key, value) -> {
|
||||
@@ -220,7 +236,7 @@ public class HotEntryCacheTest {
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testPutIfAbsentIsAtomic() throws Exception {
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10));
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
|
||||
|
||||
final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
try {
|
||||
@@ -255,7 +271,7 @@ public class HotEntryCacheTest {
|
||||
}
|
||||
|
||||
public void testPutIfAbsentReturnsExistingValue() throws Exception {
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10));
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
|
||||
|
||||
final String key = "key";
|
||||
final String valueA = "A";
|
||||
@@ -271,7 +287,7 @@ public class HotEntryCacheTest {
|
||||
}
|
||||
|
||||
public void testPutIfAbsentDoesNotAddNull() throws Exception {
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10));
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofSeconds(10), 10);
|
||||
|
||||
final String key = "key";
|
||||
final String returnedByPutIfAbsent = cache.putIfAbsent(key, k -> null);
|
||||
@@ -281,6 +297,60 @@ public class HotEntryCacheTest {
|
||||
Assert.assertEquals(actualInCache, null);
|
||||
}
|
||||
|
||||
public void testMaxSizeIsRespected() {
|
||||
final int maxSize = 10;
|
||||
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofHours(1), maxSize, clock);
|
||||
final Set<String> removedKeys = new LinkedHashSet<>();
|
||||
cache.addListener((key, value) -> removedKeys.add(key));
|
||||
|
||||
// fill the cache
|
||||
int count = 0;
|
||||
for (count = 0; count < maxSize; count++) {
|
||||
|
||||
cache.put("key" + count, "value" + count);
|
||||
clock.plus(2L, ChronoUnit.MILLIS);
|
||||
cache.updateTime();
|
||||
}
|
||||
Assert.assertEquals(cache.size(), maxSize, "cache is full");
|
||||
Assert.assertEquals(removedKeys, List.of(), "removed keys at point A");
|
||||
|
||||
// add an item to a full cache -> the oldest 20% of the entries will be evicted
|
||||
// before the new entry is added
|
||||
cache.put("key" + count, "value" + count);
|
||||
clock.plus(2L, ChronoUnit.MILLIS);
|
||||
cache.updateTime();
|
||||
count++;
|
||||
|
||||
Assert.assertEquals(cache.size(), maxSize - 1, "cache was full, 20% (2 items) were removed and one added");
|
||||
Assert.assertEquals(removedKeys, Set.of("key0", "key1"), "removed keys at point B");
|
||||
}
|
||||
|
||||
public void testEvictionDueToSizeLimitDoesNotRemoveMoreThan20Percent() {
|
||||
final int maxSize = 10;
|
||||
final ModifiableFixedTimeClock clock = new ModifiableFixedTimeClock();
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(Duration.ofHours(1), maxSize, clock);
|
||||
final Set<String> removedKeys = new LinkedHashSet<>();
|
||||
cache.addListener((key, value) -> removedKeys.add(key));
|
||||
|
||||
// fill the cache
|
||||
int count = 0;
|
||||
for (count = 0; count < maxSize; count++) {
|
||||
// all entries get the same eviction time due to the fixed clock
|
||||
cache.put("key" + count, "value" + count);
|
||||
}
|
||||
Assert.assertEquals(cache.size(), maxSize, "cache is full");
|
||||
Assert.assertEquals(removedKeys, List.of(), "removed keys at point A");
|
||||
|
||||
// add an item to a full cache -> the oldest 20% of the entries will be evicted
|
||||
// before the new entry is added
|
||||
cache.put("key" + count, "value" + count);
|
||||
count++;
|
||||
|
||||
Assert.assertEquals(cache.size(), maxSize - 1, "cache was full, 20% (2 items) were removed and one added");
|
||||
Assert.assertEquals(removedKeys.size(), (int) (maxSize * 0.2), "number of removed keys at point B");
|
||||
}
|
||||
|
||||
private void sleep(final TimeUnit timeUnit, final long timeout) {
|
||||
try {
|
||||
timeUnit.sleep(timeout);
|
||||
@@ -302,7 +372,7 @@ public class HotEntryCacheTest {
|
||||
Configurator.setRootLevel(Level.TRACE);
|
||||
|
||||
final Duration timeToLive = Duration.ofSeconds(1);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive);
|
||||
final HotEntryCache<String, String> cache = new HotEntryCache<>(timeToLive, 10);
|
||||
|
||||
cache.addListener((key, value) -> {
|
||||
System.out.println(Instant.now() + " evicting: " + key + " -> " + value);
|
||||
|
||||
Reference in New Issue
Block a user