I have the following cache implementation in a Spring Boot app and it is working without any problem. However, I want to define expiration for this approach. Is it possible to set expiration for #Cacheable?
I look at Expiry time #Cacheable spring boot and there is not seem to be a direct way for #Cacheable. Is it possible via a smart approach?
#Configuration
#EnableCaching
public class CachingConfig {
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
#Component
public class SimpleCacheCustomizer
implements CacheManagerCustomizer<ConcurrentMapCacheManager> {
#Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(asList("users"));
}
}
#Cacheable("users")
public List<User> getUsers(UUID id) {...}
As said in the Spring documentation, there is no TTL for the default cache system of Spring.
8.7. How can I Set the TTL/TTI/Eviction policy/XXX feature?
Directly through your cache provider. The cache abstraction is an
abstraction, not a cache implementation. The solution you use might
support various data policies and different topologies that other
solutions do not support (for example, the JDK
ConcurrentHashMap — exposing that in the cache abstraction would be
useless because there would no backing support). Such functionality
should be controlled directly through the backing cache (when
configuring it) or through its native API
You'll have to use an other cache provider like Redis or Gemfire if you want a TTL configuration.
An example of how to use TTL with Redis is available here.
Related
I have following declaration:
#Cacheable("books")
public Book findBook(ISBN isbn) {...}
But I want to update the cache every 30 minutes. I understand that I can create #Scheduled job to invoke method annotated #CacheEvict("books")
Also, I suppose that in this case all books will be cleared but it is more desirable to update only stale data(which were put in cache > 30 minutes ago)
Is there anything in spring that can facilitate implementation?
Cache implementations provide a feature named expire after write or time to life for this task. The different cache implementations have a lot variances. In Spring no effort was made to try to abstract or generalize the configuration part as well. Here is an example of programmatic configuration for your cache in Spring, if you like to use cache2k:
#Configuration
#EnableCaching
public class CachingConfig extends CachingConfigurerSupport {
#Bean
public CacheManager cacheManager() {
return new SpringCache2kCacheManager()
.addCaches(
b->b.name("books").keyType(ISBN.class).valueType(Book.class)
.expireAfterWrite(30, TimeUnit.MINUTES)
.entryCapacity(5000);
}
}
More information about this is in cache2k User Guide - Spring Framework Support. Other cache implementations like, EHCache or Caffeine support expiry as well, but the configuration is different.
If you like to configure the cache expiry in a "vendor neutral" way, you can use a cache implementation that support the JCache/JSR107 standard. The standard includes setting an expiry. A way to do it, looks like this:
#Configuration
#EnableCaching
public class CacheConfiguration {
#Bean
public JCacheCacheManager cacheManager() {
return new JCacheCacheManager() {
#Override
protected Collection<Cache> loadCaches() {
Collection<Cache> caches = new ArrayList<>();
caches.add(new JCacheCache(
getCacheManager().createCache("books",
new MutableConfiguration<ISBN,Book>()
.setExpiryPolicyFactory(ModifiedExpiryPolicy.factoryOf(new Duration(TimeUnit.MINUTES, 30)))),
false));
return caches;
}
};
}
}
The in JCache is, that there are configuration options, that you need, which are not part of the standard. One example is limiting the cache size. For this, you always need to add a vendor specific configuration. In case of cache2k (I am the author of cache2k), which supports JCache, the configurations are merged, which is described in detail at cache2k User Guide - JCache. This means on a programmatic level you do the "logic" part of the configuration, where as, the "operational" part like the cache size is configurable in an external configuration file.
Unfortunately, its not part of the standard how a vendor configuration and a programmatic configuration via the JCache API needs to interoperate. So, even a 100% JCache compatible cache might refuse operation, and require that you only use one way of configuration.
I have a spring boot application with the following properties:
spring.cache.type: redis
spring.redis.host: <hostname>
spring.redis.port: <hostport>
Right now if the remote host fails the application also fails with a connection error.
As in this case my cache is not core to my application, but it is used only for performance, I'd like for spring to simply bypass it and go to the database retrieving its data.
I saw that this could be attained by defining a custom errorHandler method, but in order to do so I have to implement the CachingConfigurer bean...but this also forces me to override every method (for example cache manager, cache resolver, ecc.).
#Configuration
public class CacheConfiguration implements CachingConfigurer{
#Override
public CacheManager cacheManager() {
// TODO Auto-generated method stub
return null;
}
#Override
public CacheResolver cacheResolver() {
// TODO Auto-generated method stub
return null;
}
...
#Override
public CacheErrorHandler errorHandler() {
// the only method I need, maybe
return null;
}
I would like to avoid that...I simply need a way to tell spring "the cache crashed but it's ok: just pretend you have no cache at all"
#Phate - Absolutely! I just answered a related question (possibly) using Apache Geode or Pivotal GemFire as the caching provider in a Spring Boot application with Spring's Cache Abstraction.
In that posting, rather than disabling the cache completely, I switched GemFire/Geode to run in a local-only mode (a possible configuration with GemFire/Geode). However, the same techniques can be applied to disable caching entirely if that is what is desired.
In essence, you need a pre-processing step, before Spring Boot and Spring in general start to evaluate the configuration of your application.
In my example, I implemented a custom Spring Condition that checked the availability of the cluster (i.e. servers). I then applied the Condition to my #Configuration class.
In the case of Spring Boot, Spring Boot applies auto-configuration for Redis (as a store and a caching provider) when it effectively sees (as well as see here) Redis and Spring Data Redis on the classpath of your application. So, essentially, Redis is only enabled as a caching provider when the "conditions" are true, primarily that a
RedisConnectionFactory bean was declared by your application configuration, your responsibility.
So, what would this look like?
Like my Apache Geode & Pivotal GemFire custom Spring Condition, you could implement a similar Condition for Redis, such as:
static RedisAvailableCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext,
AnnotatedTypeMetadata annotatedTypeMetadata) {
// Check the available of the Redis server, such as by opening a Socket
// connection to the server node.
// NOTE: There might be other, more reliable/robust means of checking
// the availability of a Redis server in the Redis community.
Socket redisServer;
try {
Environment environment = conditionContext.getEnvironment();
String host = environment.getProperty("spring.redis.host");
Integer port = environment.getProperty("spring.redis.port", Integer.class);
SocketAddress redisServerAddress = new InetSocketAddress(host, port);
redisServer = new Socket();
redisServer.connect(redisServerAddress);
return true;
}
catch (Throwable ignore) {
System.setProperty("spring.cache.type", "none");
return false;
}
finally {
// TODO: You need to implement this method yourself.
safeCloseSocket(redisServer);
}
}
}
Additionally, I also set the spring.cache.type to NONE, to ensure that caching is rendered as a no-op in the case that Redis is not available. NONE is explained in more detail here.
Of course, you could also use a fallback caching option, using some other caching provider (like a simple ConcurrentHashMap, but I leave that as an exercise for you). Onward...
Then, in your Spring Boot application configuration class where you have defined your RedisConnectionFactory bean (as expected by Spring Boot's auto-configuration), you add this custom Condition using Spring's #Conditiional annotation, like so:
#Confgiuration
#Conditional(RedisAvailableCondition.class);
class MyRedisConfiguration {
#Bean
RedisConnectionFactory redisConnectionFactory() {
// Construct and return new RedisConnectionFactory
}
}
This should effectively handle the case when Redis is not available.
DISCLAIMER: I did not test this myself, but is based on my Apache Geode/Pivotal GemFire example that does work. So, perhaps, with some tweaks this will address your needs. It should also serve to give you some ideas.
Hope this helps!
Cheers!
We can use any of Circuit breaker implementation to use database as fallback option in case of any cache failure. The advantage of going with circuit breaker pattern is that once your cache is up, the request will be automatically routed back to your cache, so the switch happens seamlessly.
Also you configure how many times you want to retry before falling back to database and how frequently you want to check if your cache is back up online.
Spring cloud provides out of the box support for hystrix and Resilience4j circuit breaker implementation and its easy to integrate with spring boot applications.
https://spring.io/projects/spring-cloud-circuitbreaker
https://resilience4j.readme.io/docs/circuitbreaker
I have to implement the CachingConfigurer bean...but this also forces
me to override every method (for example cache manager, cache
resolver, ecc.)
Instead of this, you can simply extend CachingConfigurerSupport and only override the errorHandler() method, returning a custom CacheErrorHandler whose method implementations are no-ops. See https://stackoverflow.com/a/68072419/1527469
Can anybody here direct me in the right direction to develop a caching application? Any links to the example are appreciated.
Its pretty straight forward with Spring-boot.
Provide the couchbase-cluster configuration.
Define a bucket where every cache related data will be read/written into.
Spring expects a CacheManager bean. So define it something like this:
#Bean
public CacheManager cacheManager() {
CacheBuilder cacheBuilder =
CacheBuilder.newInstance(bucket()).withExpiration(TTL);
return new CouchbaseCacheManager(cacheBuilder, CACHE_NAME);
}
Add annotation #Configuration and #EnableCaching
Now for the usage you can use annotations #Cacheable, #CacheEvict, #CachePut etc.
Simple usage:
#Cacheable(CACHE_NAME)
public String getCompanyName(String companyId){}
Hope it helps.
I'm using Google Guava Cache + Spring cache abstraction for caching purpose.
I'm trying to make use of Guava's Loading Cache interface for the same.
I know Spring provides support for Guava Cache, but I was wondering whether I can make use of spring's cacheable annotation alongwith Loading Cache?
Basically I wanted to keep the business layer separate from the Cache.
Kindly help. Thanks.
Guava Cache is deprecated. If you'd existing code, that'd be another matter, but for new code, use Caffeine.
Put a #Cacheable("myCacheName") on the method that you want to cache the return value for.
Put a #EnableCaching on your application class if using Spring Boot, otherwise on some #Configuration class.
Set the spec in application.properties if using Spring Boot, like so: spring.cache.caffeine.spec=maximumSize=10000,expireAfterWrite=5m. If not using Boot, use #PropertySources annotation on the same class as in #3 above.
Add org.springframework.boot:spring-boot-starter-cache and com.github.ben-manes.caffeine:caffeine to your build file. If not using Boot, you'll need to set up the dependencies differently.
You're done.
So you want both butter and jam. Okay. I will help you use loading cache along with keeping caching logic separate.
Consider you have a service class SampleServiceImpl which implements SampleService interface.
Service interface:
public interface SampleService {
User getUser(int id);
}
Service Implementation:
#Service
public class SampleServiceImpl implements SampleService {
public User getUser(int id) {
// fetch user from database
return user;
}
}
Create one more class SampleServiceCache
public class SampleServiceCache extends ServiceCacheImpl {
#Autowired
public SampleServiceCache(int expiryTime, int maximumSize) {
loadingCache =
CacheBuilder.newBuilder().maximumSize(maximumSize).expireAfterAccess(expiryTime, TimeUnit.HOURS).build(
new CacheLoader<Integer, User>() {
#Override
public User load(#Nonnull Integer userId) {
return SampleServiceCache.super.getUser(userId);
}
});
}
#Override
public User getUser(int userId) {
return loadingCache.getUnchecked(userId);
}
}
In you bean config:
#Bean
public SampleService sampleService() {
return new SampleServiceCache(expiry, maxSize);
}
The day you want to remove cache, you have to do two things:
1. Remove the cache class.
2. Change bean config to return actual implementation object rather than cache implementation object.
P.S. You can define multiple loading caches for different behaviors say user retrieval, article retrieval, etc.
I have a class that performs some read operations from a service XXX. These read operations will eventually perform DB reads and I want to optimize on those calls by caching the results of each method in the class for a specified custom key per method.
Class a {
public Output1 func1(Arguments1 ...) {
...
}
public Output2 func2(Arguments2 ...) {
...
}
public Output3 func3(Arguments3 ...) {
...
}
public Output4 func4(Arguments4 ...) {
...
}
}
I am thinking of using Spring caching(#Cacheable annotation) for caching results of each of these methods.
However, I want cache invalidation to happen automatically by some mechanism(ttl etc). Is that possible in Spring caching ? I understand that we have a #CacheEvict annotation but I want that eviction to happen automatically.
Any help would be appreciated.
According to the Spring documentation (section 36.8) :
How can I set the TTL/TTI/Eviction policy/XXX feature?
Directly through your cache provider. The cache abstraction is...
well, an abstraction not a cache implementation. The solution you are
using might support various data policies and different topologies
which other solutions do not (take for example the JDK
ConcurrentHashMap) - exposing that in the cache abstraction would be
useless simply because there would no backing support. Such
functionality should be controlled directly through the backing cache,
when configuring it or through its native API.#
This mean that Spring does not directly expose API to set Time To Live , but instead relays on the caching provider implementation to set this. This mean that you need to either set Time to live through the exposed Cache Manager, if the caching provider allows dynamic setup of these attributes. Or alternatively you should configure yourself the cache region that the Spring is using with the #Cacheable annotation.
In order to find the name of the cache region that the #Cacheable is exposing. You can use a JMX console to browse the available cache regions in your application.
If you are using EHCache for example once you know the cache region you can provide xml configuration like this:
<cache name="myCache"
maxEntriesLocalDisk="10000" eternal="false" timeToIdleSeconds="3600"
timeToLiveSeconds="0" memoryStoreEvictionPolicy="LFU">
</cache>
Again I repeat all configuration is Caching provider specific and Spring does not expose an interface when dealing with it.
REMARK: The default cache provider that is configured by Spring if no cache provider defined is ConcurrentHashMap. It does not have support for Time To Live. In order to get this functionality you have to switch to a different cache provider(for example EHCache).