Handle low-level errors for sync/flux/mono/future gets
This change adds 3 protected methods to `AbstractCacheInvoker` that wrap additional `Cache#retrieve` and `Cache#get` calls with `handleCacheGetError` in case the Cache call itself fails. For example, if the cache is remote and a connection to it cannot be established. Closes gh-21590
This commit is contained in:
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
@@ -78,6 +82,52 @@ public abstract class AbstractCacheInvoker {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected <T> T doGet(Cache cache, Object key, Callable<T> valueLoader) {
|
||||
try {
|
||||
return cache.get(key, valueLoader);
|
||||
}
|
||||
catch (Cache.ValueRetrievalException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
getErrorHandler().handleCacheGetError(ex, cache, key);
|
||||
try {
|
||||
return valueLoader.call();
|
||||
}
|
||||
catch (Exception ex2) {
|
||||
throw new RuntimeException(ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected CompletableFuture<?> doRetrieve(Cache cache, Object key) {
|
||||
try {
|
||||
return cache.retrieve(key);
|
||||
}
|
||||
catch (Cache.ValueRetrievalException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
getErrorHandler().handleCacheGetError(ex, cache, key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> CompletableFuture<T> doRetrieve(Cache cache, Object key, Supplier<CompletableFuture<T>> valueLoader) {
|
||||
try {
|
||||
return cache.retrieve(key, valueLoader);
|
||||
}
|
||||
catch (Cache.ValueRetrievalException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
getErrorHandler().handleCacheGetError(ex, cache, key);
|
||||
return valueLoader.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#put(Object, Object)} on the specified {@link Cache}
|
||||
* and invoke the error handler if an exception occurs.
|
||||
|
||||
@@ -456,7 +456,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
|
||||
Cache cache = context.getCaches().iterator().next();
|
||||
if (CompletableFuture.class.isAssignableFrom(method.getReturnType())) {
|
||||
return cache.retrieve(key, () -> (CompletableFuture<?>) invokeOperation(invoker));
|
||||
return doRetrieve(cache, key, () -> (CompletableFuture<?>) invokeOperation(invoker));
|
||||
}
|
||||
if (this.reactiveCachingHandler != null) {
|
||||
Object returnValue = this.reactiveCachingHandler.executeSynchronized(invoker, method, cache, key);
|
||||
@@ -465,7 +465,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
}
|
||||
try {
|
||||
return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
|
||||
return wrapCacheValue(method, doGet(cache, key, () -> unwrapReturnValue(invokeOperation(invoker))));
|
||||
}
|
||||
catch (Cache.ValueRetrievalException ex) {
|
||||
// Directly propagate ThrowableWrapper from the invoker,
|
||||
@@ -515,7 +515,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
|
||||
for (Cache cache : context.getCaches()) {
|
||||
if (CompletableFuture.class.isAssignableFrom(context.getMethod().getReturnType())) {
|
||||
CompletableFuture<?> result = cache.retrieve(key);
|
||||
CompletableFuture<?> result = doRetrieve(cache, key);
|
||||
if (result != null) {
|
||||
return result.exceptionally(ex -> {
|
||||
getErrorHandler().handleCacheGetError((RuntimeException) ex, cache, key);
|
||||
@@ -1144,7 +1144,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
|
||||
ReactiveAdapter adapter = this.registry.getAdapter(context.getMethod().getReturnType());
|
||||
if (adapter != null) {
|
||||
CompletableFuture<?> cachedFuture = cache.retrieve(key);
|
||||
CompletableFuture<?> cachedFuture = doRetrieve(cache, key);
|
||||
if (cachedFuture == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user