@CacheSpec(implementation = Implementation.Caffeine, population = Population.EMPTY, maximumSize = Maximum.DISABLED, weigher = CacheWeigher.DEFAULT, expireAfterAccess = Expire.DISABLED, expireAfterWrite = Expire.DISABLED, refreshAfterWrite = Expire.DISABLED, keys = ReferenceType.STRONG, values = ReferenceType.STRONG) @Test(dataProvider = "caches") public void noPolicy_async(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { assertThat(cache.synchronous().policy().eviction(), is(Optional.empty())); assertThat(cache.synchronous().policy().expireAfterWrite(), is(Optional.empty())); assertThat(cache.synchronous().policy().expireAfterAccess(), is(Optional.empty())); assertThat(cache.synchronous().policy().refreshAfterWrite(), is(Optional.empty())); } }
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))); }
@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)); }
@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.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.PARTIAL, Population.FULL }, mustExpireWithAnyOf = { AFTER_WRITE, VARIABLE }, expireAfterWrite = Expire.ONE_MINUTE, expiry = { CacheExpiry.DISABLED, CacheExpiry.WRITE }, expiryTime = Expire.ONE_MINUTE) @SuppressWarnings("FutureReturnValueIgnored") public void getIfPresent(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { context.ticker().advance(30, TimeUnit.SECONDS); cache.getIfPresent(context.firstKey()); context.ticker().advance(45, TimeUnit.SECONDS); assertThat(cache.getIfPresent(context.firstKey()), is(nullValue())); assertThat(cache.getIfPresent(context.lastKey()), is(nullValue())); assertThat(cache.synchronous().estimatedSize(), is(0L)); long count = context.initialSize(); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.EXPIRED)); }
@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())); }
@Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.CREATE) public void put_replace(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { CompletableFuture<Integer> future = CompletableFuture.completedFuture(context.absentValue()); context.ticker().advance(30, TimeUnit.SECONDS); cache.put(context.firstKey(), future); cache.put(context.absentKey(), future); context.consumedNotifications().clear(); // Ignore replacement notification context.ticker().advance(45, TimeUnit.SECONDS); assertThat(cache.getIfPresent(context.firstKey()), is(nullValue())); assertThat(cache.getIfPresent(context.middleKey()), is(nullValue())); assertThat(cache.getIfPresent(context.absentKey()), is(futureOf(context.absentValue()))); assertThat(cache.synchronous().estimatedSize(), is(1L)); long count = context.initialSize(); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.EXPIRED)); verifyWriter(context, (verifier, writer) -> verifier.deletions(count, RemovalCause.EXPIRED)); }
@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, removalListener = Listener.CONSUMING) public void put(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { Integer key = context.absentKey(); context.clear(); GcFinalization.awaitFullGc(); cache.put(key, CompletableFuture.completedFuture(context.absentValue())); long count = context.initialSize(); assertThat(cache.synchronous().estimatedSize(), is(1L)); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.COLLECTED)); }
@SuppressWarnings("unchecked") public <T> T getDataIfPresent(String path) { return (T) dataCache.getIfPresent(path); }
@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.NEGATIVE, Loader.BULK_NEGATIVE}, removalListener = Listener.CONSUMING) public void getAll(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { Set<Integer> keys = ImmutableSet.of(context.firstKey(), context.lastKey(), context.absentKey()); context.clear(); GcFinalization.awaitFullGc(); assertThat(cache.getAll(keys).join(), is(keys.stream().collect(toMap(Function.identity(), key -> -key)))); long count = context.initialSize() - cache.synchronous().estimatedSize() + 1; assertThat(count, is(greaterThan((long) keys.size()))); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.COLLECTED)); verifyWriter(context, (verifier, writer) -> verifier.deletions(count, RemovalCause.COLLECTED)); }
@Override public CompletableFuture<Void> write(String raw, UserAgentIngredients ingredients) { return CompletableFuture.runAsync(() -> caffeine.put(raw, CompletableFuture.completedFuture(ingredients))); }
@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)); }
@Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiryTime = Expire.ONE_MINUTE, mustExpireWithAnyOf = { AFTER_ACCESS, AFTER_WRITE, VARIABLE }, expiry = { CacheExpiry.DISABLED, CacheExpiry.WRITE, CacheExpiry.ACCESS }, expireAfterAccess = {Expire.DISABLED, Expire.ONE_MINUTE}, expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE}) public void put_replace(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { CompletableFuture<Integer> future = CompletableFuture.completedFuture(context.absentValue()); context.ticker().advance(30, TimeUnit.SECONDS); cache.put(context.firstKey(), future); cache.put(context.absentKey(), future); context.consumedNotifications().clear(); // Ignore replacement notification context.ticker().advance(45, TimeUnit.SECONDS); assertThat(cache.getIfPresent(context.firstKey()), is(futureOf(context.absentValue()))); assertThat(cache.getIfPresent(context.absentKey()), is(futureOf(context.absentValue()))); assertThat(cache.getIfPresent(context.middleKey()), is(nullValue())); assertThat(cache.synchronous().estimatedSize(), is(2L)); long count = context.initialSize() - 1; assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.EXPIRED)); verifyWriter(context, (verifier, writer) -> verifier.deletions(count, RemovalCause.EXPIRED)); }
@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)); }
@Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, 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}) public void put_insert(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { context.ticker().advance(1, TimeUnit.MINUTES); cache.put(context.firstKey(), CompletableFuture.completedFuture(context.absentValue())); runVariableExpiration(context); long count = context.initialSize(); assertThat(cache.synchronous().estimatedSize(), is(1L)); assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.EXPIRED)); }
@Test(dataProvider = "caches") @CacheSpec(mustExpireWithAnyOf = { AFTER_ACCESS, VARIABLE }, expiry = { CacheExpiry.DISABLED, CacheExpiry.ACCESS }, expiryTime = Expire.ONE_MINUTE, expireAfterAccess = Expire.ONE_MINUTE, population = { Population.PARTIAL, Population.FULL }) @SuppressWarnings("FutureReturnValueIgnored") public void getIfPresent(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { context.ticker().advance(30, TimeUnit.SECONDS); cache.getIfPresent(context.firstKey()); context.ticker().advance(45, TimeUnit.SECONDS); assertThat(cache.getIfPresent(context.firstKey()), is(futureOf(-context.firstKey()))); assertThat(cache.getIfPresent(context.lastKey()), is(nullValue())); assertThat(cache.synchronous().estimatedSize(), is(1L)); long count = context.initialSize() - 1; assertThat(cache, hasRemovalNotifications(context, count, RemovalCause.EXPIRED)); }
@Override public CompletableFuture<Optional<V>> getIfPresent(final K key) { requireNonNull(key); final CompletableFuture<V> future = asyncLoadingCache.getIfPresent(key); return future == null ? CompletableFuture.completedFuture(Optional.empty()) : future.thenApply(Optional::ofNullable); }
@CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(loader = { Loader.BULK_NEGATIVE_EXCEEDS }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void getAll_exceeds(AsyncLoadingCache<Integer, Integer> cache, CacheContext context) { Map<Integer, Integer> result = cache.getAll(context.absentKeys()).join(); assertThat(result.keySet(), equalTo(context.absentKeys())); assertThat(cache.synchronous().estimatedSize(), is(greaterThan(context.initialSize() + context.absentKeys().size()))); assertThat(context, both(hasMissCount(result.size())).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(1)).and(hasLoadFailureCount(0))); }
@Override public void put(final K key, final V value) { requireNonNull(key); requireNonNull(value); // non-blocking. // synchronousCacheView.put has same implementation with extra null check on value. asyncLoadingCache.put(key, CompletableFuture.completedFuture(value)); }