private void secondUpdate(AsyncLoadingCache<String, String> cache, ConcurrentMap<String, String> source) throws Exception { source.put(A_KEY, A_UPDATE_2); source.put(B_KEY, B_UPDATE_2); assertThat("serve cached first updated value", cache.get(A_KEY), is(futureOf(A_UPDATE_1))); assertThat("serve cached first updated value", cache.get(B_KEY), is(futureOf(B_UPDATE_1))); Thread.sleep(EPSILON); // sleep for less than expiration assertThat("serve cached first updated value", cache.get(A_KEY), is(futureOf(A_UPDATE_1))); assertThat("serve cached first updated value", cache.get(A_KEY), is(futureOf(A_UPDATE_1))); }
@CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(loader = Loader.EXCEPTIONAL) public void get_absent_failure(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { CompletableFuture<Integer> future = cache.get(context.absentKey()); assertThat(future.isCompletedExceptionally(), is(true)); assertThat(cache.getIfPresent(context.absentKey()), is(nullValue())); }
@Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, expiryTime = Expire.ONE_MINUTE, mustExpireWithAnyOf = { AFTER_WRITE, VARIABLE }, expiry = { CacheExpiry.DISABLED, CacheExpiry.WRITE }, expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE}) @SuppressWarnings("FutureReturnValueIgnored") public void get_writeTime(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { Integer key = context.absentKey(); Integer value = context.absentValue(); cache.get(key, k -> { context.ticker().advance(5, TimeUnit.MINUTES); return value; }); assertThat(cache.synchronous().estimatedSize(), is(1L)); assertThat(cache.getIfPresent(key), futureOf(value)); }
private void initialValues(AsyncLoadingCache<String, String> cache, ConcurrentMap<String, String> source, ConcurrentMap<String, Date> lastLoad) throws InterruptedException, ExecutionException { source.put(A_KEY, A_ORIGINAL); source.put(B_KEY, B_ORIGINAL); lastLoad.clear(); assertThat("should serve initial value", cache.get(A_KEY), is(futureOf(A_ORIGINAL))); assertThat("should serve initial value", cache.get(B_KEY), is(futureOf(B_ORIGINAL))); }
@CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(refreshAfterWrite = Expire.ONE_MINUTE, population = { Population.PARTIAL, Population.FULL }) @SuppressWarnings("FutureReturnValueIgnored") public void get(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { context.ticker().advance(30, TimeUnit.SECONDS); cache.get(context.firstKey()); cache.get(context.absentKey()); context.ticker().advance(45, TimeUnit.SECONDS); assertThat(cache.getIfPresent(context.firstKey()), is(futureOf(-context.firstKey()))); assertThat(cache, hasRemovalNotifications(context, 1, RemovalCause.REPLACED)); }
private void firstUpdate(AsyncLoadingCache<String, String> cache, ConcurrentMap<String, String> source) throws InterruptedException, ExecutionException { source.put(A_KEY, A_UPDATE_1); source.put(B_KEY, B_UPDATE_1); assertThat("should serve cached initial value", cache.get(A_KEY), is(futureOf(A_ORIGINAL))); assertThat("should serve cached initial value", cache.get(B_KEY), is(futureOf(B_ORIGINAL))); Thread.sleep(EPSILON); // sleep for less than expiration assertThat("still serve cached initial value", cache.get(A_KEY), is(futureOf(A_ORIGINAL))); assertThat("still serve cached initial value", cache.get(B_KEY), is(futureOf(B_ORIGINAL))); Thread.sleep(TTL + EPSILON); // sleep until expiration assertThat("now serve first updated value", cache.get(A_KEY), is(futureOf(A_UPDATE_1))); assertThat("now serve first updated value", cache.get(B_KEY), is(futureOf(B_UPDATE_1))); }
@Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, removalListener = Listener.CONSUMING, mustExpireWithAnyOf = { AFTER_ACCESS, AFTER_WRITE, VARIABLE }, expiry = { CacheExpiry.DISABLED, CacheExpiry.CREATE, CacheExpiry.WRITE, CacheExpiry.ACCESS }, expireAfterAccess = {Expire.DISABLED, Expire.ONE_MINUTE}, expiryTime = Expire.ONE_MINUTE, expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE}) @SuppressWarnings("FutureReturnValueIgnored") public void get_async(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { CompletableFuture<Integer> future = new CompletableFuture<Integer>(); cache.get(context.absentKey(), (k, e) -> future); context.ticker().advance(2, TimeUnit.MINUTES); cache.synchronous().cleanUp(); assertThat(cache, hasRemovalNotifications(context, 0, RemovalCause.EXPIRED)); future.complete(context.absentValue()); context.ticker().advance(30, TimeUnit.SECONDS); assertThat(cache.getIfPresent(context.absentKey()), is(future)); context.ticker().advance(1, TimeUnit.MINUTES); assertThat(cache.getIfPresent(context.absentKey()), is(nullValue())); cache.synchronous().cleanUp(); assertThat(cache, hasRemovalNotifications(context, 1, RemovalCause.EXPIRED)); verifyWriter(context, (verifier, writer) -> verifier.deletions(1, RemovalCause.EXPIRED)); }
@CacheSpec @CheckNoWriter @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void get_null(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { cache.get(null); }
@CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(loader = Loader.EXCEPTIONAL, executor = CacheExecutor.THREADED, executorFailure = ExecutorFailure.IGNORED) public void get_absent_failure_async(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) throws InterruptedException { AtomicBoolean done = new AtomicBoolean(); Integer key = context.absentKey(); CompletableFuture<Integer> valueFuture = cache.get(key); valueFuture.whenComplete((r, e) -> done.set(true)); Awaits.await().untilTrue(done); Awaits.await().until(() -> !cache.synchronous().asMap().containsKey(context.absentKey())); Awaits.await().until(() -> context, both(hasMissCount(1)).and(hasHitCount(0))); Awaits.await().until(() -> context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1))); assertThat(valueFuture.isCompletedExceptionally(), is(true)); assertThat(cache.getIfPresent(key), is(nullValue())); }
@CacheSpec @CheckNoWriter @Test(dataProvider = "caches") public void get_absent(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { assertThat(cache.get(context.absentKey()), is(futureOf(context.absentValue()))); }
@Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void put_replace(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { CompletableFuture<Integer> value = CompletableFuture.completedFuture(context.absentValue()); for (Integer key : context.firstMiddleLastKeys()) { cache.put(key, value); assertThat(cache.get(key), is(futureOf(context.absentValue()))); } assertThat(cache.synchronous().estimatedSize(), is(context.initialSize())); int count = context.firstMiddleLastKeys().size(); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); }
@Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, loader = Loader.IDENTITY, removalListener = Listener.CONSUMING, expiryTime = Expire.ONE_MINUTE, mustExpireWithAnyOf = { AFTER_ACCESS, AFTER_WRITE, VARIABLE }, expiry = { CacheExpiry.DISABLED, CacheExpiry.CREATE, CacheExpiry.WRITE, CacheExpiry.ACCESS }, expireAfterAccess = {Expire.DISABLED, Expire.ONE_MINUTE}, expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE}) @SuppressWarnings("FutureReturnValueIgnored") public void get(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { context.ticker().advance(2, TimeUnit.MINUTES); cache.get(context.firstKey()); cache.get(context.middleKey(), k -> context.absentValue()); cache.get(context.lastKey(), (k, executor) -> CompletableFuture.completedFuture(context.absentValue())); long count = context.initialSize(); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.EXPIRED)); }
@CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void get_present(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { assertThat(cache.get(context.firstKey()), futureOf(-context.firstKey())); assertThat(cache.get(context.middleKey()), futureOf(-context.middleKey())); assertThat(cache.get(context.lastKey()), futureOf(-context.lastKey())); assertThat(context, both(hasMissCount(0)).and(hasHitCount(3))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(0))); }
@Test(dataProvider = "caches", timeOut = 5000) // Issue #69 @CacheSpec(implementation = Implementation.Caffeine, population = Population.EMPTY, executor = CacheExecutor.THREADED, compute = Compute.ASYNC, values = ReferenceType.STRONG) public void refresh_deadlock(CacheContext context) { CompletableFuture<Integer> future = new CompletableFuture<>(); AsyncLoadingCache<Integer, Integer> cache = context.buildAsync((k, e) -> future); cache.synchronous().refresh(context.absentKey()); CompletableFuture<Integer> get = cache.get(context.absentKey()); future.complete(context.absentValue()); assertThat(get, futureOf(context.absentValue())); }
@CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(refreshAfterWrite = Expire.ONE_MINUTE, population = { Population.PARTIAL, Population.FULL }) @SuppressWarnings("FutureReturnValueIgnored") public void getFunc(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { Function<Integer, Integer> mappingFunction = context.original()::get; context.ticker().advance(30, TimeUnit.SECONDS); cache.get(context.firstKey(), mappingFunction); context.ticker().advance(45, TimeUnit.SECONDS); cache.get(context.lastKey(), mappingFunction); // refreshed assertThat(cache.synchronous().estimatedSize(), is(context.initialSize())); assertThat(cache, hasRemovalNotifications(context, 1, RemovalCause.REPLACED)); }
@Test(dataProvider = "caches") @CacheSpec(keys = ReferenceType.WEAK, values = ReferenceType.STRONG, expireAfterAccess = Expire.DISABLED, expireAfterWrite = Expire.DISABLED, maximumSize = Maximum.DISABLED, weigher = CacheWeigher.DEFAULT, population = Population.FULL, stats = Stats.ENABLED, loader = Loader.IDENTITY, removalListener = Listener.CONSUMING) public void get(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { context.clear(); GcFinalization.awaitFullGc(); assertThat(cache.get(context.absentKey()), is(futureOf(context.absentKey()))); long count = context.initialSize(); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.COLLECTED)); verifyWriter(context, (verifier, writer) -> verifier.deletions(count, RemovalCause.COLLECTED)); }
@Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Caffeine, population = Population.EMPTY, refreshAfterWrite = Expire.ONE_MINUTE, executor = CacheExecutor.THREADED, compute = Compute.ASYNC, values = ReferenceType.STRONG) public void get_sameFuture(CacheContext context) { AtomicBoolean done = new AtomicBoolean(); AsyncLoadingCache<Integer, Integer> cache = context.buildAsync(key -> { await().untilTrue(done); return -key; }); Integer key = 1; cache.synchronous().put(key, key); CompletableFuture<Integer> original = cache.get(key); for (int i = 0; i < 10; i++) { context.ticker().advance(1, TimeUnit.MINUTES); CompletableFuture<Integer> next = cache.get(key); assertThat(next, is(sameInstance(original))); } done.set(true); await().until(() -> cache.synchronous().getIfPresent(key), is(-key)); }
@Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Caffeine, population = Population.EMPTY, executor = CacheExecutor.THREADED, compute = Compute.ASYNC, values = ReferenceType.STRONG) public void refresh(CacheContext context) { AtomicBoolean done = new AtomicBoolean(); AsyncLoadingCache<Integer, Integer> cache = context.buildAsync(key -> { await().untilTrue(done); return -key; }); Integer key = 1; cache.synchronous().put(key, key); CompletableFuture<Integer> original = cache.get(key); for (int i = 0; i < 10; i++) { context.ticker().advance(1, TimeUnit.SECONDS); cache.synchronous().refresh(key); CompletableFuture<Integer> next = cache.get(key); assertThat(next, is(sameInstance(original))); } done.set(true); await().until(() -> cache.synchronous().getIfPresent(key), is(-key)); }
public CompletableFuture<RankLadder> update(String identifier, Function<RankLadder, RankLadder> updateFunc) { return cache.get(identifier) .thenCompose(oldLadder -> { RankLadder newLadder = updateFunc.apply(oldLadder); if (oldLadder == newLadder) { return CompletableFuture.completedFuture(newLadder); } return set(identifier, newLadder); }); }
public CompletableFuture<ImmutableSubjectData> getData(String identifier, Caching<ImmutableSubjectData> listener) { Objects.requireNonNull(identifier, "identifier"); CompletableFuture<ImmutableSubjectData> ret = cache.get().get(identifier); ret.thenRun(() -> { if (listener != null) { listeners.addListener(identifier, listener); } }); return ret; }