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:
Simon Baslé
2024-08-12 15:02:05 +02:00
parent 5e72ee36e2
commit 2eda5d7a2a
3 changed files with 126 additions and 5 deletions

View File

@@ -17,11 +17,15 @@
package org.springframework.cache.interceptor;
import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
@@ -39,6 +43,8 @@ import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willReturn;
import static org.mockito.BDDMockito.willThrow;
@@ -83,11 +89,56 @@ class CacheErrorHandlerTests {
willThrow(exception).given(this.cache).get(0L);
Object result = this.simpleService.get(0L);
verify(this.errorHandler).handleCacheGetError(exception, cache, 0L);
verify(this.errorHandler).handleCacheGetError(exception, this.cache, 0L);
verify(this.cache).get(0L);
verify(this.cache).put(0L, result); // result of the invocation
}
@Test
public void getSyncFail() {
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
willThrow(exception).given(this.cache).get(eq(0L), any(Callable.class));
Object result = this.simpleService.getSync(0L);
assertThat(result).isEqualTo(0L);
verify(this.errorHandler).handleCacheGetError(exception, this.cache, 0L);
verify(this.cache).get(eq(0L), any(Callable.class));
}
@Test
public void getCompletableFutureFail() {
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
willThrow(exception).given(this.cache).retrieve(eq(0L));
Object result = this.simpleService.getFuture(0L).join();
assertThat(result).isEqualTo(0L);
verify(this.errorHandler).handleCacheGetError(exception, this.cache, 0L);
verify(this.cache).retrieve(eq(0L));
}
@Test
public void getMonoFail() {
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
willThrow(exception).given(this.cache).retrieve(eq(0L));
Object result = this.simpleService.getMono(0L).block();
assertThat(result).isEqualTo(0L);
verify(this.errorHandler).handleCacheGetError(exception, this.cache, 0L);
verify(this.cache).retrieve(eq(0L));
}
@Test
public void getFluxFail() {
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
willThrow(exception).given(this.cache).retrieve(eq(0L));
Object result = this.simpleService.getFlux(0L).blockLast();
assertThat(result).isEqualTo(0L);
verify(this.errorHandler).handleCacheGetError(exception, this.cache, 0L);
verify(this.cache).retrieve(eq(0L));
}
@Test
void getAndPutFail() {
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
@@ -220,6 +271,26 @@ class CacheErrorHandlerTests {
return this.counter.getAndIncrement();
}
@Cacheable(sync = true)
public Object getSync(long id) {
return this.counter.getAndIncrement();
}
@Cacheable
public CompletableFuture<Long> getFuture(long id) {
return CompletableFuture.completedFuture(this.counter.getAndIncrement());
}
@Cacheable
public Mono<Long> getMono(long id) {
return Mono.just(this.counter.getAndIncrement());
}
@Cacheable
public Flux<Long> getFlux(long id) {
return Flux.just(this.counter.getAndIncrement(), 0L);
}
@CachePut
public Object put(long id) {
return this.counter.getAndIncrement();