How to update cache every 30 minutes in spring? - java

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.

Related

How to set expiration for #Cacheable in Spring Boot?

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.

Building Couchbase caching application using Java

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.

Automatic cache invalidation in Spring

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).

Why I have cache misses in Service using Spring Cache

I have configured my cache as follows:
#Configuration
#EnableCaching
public class CacheConfig {
#Bean(name = "caffeineCachingProvider")
public CachingProvider caffeineCachingProvider() {
return Caching.getCachingProvider("com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider");
}
#Bean(name = "caffeineCacheManager")
public JCacheCacheManager getSpringCacheManager() {
CacheManager cacheManager = caffeineCachingProvider().getCacheManager();
CaffeineConfiguration<String, List<Product>> caffeineConfiguration = new CaffeineConfiguration<>();
caffeineConfiguration.setExpiryPolicyFactory(FactoryBuilder.factoryOf(new AccessedExpiryPolicy(new Duration(TimeUnit.MINUTES, 60))));
caffeineConfiguration.setCopierFactory(Copier::identity);
cacheManager.createCache("informerCache", caffeineConfiguration);
return new JCacheCacheManager(cacheManager);
}
}
Also I have the #Service that uses it in following way:
#Service
public class InformerService {
#CacheResult(cacheName = "informerCache")
public List<Product> getProducts(#CacheKey String category, #CacheKey String countrySign, #CacheKey long townId) throws Exception {
Thread.sleep(5000);
// do some work
}
}
So I have the next behavior.
When I'm calling the service method first time it takes 5 seconds
then doing some work as expected.
Calling method the second time with same parameters - > caching works -> returns result immediately
Calling the third time with same parameters again results in Thread.sleep
And all over again.
How to solve this ? Is that the issue about proxying ? What did I miss ?
As discussed in the comments, this was a bug in the JCache adapter. Thank you for letting me know about this problem. I released version 2.1.0 which includes this fix. That release also includes friendlier initial settings for CaffeineConfiguration which you identified in another post.
While the core library is heavily tested, the JCache adapters relied too heavily on the JSR's TCK (test compatibility kit). Unfortunately that test suite isn't very effective, so I added tests to help avoid these types of mistakes in the future.
This issue only occurred in JCache because its version of expiration is not supported by Caffeine's core library. Caffeine prefers to use an O(1) design that eagerly cleans up expired entries by using fixed durations. JCache uses per-entry lazy expiration and the specification authors assume a capacity constraint is used to eventually discard expired entries. I added a warning to the documentation about this feature, as it could be error prone. While none of the other JCache implementations go beyond this, a pending task is to decide on a mechanism to help mitigate this JCache design flaw.
Thanks again for reporting this issue. As always, feel free to reach out if you have any other issues or feedback to share.

Is it possible to make Spring #Import or #Configuration parametrized?

I've created a lot of common small bean-definition containers (#Configuration) which I use to rapidly develop applications with Spring Boot like:
#Import({
FreemarkerViewResolver.class, // registers freemarker that auto appends <#escape etc.
ConfigurationFromPropertiesFile.class, // loads conf/configuration.properties
UtfContentTypeResponse.class, // sets proper Content-language and Content-type
LocaleResolverWithLanguageSwitchController // Locale resolver + switch controller
);
class MySpringBootApp ...
For example, one of such #Configurations can set up session storage for locale cookie with web controller to switch to selected language etc.
They are very fun to work with and reuse, but it would be really great to make it parametrized, which could allow lot more reusege. I mean something like:
Pseudo code:
#Imports( imports = {
#FreemarkerViewResolver( escapeHtml = true, autoIncludeSpringMacros = true),
#ConfigurationFromProperties( path = "conf/configuration.properties" ),
#ContentTypeResponse( encoding = "UTF-8" ),
#LocaleResolver( switchLocaleUrl = "/locale/{loc}", defaultLocale = "en"
})
So, I basically mean "configurable #Configurations". What would be the best way to make the configuration that way?
Maybe something more like this (again, pseudo code):
#Configuration
public class MyAppConfiguration {
#Configuration
public FreemarkerConfiguration freemarkerConfiguration() {
return FreemarkerConfigurationBuilder.withEscpeAutoAppend();
}
#Configuration
public ConfigurationFromPropertiesFile conf() {
return ConfigurationFromPropertiesFile.fromPath("...");
}
#Configuration
public LocaleResolverConfigurator loc() {
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale("en").withSwitchUrl("/switchlocale/{loc}");
}
Let me quote Spring Boot Reference Guide - Externalized Configuration:
"Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments."
In my opinion the customization is not done at import time via annotation parameters like in your 2nd pseudo code block, instead the customization happens at run time e.g. in the configuration classes. Let me adapt your 3rd code block (only one function):
#Configuration
public class MyAppConfiguration {
#Autowired
private Environment env;
// Provide a default implementation for FreeMarkerConfigurer only
// if the user of our config doesn't define her own configurer.
#Bean
#ConditionalOnMissingBean(FreeMarkerConfigurer.class)
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setTemplateLoaderPath("/WEB-INF/views/");
return result;
}
...
#Bean
public LocaleResolverConfigurator loc() {
String defaultLocale = env.getProperty("my.app.config.defaultlocale", "en");
String switchLocale = env.getProperty("my.app.config.switchlocale", "/switchlocale/{loc}");
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale(defaultLocale).withSwitchUrl(switchLocale);
}
For LocaleResolverConfigurator the configuration is read from the environment, meaningful default values are defined. It is easy to change the default value(s) by providing a different value for a config parameter in any of the supported ways (documented in the first link) - via command line or a yaml file. The advantage over annotation parameters is that you can change the behavior at run time instead of compile time.
You could also inject the config parameters (if you prefer to have them as instance variable) or use a lot of other conditions, e.g. #ConditionalOnMissingBean, #ConditionalOnClass, #ConditionalOnExpression and so on. For example with #ConditionalOnClass you could check if a particular class is on your class path and provide a setting for the library identified by this class. With #ConditionalOnMissingClass you could provide an alternative implementation. In the example above I used ConditionalOnMissingBean to provide a default implementation for the FreeMarkerConfigurer. This implementation is only used when no FreeMarkerConfigurer bean is available thus can be overridden easily.
Take a look at the starters provided by Spring Boot or the community. A good read is also this blog entry. I learned a lot from spring-boot-starter-batch-web, they had an article series in a German Java magazine, but parts are also online, see Boot your own infrastructure – Extending Spring Boot in five steps (MUST READ) and especially the paragraph "Make your starter configurable by using properties".
Though I like the idea of having imports be parameterized, I think that as it stands now using #Import and #Configuration not a good fit.
I can think of two ways to use dynamic configurations, that don't rely on PropertySource style configuration.
Create a custom #ImportConfig annotation and annotation processor that accepts configuration properties that are hard-coded into the generated source files.
Use a BeanFactoryPostProcessor or BeanPostProcessor to add or manipulate your included beans respectively.
Neither is particularly simple IMO, but since it looks like you have a particular way of working. So it could be worth the time invested.

Categories

Resources