Skip to content

Commit

Permalink
sync with guava's cache test changes
Browse files Browse the repository at this point in the history
Note that since Caffeine's refresh is linearizable, we invert their
test that asserts the stale reload populates the cache and instead we
discard it as no longer reliable.
  • Loading branch information
ben-manes committed Sep 2, 2024
1 parent 967f17a commit b6f745a
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,8 @@ public void testRemovalNotification_clear_basher() throws InterruptedException {
// notification.
assertEquals(expectedKeys, Sets.union(cache.asMap().keySet(), removalNotifications.keySet()));
assertTrue(Collections.disjoint(cache.asMap().keySet(), removalNotifications.keySet()));
threadPool.shutdown();
threadPool.awaitTermination(300, SECONDS);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
Expand All @@ -54,6 +56,7 @@
import com.google.common.util.concurrent.ExecutionError;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.common.util.concurrent.Uninterruptibles;
Expand Down Expand Up @@ -2235,6 +2238,55 @@ public String load(String key) {
assertEquals(refreshKey + suffix, map.get(refreshKey));
}

@SuppressWarnings({"CheckReturnValue", "FutureReturnValueIgnored"})
public void testLongAsyncRefresh() throws Exception {
FakeTicker ticker = new FakeTicker();
AtomicInteger counter = new AtomicInteger();
CountDownLatch reloadStarted = new CountDownLatch(1);
CountDownLatch reloadExpired = new CountDownLatch(1);
CountDownLatch reloadCompleted = new CountDownLatch(1);

ListeningExecutorService refreshExecutor = MoreExecutors.listeningDecorator(
Executors.newSingleThreadExecutor());
try {
Caffeine<Object, Object> builder = Caffeine.newBuilder()
.expireAfterWrite(100, MILLISECONDS)
.refreshAfterWrite(5, MILLISECONDS)
.executor(refreshExecutor)
.ticker(ticker::read);

CacheLoader<String, String> loader =
new CacheLoader<String, String>() {
@Override public String load(String key) {
return key + "Load-" + counter.incrementAndGet();
}
@Override public ListenableFuture<String> reload(String key, String oldValue) {
ListenableFuture<String> future = refreshExecutor.submit(() -> {
reloadStarted.countDown();
reloadExpired.await();
return key + "Reload";
});
future.addListener(reloadCompleted::countDown, refreshExecutor);
return future;
}
};
LoadingCache<String, String> cache = CaffeinatedGuava.build(builder, loader);

assertThat(cache.get("test")).isEqualTo("testLoad-1");

ticker.advance(10, MILLISECONDS); // so that the next call will trigger refresh
assertThat(cache.get("test")).isEqualTo("testLoad-1");
reloadStarted.await();
ticker.advance(500, MILLISECONDS); // so that the entry expires during the reload
reloadExpired.countDown();

assertThat(cache.get("test")).isEqualTo("testLoad-2");
} finally {
refreshExecutor.shutdown();
refreshExecutor.awaitTermination(Duration.ofMinutes(1));
}
}

static <T> Callable<T> throwing(final Exception exception) {
return new Callable<T>() {
@Override public T call() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public boolean equals(Object o) {
return false;
}
var node = (Node) o;
return (key == node.key) && (priority == node.priority);
return compareTo(node) == 0;
}

@Override
Expand Down

0 comments on commit b6f745a

Please sign in to comment.