Skip to content

Commit 9104303

Browse files
author
Peter Alfonsi
committed
remove potential double-loading
Signed-off-by: Peter Alfonsi <[email protected]>
1 parent aee2d8e commit 9104303

File tree

1 file changed

+21
-27
lines changed

1 file changed

+21
-27
lines changed

modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCache.java

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ static class TieredSpilloverCacheSegment<K, V> implements ICache<K, V> {
162162
* This map is used to handle concurrent requests for same key in computeIfAbsent() to ensure we load the value
163163
* only once.
164164
*/
165-
Map<ICacheKey<K>, CompletableFuture<Tuple<ICacheKey<K>, V>>> completableFutureMap = new ConcurrentHashMap<>();
165+
Map<ICacheKey<K>, CompletableFuture<Tuple<Tuple<ICacheKey<K>, V>, Boolean>>> completableFutureMap = new ConcurrentHashMap<>();
166166

167167
TieredSpilloverCacheSegment(
168168
Builder<K, V> builder,
@@ -291,7 +291,7 @@ public V computeIfAbsent(ICacheKey<K> key, LoadAwareCacheLoader<ICacheKey<K>, V>
291291
// getValueFromTieredCache(),
292292
// we will see all misses. Instead, handle stats in computeIfAbsent().
293293
Tuple<V, String> cacheValueTuple;
294-
CompletableFuture<Tuple<ICacheKey<K>, V>> future = null;
294+
CompletableFuture<Tuple<Tuple<ICacheKey<K>, V>, Boolean>> future = null;
295295
try (ReleasableLock ignore = readLock.acquire()) {
296296
cacheValueTuple = getValueFromTieredCache(false).apply(key);
297297
if (cacheValueTuple == null) {
@@ -343,14 +343,17 @@ public V computeIfAbsent(ICacheKey<K> key, LoadAwareCacheLoader<ICacheKey<K>, V>
343343
private Tuple<V, Tuple<Boolean, Boolean>> compute(
344344
ICacheKey<K> key,
345345
LoadAwareCacheLoader<ICacheKey<K>, V> loader,
346-
CompletableFuture<Tuple<ICacheKey<K>, V>> future
346+
CompletableFuture<Tuple<Tuple<ICacheKey<K>, V>, Boolean>> future
347347
) throws Exception {
348-
// Handler to handle results post-processing. Takes a tuple<key, value> or exception as an input and returns
349-
// the value. Also before returning value, puts the value in cache.
348+
// Handler to handle results post-processing. Takes a Tuple<Tuple<key, value>, boolean>, where the boolean represents whether
349+
// this key/value pair was rejected by the policies,
350+
// or exception as an input and returns the value. Also before returning value, puts the value in cache if accepted by policies.
350351
boolean wasCacheMiss = false;
351352
boolean wasRejectedByPolicy = false;
352-
BiFunction<Tuple<ICacheKey<K>, V>, Throwable, Void> handler = (pair, ex) -> {
353-
if (pair != null) {
353+
BiFunction<Tuple<Tuple<ICacheKey<K>, V>, Boolean>, Throwable, Void> handler = (pairInfo, ex) -> {
354+
Tuple<ICacheKey<K>, V> pair = pairInfo.v1();
355+
boolean rejectedByPolicy = pairInfo.v2();
356+
if (pair != null && !rejectedByPolicy) {
354357
boolean didAddToCache = false;
355358
try (ReleasableLock ignore = writeLock.acquire()) {
356359
onHeapCache.put(pair.v1(), pair.v2());
@@ -387,33 +390,24 @@ private Tuple<V, Tuple<Boolean, Boolean>> compute(
387390
future.completeExceptionally(npe);
388391
throw new ExecutionException(npe);
389392
} else {
390-
if (evaluatePoliciesList(value, policies)) {
391-
future.complete(new Tuple<>(key, value));
392-
wasCacheMiss = true;
393-
} else {
394-
future.complete(null); // Passing null would skip the logic to put this into onHeap cache.
395-
// Signal to the caller that the key didn't enter the cache by sending a removal notification.
396-
// This case does not count as a cache miss.
397-
removalListener.onRemoval(new RemovalNotification<>(key, value, RemovalReason.EXPLICIT));
398-
wasRejectedByPolicy = true;
399-
}
393+
wasRejectedByPolicy = !evaluatePoliciesList(value, policies);
394+
future.complete(new Tuple<>(new Tuple<>(key, value), wasRejectedByPolicy));
395+
wasCacheMiss = !wasRejectedByPolicy;
400396
}
401397
} else {
402398
try {
403-
Tuple<ICacheKey<K>, V> futureTuple = future.get();
404-
if (futureTuple == null) {
405-
// This case can happen if we earlier completed the future with null to skip putting the value into the cache.
406-
// It does not count as a cache miss.
407-
value = loader.load(key);
408-
removalListener.onRemoval(new RemovalNotification<>(key, value, RemovalReason.EXPLICIT));
409-
wasRejectedByPolicy = true;
410-
} else {
411-
value = futureTuple.v2();
412-
}
399+
Tuple<Tuple<ICacheKey<K>, V>, Boolean> futureTuple = future.get();
400+
wasRejectedByPolicy = futureTuple.v2();
401+
value = futureTuple.v1().v2();
413402
} catch (InterruptedException ex) {
414403
throw new IllegalStateException(ex);
415404
}
416405
}
406+
if (wasRejectedByPolicy) {
407+
// Signal to the caller that the key didn't enter the cache by sending a removal notification.
408+
// This case does not count as a cache miss.
409+
removalListener.onRemoval(new RemovalNotification<>(key, value, RemovalReason.EXPLICIT));
410+
}
417411
return new Tuple<>(value, new Tuple<>(wasCacheMiss, wasRejectedByPolicy));
418412
}
419413

0 commit comments

Comments
 (0)