55import com .fasterxml .jackson .core .type .TypeReference ;
66import com .fasterxml .jackson .databind .JavaType ;
77import com .fasterxml .jackson .databind .type .TypeFactory ;
8+ import io .vavr .CheckedFunction2 ;
89import jakarta .inject .Provider ;
910import lombok .NonNull ;
1011import lombok .RequiredArgsConstructor ;
2526import java .util .List ;
2627import java .util .Map ;
2728import java .util .Objects ;
28- import java .util .function .BiFunction ;
2929import java .util .function .Function ;
3030import java .util .stream .Stream ;
3131
@@ -49,113 +49,113 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
4949
5050 var cacheable = method .getAnnotation (Cacheable .class );
5151 if (cacheable != null ) {
52- return runCacheAwareAction (invocation , isReactive , cacheable .name (), cacheable .key (),
52+ return runCacheAwareAction (invocation , cacheable .name (), cacheable .key (),
5353 (key , group ) -> processCacheableMethod (invocation , isReactive , key , group , cacheable ));
5454 }
5555
5656 var cachePut = method .getAnnotation (CachePut .class );
5757 if (cachePut != null ) {
58- return runCacheAwareAction (invocation , isReactive , cachePut .name (), cachePut .key (),
58+ return runCacheAwareAction (invocation , cachePut .name (), cachePut .key (),
5959 (key , group ) -> processCachePutMethod (invocation , isReactive , key , group ));
6060 }
6161
6262 var cacheEvict = method .getAnnotation (CacheEvict .class );
6363 if (cacheEvict != null ) {
64- return runCacheAwareAction (invocation , isReactive , cacheEvict .name (), cacheEvict .key (),
64+ return runCacheAwareAction (invocation , cacheEvict .name (), cacheEvict .key (),
6565 (key , group ) -> processCacheEvictMethod (invocation , isReactive , key , cacheEvict ));
6666 }
6767
6868 return invocation .proceed ();
6969 }
7070
71- private Object runCacheAwareAction (MethodInvocation invocation , boolean isReactive , String group , String keyAgs ,
72- BiFunction <String , String , Object > action ) throws Throwable {
73-
71+ private Object runCacheAwareAction (
72+ MethodInvocation invocation , String group , String keyArgs , CheckedFunction2 <String , String , Object > action )
73+ throws Throwable {
7474 String key ;
75-
7675 try {
77- key = getKeyName (group , keyAgs , invocation );
76+ key = getKeyName (group , keyArgs , invocation );
7877 } catch (Exception e ) {
7978 // If there is an error evaluating the key, proceed without caching
80- log .error ("Error evaluating key expression: {}" , keyAgs , e );
81- log .warn ("Cache will be skipped due to error evaluating key expression" );
79+ log .warn ("Cache will be skipped due to error evaluating key expression '{}'" , keyArgs , e );
8280 return invocation .proceed ();
8381 }
84-
85- if (isReactive ) {
86- return action .apply (key , group );
87- }
88-
89- return ((Mono <?>) action .apply (key , group )).block ();
82+ return action .apply (key , group );
9083 }
9184
92- private Mono < Object > processCacheEvictMethod (MethodInvocation invocation , boolean isReactive , String key ,
93- CacheEvict cacheEvict ) {
85+ private Object processCacheEvictMethod (
86+ MethodInvocation invocation , boolean isReactive , String key , CacheEvict cacheEvict ) throws Throwable {
9487 if (isReactive ) {
9588 try {
9689 return ((Mono <?>) invocation .proceed ())
9790 .flatMap (value -> cacheManager .get ().evict (key , cacheEvict .keyUsesPatternMatching ())
98- .thenReturn (value ))
91+ .thenReturn (value )
92+ .onErrorResume (exception -> {
93+ log .error ("Error evicting cache" , exception );
94+ return Mono .just (value ); // Return value even if evict fails
95+ }))
9996 .switchIfEmpty (
100- cacheManager .get ().evict (key , cacheEvict .keyUsesPatternMatching ()).then (Mono .empty ()))
97+ cacheManager .get ().evict (key , cacheEvict .keyUsesPatternMatching ())
98+ .onErrorResume (exception -> {
99+ log .error ("Error evicting cache" , exception );
100+ return Mono .empty ();
101+ })
102+ .then (Mono .empty ()))
101103 .map (Function .identity ());
102104 } catch (Throwable e ) {
103105 return Mono .error (e );
104106 }
105107 } else {
108+ var value = invocation .proceed ();
106109 try {
107- var value = invocation .proceed ();
108- if (value == null ) {
109- return cacheManager .get ().evict (key , cacheEvict .keyUsesPatternMatching ()).then (Mono .empty ());
110- }
111- return cacheManager .get ().evict (key , cacheEvict .keyUsesPatternMatching ()).thenReturn (value );
112- } catch (Throwable e ) {
113- return Mono .error (e );
110+ // Evict cache asynchronously to avoid blocking the execution thread. Makes cache eventual consistent
111+ cacheManager .get ().evictAsync (key , cacheEvict .keyUsesPatternMatching ());
112+ } catch (RuntimeException exception ) {
113+ log .error ("Error evicting async cache" , exception );
114114 }
115+ return value ;
115116 }
116117 }
117118
118- private Mono < Object > processCachePutMethod (MethodInvocation invocation , boolean isReactive , String key ,
119- String group ) {
119+ private Object processCachePutMethod (
120+ MethodInvocation invocation , boolean isReactive , String key , String group ) throws Throwable {
120121 if (isReactive ) {
121122 try {
122123 return ((Mono <?>) invocation .proceed ()).flatMap (value -> cachePut (value , key , group ));
123124 } catch (Throwable e ) {
124125 return Mono .error (e );
125126 }
126127 } else {
127- try {
128- var value = invocation .proceed ();
129- return cachePut (value , key , group ).thenReturn (value );
130- } catch (Throwable e ) {
131- return Mono .error (e );
132- }
128+ return processSyncCacheMiss (invocation , key , group );
133129 }
134130 }
135131
136- private Object processCacheableMethod (MethodInvocation invocation , boolean isReactive , String key ,
137- String group , Cacheable cacheable ) {
138-
132+ private Object processCacheableMethod (
133+ MethodInvocation invocation , boolean isReactive , String key , String group , Cacheable cacheable )
134+ throws Throwable {
139135 if (isReactive ) {
140-
141136 if (invocation .getMethod ().getReturnType ().isAssignableFrom (Mono .class )) {
142137 return handleMono (invocation , key , group , cacheable );
143138 } else {
144139 return handleFlux (invocation , key , group , cacheable );
145140 }
146141 } else {
147-
148- if (cacheable .wrapperType () != Object .class ) {
149- TypeReference typeReference = TypeReferenceUtils .forTypes (cacheable .wrapperType (),
150- cacheable .returnType ());
151-
152- return cacheManager .get ().get (key , typeReference )
153- .switchIfEmpty (processSyncCacheMiss (invocation , key , group ));
142+ Object cachedValue ;
143+ try {
144+ if (cacheable .wrapperType () != Object .class ) {
145+ var typeReference = TypeReferenceUtils .forTypes (
146+ cacheable .wrapperType (), cacheable .returnType ());
147+ cachedValue = cacheManager .get ().getSync (key , typeReference );
148+ } else {
149+ cachedValue = cacheManager .get ().getSync (key , invocation .getMethod ().getReturnType ());
150+ }
151+ } catch (RuntimeException exception ) {
152+ log .error ("Error getting value synchronously from cache" , exception );
153+ cachedValue = null ; // Treat as cache miss
154154 }
155-
156- return cacheManager . get (). get ( key , invocation . getMethod (). getReturnType ())
157- . map ( Object . class :: cast )
158- . switchIfEmpty ( processSyncCacheMiss (invocation , key , group ) );
155+ if ( cachedValue != null ) {
156+ return cachedValue ;
157+ }
158+ return processSyncCacheMiss (invocation , key , group );
159159 }
160160 }
161161
@@ -189,6 +189,10 @@ private Flux<Object> getFromCacheOrCallMethod(MethodInvocation invocation, Strin
189189 TypeReference <List <?>> collectionType ) {
190190 return cacheManager .get ()
191191 .get (key , collectionType )
192+ .onErrorResume (exception -> {
193+ log .error ("Error getting value from cache" , exception );
194+ return Mono .empty (); // Treat as cache miss
195+ })
192196 .map (Collection .class ::cast )
193197 .flatMapMany (Flux ::fromIterable )
194198 .switchIfEmpty (processFluxCacheMiss (invocation , key , group ));
@@ -200,22 +204,26 @@ private Mono<Object> handleMono(MethodInvocation invocation, String key, String
200204 cacheable .returnType ());
201205
202206 return cacheManager .get ().get (key , typeReference )
207+ .onErrorResume (exception -> {
208+ log .error ("Error getting value from cache" , exception );
209+ return Mono .empty (); // Treat as cache miss
210+ })
203211 .switchIfEmpty (processCacheMiss (invocation , key , group ));
204212 }
205213
206214 return cacheManager .get ().get (key , cacheable .returnType ())
215+ .onErrorResume (exception -> {
216+ log .error ("Error getting value from cache" , exception );
217+ return Mono .empty (); // Treat as cache miss
218+ })
207219 .map (Object .class ::cast )
208220 .switchIfEmpty (processCacheMiss (invocation , key , group ));
209221 }
210222
211- private Mono <Object > processSyncCacheMiss (MethodInvocation invocation , String key , String group ) {
212- return Mono .defer (() -> {
213- try {
214- return Mono .just (invocation .proceed ());
215- } catch (Throwable e ) {
216- return Mono .error (e );
217- }
218- }).flatMap (value -> cachePut (value , key , group ));
223+ private Object processSyncCacheMiss (MethodInvocation invocation , String key , String group ) throws Throwable {
224+ var value = invocation .proceed ();
225+ cachePutAsync (value , key , group );
226+ return value ;
219227 }
220228
221229 private Mono <Object > processCacheMiss (MethodInvocation invocation , String key , String group ) {
@@ -239,11 +247,9 @@ private Flux<Object> processFluxCacheMiss(MethodInvocation invocation, String ke
239247 .flatMap (value -> cachePut (value , key , group ));
240248
241249 return flux
242- .doOnSubscribe (subscription -> Schedulers .boundedElastic ().schedule (() -> {
243- cacheable .subscribe (
244- __ -> log .info ("Flux value put in cache" ),
245- e -> log .error ("Error putting flux value in cache" , e ));
246- }));
250+ .doOnSubscribe (subscription -> Schedulers .boundedElastic ().schedule (() -> cacheable .subscribe (
251+ __ -> log .debug ("Flux value put in cache" ),
252+ e -> log .error ("Error putting flux value in cache" , e ))));
247253 } catch (Throwable e ) {
248254 return Flux .error (e );
249255 }
@@ -261,10 +267,25 @@ private Mono<Object> cachePut(Object value, String key, String group) {
261267 });
262268 }
263269
270+ private void cachePutAsync (Object value , String key , String group ) {
271+ // Methods returning null values are not cached
272+ if (value == null ) {
273+ return ;
274+ }
275+ try {
276+ var ttlDuration = cacheConfiguration .getCaches ()
277+ .getOrDefault (group , cacheConfiguration .getDefaultDuration ());
278+ // Set cache asynchronously to avoid blocking the execution thread. Makes cache eventual consistent
279+ cacheManager .get ().putAsync (key , value , ttlDuration );
280+ } catch (RuntimeException exception ) {
281+ log .error ("Error putting async value in cache" , exception );
282+ }
283+ }
284+
264285 private String getKeyName (String name , String key , MethodInvocation invocation ) {
265286 Map <String , Object > params = new HashMap <>();
266287
267- // Use Paranamer to resolve parameter names
288+ // Use Parameter to resolve parameter names
268289 Parameter [] parameters = invocation .getMethod ().getParameters ();
269290 Object [] args = invocation .getArguments ();
270291
@@ -281,5 +302,4 @@ private String getKeyName(String name, String key, MethodInvocation invocation)
281302 }
282303 return "%s:-%s" .formatted (name , evaluatedKey );
283304 }
284-
285305}
0 commit comments