@Test public void testParallelGets() { AsyncLoadingCache<String, Boolean> cachedResult = new AsyncLoadingCache<>(100); final AtomicInteger counter = new AtomicInteger(); CompletableFuture<Void> signal = new CompletableFuture<>(); final Supplier<CompletableFuture<Boolean>> supplier = () -> { counter.incrementAndGet(); return signal.thenApply(ignored -> true); }; List<String> keys = ImmutableList.of("key-1", "key-2", "key-3"); List<CompletableFuture<Boolean>> parallelOperations = new ArrayList<>(); for (int i = 0; i < 20; i++) { for (String key : keys) { parallelOperations.add(cachedResult.orElseGet(key, supplier)); } } signal.complete(null); List<Boolean> values = AsyncUtil.getAll(parallelOperations).join(); for (Boolean value : values) { assertTrue(value); } List<CompletableFuture<Boolean>> afterCompleteOperations = new ArrayList<>(); for (int i = 0; i < 3; i++) { for (String key : keys) { afterCompleteOperations.add(cachedResult.orElseGet(key, supplier)); } } values = AsyncUtil.getAll(afterCompleteOperations).join(); for (Boolean value : values) { assertTrue(value); } assertThat("supplier is called once per incomplete access", counter.get(), is(parallelOperations.size())); }
@SuppressWarnings("squid:S2789") @Nonnull public CompletableFuture<V> orElseGet(@Nonnull K key, @Nonnull Supplier<CompletableFuture<V>> supplier) { try { Optional<V> cachedValue = cache.getIfPresent(key); if (cachedValue == null) { return MoreAsyncUtil.getWithDeadline(deadlineTimeMillis, supplier).thenApply(value -> { final Optional<V> existingValue = cache.asMap().putIfAbsent(key, Optional.ofNullable(value)); return existingValue == null ? value : existingValue.orElse(null); }); } else { return CompletableFuture.completedFuture(cachedValue.orElse(null)); } } catch (Exception e) { throw new RecordCoreException("failed getting value", e).addLogInfo("cacheKey", key); } }
AsyncLoadingCache { @SuppressWarnings("squid:S2789") @Nonnull public CompletableFuture<V> orElseGet(@Nonnull K key, @Nonnull Supplier<CompletableFuture<V>> supplier) { try { Optional<V> cachedValue = cache.getIfPresent(key); if (cachedValue == null) { return MoreAsyncUtil.getWithDeadline(deadlineTimeMillis, supplier).thenApply(value -> { final Optional<V> existingValue = cache.asMap().putIfAbsent(key, Optional.ofNullable(value)); return existingValue == null ? value : existingValue.orElse(null); }); } else { return CompletableFuture.completedFuture(cachedValue.orElse(null)); } } catch (Exception e) { throw new RecordCoreException("failed getting value", e).addLogInfo("cacheKey", key); } } }
AsyncLoadingCache { @SuppressWarnings("squid:S2789") @Nonnull public CompletableFuture<V> orElseGet(@Nonnull K key, @Nonnull Supplier<CompletableFuture<V>> supplier) { try { Optional<V> cachedValue = cache.getIfPresent(key); if (cachedValue == null) { return MoreAsyncUtil.getWithDeadline(deadlineTimeMillis, supplier).thenApply(value -> { final Optional<V> existingValue = cache.asMap().putIfAbsent(key, Optional.ofNullable(value)); return existingValue == null ? value : existingValue.orElse(null); }); } else { return CompletableFuture.completedFuture(cachedValue.orElse(null)); } } catch (Exception e) { throw new RecordCoreException("failed getting value", e).addLogInfo("cacheKey", key); } } AsyncLoadingCache(long refreshTimeMillis);  AsyncLoadingCache(long refreshTimeMillis, long deadlineTimeMillis);  AsyncLoadingCache(long refreshTimeMillis, long deadlineTimeMillis, long maxSize); }
AsyncLoadingCache { @SuppressWarnings("squid:S2789") @Nonnull public CompletableFuture<V> orElseGet(@Nonnull K key, @Nonnull Supplier<CompletableFuture<V>> supplier) { try { Optional<V> cachedValue = cache.getIfPresent(key); if (cachedValue == null) { return MoreAsyncUtil.getWithDeadline(deadlineTimeMillis, supplier).thenApply(value -> { final Optional<V> existingValue = cache.asMap().putIfAbsent(key, Optional.ofNullable(value)); return existingValue == null ? value : existingValue.orElse(null); }); } else { return CompletableFuture.completedFuture(cachedValue.orElse(null)); } } catch (Exception e) { throw new RecordCoreException("failed getting value", e).addLogInfo("cacheKey", key); } } AsyncLoadingCache(long refreshTimeMillis);  AsyncLoadingCache(long refreshTimeMillis, long deadlineTimeMillis);  AsyncLoadingCache(long refreshTimeMillis, long deadlineTimeMillis, long maxSize); @SuppressWarnings("squid:S2789") // comparison of null and optional used to differentiate absence of key and presence of null @Nonnull CompletableFuture<V> orElseGet(@Nonnull K key, @Nonnull Supplier<CompletableFuture<V>> supplier); long getRefreshTimeSeconds(); long getMaxSize(); void clear(); @Override String toString(); }
AsyncLoadingCache { @SuppressWarnings("squid:S2789") @Nonnull public CompletableFuture<V> orElseGet(@Nonnull K key, @Nonnull Supplier<CompletableFuture<V>> supplier) { try { Optional<V> cachedValue = cache.getIfPresent(key); if (cachedValue == null) { return MoreAsyncUtil.getWithDeadline(deadlineTimeMillis, supplier).thenApply(value -> { final Optional<V> existingValue = cache.asMap().putIfAbsent(key, Optional.ofNullable(value)); return existingValue == null ? value : existingValue.orElse(null); }); } else { return CompletableFuture.completedFuture(cachedValue.orElse(null)); } } catch (Exception e) { throw new RecordCoreException("failed getting value", e).addLogInfo("cacheKey", key); } } AsyncLoadingCache(long refreshTimeMillis);  AsyncLoadingCache(long refreshTimeMillis, long deadlineTimeMillis);  AsyncLoadingCache(long refreshTimeMillis, long deadlineTimeMillis, long maxSize); @SuppressWarnings("squid:S2789") // comparison of null and optional used to differentiate absence of key and presence of null @Nonnull CompletableFuture<V> orElseGet(@Nonnull K key, @Nonnull Supplier<CompletableFuture<V>> supplier); long getRefreshTimeSeconds(); long getMaxSize(); void clear(); @Override String toString(); static final long DEFAULT_DEADLINE_TIME_MILLIS; static final long UNLIMITED; }