Skip to content

Commit

Permalink
Cache programmatic API - support asynchronous value loader
Browse files Browse the repository at this point in the history
- resolves #30311
  • Loading branch information
mkouba committed Jan 11, 2023
1 parent 54901e3 commit 0159361
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.quarkus.cache.CaffeineCache;
import io.quarkus.cache.runtime.caffeine.CaffeineCacheImpl;
import io.quarkus.test.QuarkusUnitTest;
import io.smallrye.mutiny.Uni;

public class ProgrammaticApiTest {

Expand Down Expand Up @@ -212,10 +213,25 @@ public void testPutShouldPopulateCache() {
}
}

@Test
public void testAsyncLoader() throws Exception {
// Action: value retrieval from the cache.
// Expected effect: asyncvalue loader function used.
// Verified by: same object reference between STEPS 1 and 2 results.
String key = "alpha";
String result = "foo";
Uni<String> uni = Uni.createFrom().item(result);
String value = cache.getAsync(key, k -> uni).await().indefinitely();
assertSame(value, result);
assertKeySetContains(key);
assertGetIfPresent(key, value);

}

private void assertKeySetContains(Object... expectedKeys) {
Set<Object> expectedKeySet = new HashSet<>(Arrays.asList(expectedKeys));
Set<Object> actualKeySet = cache.as(CaffeineCache.class).keySet();
assertEquals(expectedKeySet, actualKeySet);
assertTrue(actualKeySet.containsAll(expectedKeySet));
}

private void assertGetIfPresent(Object key, Object value) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ public <K, V> Uni<V> get(K key, Function<K, V> valueLoader) {
throw new UnsupportedOperationException("This method is not tested here");
}

@Override
public <K, V> Uni<V> getAsync(K key, Function<K, Uni<V>> valueLoader) {
throw new UnsupportedOperationException("This method is not tested here");
}

@Override
public Uni<Void> invalidate(Object key) {
throw new UnsupportedOperationException("This method is not tested here");
Expand Down
14 changes: 14 additions & 0 deletions extensions/cache/runtime/src/main/java/io/quarkus/cache/Cache.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ public interface Cache {
*/
<K, V> Uni<V> get(K key, Function<K, V> valueLoader);

/**
* Returns a lazy asynchronous action that will emit the cache value identified by {@code key}, obtaining that value from
* {@code valueLoader} if necessary.
*
* @param <K>
* @param <V>
* @param key
* @param valueLoader
* @return a lazy asynchronous action that will emit a cache value
* @throws NullPointerException if the key is {@code null}
*/
<K, V> Uni<V> getAsync(K key, Function<K, Uni<V>> valueLoader);

/**
* Removes the cache entry identified by {@code key} from the cache. If the key does not identify any cache entry, nothing
* will happen.
Expand All @@ -61,4 +74,5 @@ public interface Cache {
* @throws IllegalStateException if this cache is not an instance of {@code type}
*/
<T extends Cache> T as(Class<T> type);

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ public CompletionStage<V> get() {
});
}

@SuppressWarnings("unchecked")
@Override
public <K, V> Uni<V> getAsync(K key, Function<K, Uni<V>> valueLoader) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED_MSG);
return (Uni<V>) Uni.createFrom()
.completionStage(cache.asMap().computeIfAbsent(key, new Function<Object, CompletableFuture<Object>>() {
@Override
public CompletableFuture<Object> apply(Object key) {
return (CompletableFuture<Object>) valueLoader.apply((K) key)
.map(i -> NullValueConverter.toCacheValue(i))
.subscribeAsCompletionStage();
}
}));
}

@Override
public <V> CompletableFuture<V> getIfPresent(Object key) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED_MSG);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public V get() {
});
}

@Override
public <K, V> Uni<V> getAsync(K key, Function<K, Uni<V>> valueLoader) {
return valueLoader.apply(key);
}

@Override
public Uni<Void> invalidate(Object key) {
return Uni.createFrom().voidItem();
Expand Down

0 comments on commit 0159361

Please sign in to comment.