In my Vaadin 8 project, there are some objects that I keep in the main memory whenever the application is up&running.
For memory efficiency, I'm looking to keep this cache for all users-- not a separate cache for each user. So the cache should be per application, i.e., a single set of these objects, and NOT per session.
How to do this in Vaadin 8? Please note - there's no Spring in this system, so doing it on Spring is not an option.
Excuse if a naïve question. Not yet that savvy on Vaadin/web dev.
For a application wide cache, just create a class with a public static cache that you can access from everywhere. The static object will be the same for every UI and session.
Since you didn't specify what do you want to cache, I suppose you want to cache custom objects made by you to use for some sort of server side logic.
To do so in plain java, you can create a simple hashmap to use as cache. A VERY simple example would be:
public class GlobalCache {
private static ConcurrentHashMap<String, Object> cacheMap = new ConcurrentHashMap<>();
public static Object getObject(String key, Function<String, Object> creator) {
return cacheMap.computeIfAbsent(key, creator);
}
}
That wouldn't be a good cache since it won't invalidate its entries.
If you can add any libraries, you should add Guava. Guava provides a great cache implementation that you can use:
//Add this to your gradle:
dependencies {
implementation group: 'com.google.guava', name: 'guava', version: '24.1-jre'
}
//And this will become your code
public class GlobalCache {
private static Cache<String, Object> cache =
CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(5, TimeUnit.MINUTES).build();
public static Object getObject(String key, Callable<? extends Object> creator) throws ExecutionException {
return cache.get(key, creator);
}
}
Related
I have built a spring boot application for the first time. I have about 10-12 user specific caches and 1 global cache in my application. User level caches are HashMap based.
Class userCache1 {
private Map<Integer, MyObject> dataMap = new HashMap<>();
public Map<Integer, MyObject> getDataMap() {
return dataMap;
}
public void setDataMap(Map<Integer, MyData> dataMap) {
this.dataMap = dataMap;
}
public dataMap get(int key) {
if(dataMap.containsKey(key))
return dataMap.get(key);
else
return null;
}
public void setData(int dataMap, MyData data) {
dataMap.put(key, data);
}
}
Now there has to be one instance of userCache1 for each registered user. I have similarly objects each holding various kinds of data that are required by multiple objects for the application to process the user requests. Some of the classes would required 5-10 of these caches such as userCache1...userCache5.
Below are my questions:
How would I tie the userCache1 to userId which is the primary key of the user of this application? Do I need another class that holds userId and userCache1? Wouldn't it create one such class for each of these user level caches just for the purpose of associating the cache with userId? Are there better options that I can use?
Based on the approach I take in #1, I would like to use DI to inject the required userCacheX objects to the constructor of the classes that require these caches to process the biz logic. I don't think that having a constructor with 5+ such caches is an elegant approach. What makes the most sense in this example as below
public class UserCache {
private UserCache1 cache1;
private UserCache2 cache2;
...
private UserCacheN cacheN;
UserCache(UserCache1...UserCacheN) {
this.cache1 = cache1;
....
this.cacheN = cacheN;
}
/*
use the cache1...cacheN as needed in the body of this class
*/
}
In addition to user specific cache I have 1 or 2 global cache as well. I understand that spring enables caching which I would be using once I decide on the high level design.
All my user specific caches are HashMap based at this time. The content of most of these caches would NOT change during lifetime of the user once registered but I would assume that i should expire the caches to manage memory efficiently and optimize performance which can happen once I successfully build the application.
I am planning to use Spring Boot, Hibernate to persist these objects into the datastore.
What are my recommended design options for this problem?
I am adding a caching mechanism to my web application.
After doing a research I decided to use Ehcache with Spring.
Adding #Cachable annotation to a method will execute the method once and for any further invocations the response will be returned from the cache.
The problem is that I need to decide at runtime (let's suppose according to the logged in user) which cache I want to use.
I have come to a point where I need to decide whether to use KeyGenerators or cacheResolver.
I searched for best practices, when to use each one of them but did not find any good readings. I hope If anybody can clarify the differences and elaborate on best practices and to do's and not to do's.
KeyGenerator:
#Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
#Override
public Object generate(Object o, Method method, String loggedInUserId) {
StringBuilder sb = new StringBuilder();
sb.append("cache_");
sb.append(loggedInUserId);
return sb;
}
};
CacheResolver
class loggedInUserCacheResolver implements CacheResolver {
#Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<Cache> caches = new ArrayList<>();
// do some logic and return caches to use.
return caches;
}
}
The KeyGenerator will only allow you to manipulate the creation of the cache key. However all keys will still belong to a single cache, even though they will not have any collision.
The CacheResolver allow you to use logic to pick which Cache to use.
Given your initial statement, you would need a CacheResolver for your use case. Note that I did not double check if the cache is resolved once or for each invocation.
Note that you could combine both annotations to have a custom cache and a custom key definition.
I have a class that uses a static cache that is shared between all instances of the class. I'd like to be able to set the timeout of the cache at runtime.
To provide a concrete use case: I cache values fetched from cloud storage. I'd like to refresh the values much quicker in development environments than in prod. When deploying the code, it takes an argument for the config file corresponding to that environment. This config file can contain a value for the cache refresh time.
public class Pipeline {
private static final LoadingCache<BlobId, Definitions> CACHE =
CacheBuilder.newBuilder()
.refreshAfterWrite(VALUE, TimeUnit.MINUTES) // <-- how do I set VALUE from a config file?
.build(
new CacheLoader<BlobId, Definitions>() {
public Definitions load(BlobId key) throws Exception {
return DefinitionsLoader.load(key);
}
});
...
}
To dynamically load different configurations at runtime, you can use a .properties file. In the example below I load the properties file in a static block, but you can also implement the logic in a static method that initializes the cache.
https://docs.oracle.com/javase/tutorial/essential/environment/properties.html
private static final LoadingCache<BlobId, Definitions> CACHE;
static {
Properties prop = new Properties();
try {
prop.load(new FileInputStream("config.properties"));
} catch (IOException e) {
// handle exception
}
Long value = Long.parseLong(prop.getProperty("value", "5"));
CACHE = CacheBuilder.newBuilder()
.refreshAfterWrite(value, TimeUnit.MINUTES)
.build(new CacheLoader<Integer, Definitions>() {
public Definitions load(BlobId key) throws Exception {
return DefinitionsLoader.load(key);
}
});
}
Static field initialized in their declaration are not designed to be parameterized as you want to do.
Besides, your cache loading is not flexible.
If tomorrow you change your mind or you want to use multiple of them, you cannot.
At last, it is not testable either.
If you want to provide a specific behavior to the cache loading, the most natural way to do it is changing the API of the class that contains the field.
You could provide a Pipeline constructor with a long delayInMnToRefresh parameter and you could use this parameter to set the refresh time of the cache.
public Pipeline(int delayInMnToRefresh){
CacheBuilder.newBuilder()
.refreshAfterWrite(delayInMnToRefresh, TimeUnit.MINUTES)
....
}
If you use Spring, your could using a #Autowired constructor that uses a property defined at runtime when the Spring context is loaded :
#Autowired
public Pipeline(#Value("${clould.cache.delayInMnToRefresh}") int delayInMnToRefresh){
....
}
With a property defined in this way for example in env :
clould.cache.delayInMnToRefresh=5
And another property defined in this way for example in prod :
clould.cache.delayInMnToRefresh=15
Of course, you can implement this requirement without Spring (and Spring Boot) but you should simply perform more boiler plate tasks (load the properties file before to call this method and handle yourself the notion of environment).
I have a use case where I want to populate entries into a data structure from multiple threads and after a particular size is reached start dropping old records. So I decided to use Guava Loading Cache for this.
I want to populate entries into my Loading Cache from multiple threads and I am setting eviction based policy as Size Based Eviction.
private final ScheduledExecutorService executorService = Executors
.newSingleThreadScheduledExecutor();
private final LoadingCache<String, DataBuilder> cache =
CacheBuilder.newBuilder().maximumSize(10000000)
.removalListener(RemovalListeners.asynchronous(new CustomListener(), executorService))
.build(new CacheLoader<String, DataBuilder>() {
#Override
public DataBuilder load(String key) throws Exception {
// what I should do here?
// return
}
});
// this will be called from multiple threads to populate the cache
public void addToCache(String key, DataBuilder dataBuilder) {
// what I should do here?
//cache.get(key).
}
My addToCache method will be called from multiple threads to populate the cache. I am confuse what I should be doing inside addToCache method to fill the cache and also what does my load method looks like?
Here DataBuilder is my builder pattern.
Obviously your problem is that you don't get the main purpose of a CacheLoader.
A CacheLoader is used to automatically load the value of a given key (which doesn't exist in the cache yet) when calling get(K key) or getUnchecked(K key) in way that even if we have several threads trying to get the value of the same key at the same time, only one thread will actually load the value and once done all calling threads will have the same value.
This is typically useful when the value to load takes some time, like for example when it is the result of a database access or a long computation, because the longer it takes the higher is the probability to have several threads trying to load the same value at the same time which would waste resources without a mechanism that ensures that only one thread will load the data for all calling threads.
So here let's say that your DataBuilder's instances are long to build or you simply need to ensure that all threads will have the same instance for a given key, you would then indeed need a CacheLoader and it would look like this:
new CacheLoader<String, DataBuilder>() {
#Override
public DataBuilder load(String key) throws Exception {
return callSomeMethodToBuildItFromTheKey(key); // could be new DataBuilder(key)
}
}
Thanks to the CacheLoader, you have no need to call put explicitly anymore as your cache will be populated behind the scene by the threads calling cache.get(myKey) or cache.getUnchecked(myKey).
If you want to manually populate your cache, you can simply use the put(K key, V value) method like any Cache as next:
public void addToCache(String key, DataBuilder dataBuilder) {
cache.put(key, dataBuilder);
}
If you intend to populate the cache yourself, you don't need a CacheLoader, you can simply call build() instead of build(CacheLoader<? super K1,V1> loader) to build your Cache instance (it won't be a LoadingCache anymore).
Your code would then be:
private final Cache<String, DataBuilder> cache =
CacheBuilder.newBuilder().maximumSize(10000000)
.removalListener(
RemovalListeners.asynchronous(new CustomListener(), executorService)
).build();
I have a requirement where we are loading static data from a database for use in a Java application. Any caching mechanism should have the following functionality:
Load all static data from the database (once loaded, this data will not change)
Load new data from the database (data present in the database at start-up will not change but it is possible to add new data)
Lazy loading of all the data isn't an option as the application will be deployed to multiple geographical locations and will have to communicate with a single database. Lazy loading the data will make the first request for a specific element too slow where the application is in a different region to the database.
I have been using the MapMaker API in Guava with success but we are now upgrading to the latest release and I can't seem to find the same functionality in the CacheBuilder API; I can't seem to find a clean way of loading all data at start-up.
One way would be to load all keys from the database and load those through the Cache individually. This would work but would result in N+1 calls to the database, which isn't quite the efficient solution I'm looking for.
public void loadData(){
List<String> keys = getAllKeys();
for(String s : keys)
cache.get(s);
}
Or the other solution is to use a ConcurrentHashMap implementation and handle all of the threads and missing entries myself? I'm not keen on doing this as the MapMaker and CacheBuilder APIs provide the key-based thread locking for free without having to provide extra testing. I'm also pretty sure the MapMaker/CacheBuilder implementations will have some efficiencies that I don't know about/haven't got time to investigate.
public Element get(String key){
Lock lock = getObjectLock(key);
lock.lock();
try{
Element ret = map.get(key)
if(ret == null){
ret = getElement(key); // database call
map.put(key, e);
}
return ret;
}finally {
lock.unlock();
}
}
Can anyone think of a better solution to my two requirements?
Feature Request
I don't think pre-loading a cache is an uncommon requirement, so it would be nice if the CacheBuilder provided a configuration option to pre-load the cache. I think providing an Interface (much like CacheLoader) which will populate the cache at start-up would be an ideal solution, such as:
CacheBuilder.newBuilder().populate(new CachePopulator<String, Element>(){
#Override
public Map<String, Element> populate() throws Exception {
return getAllElements();
}
}).build(new CacheLoader<String, Element>(){
#Override
public Element load(String key) throws Exception {
return getElement(key);
}
});
This implementation would allow the Cache to be pre-populated with all relevant Element objects, whilst keeping the underlying CustomConcurrentHashMap non-visible to the outside world.
In the short-term I would just use Cache.asMap().putAll(Map<K, V>).
Once Guava 11.0 is released you can use Cache.getAll(Iterable<K>), which will issue a single bulk request for all absent elements.
I'd load all static data from the DB, and store it in the Cache using cache.asMap().put(key, value) ([Guava 10.0.1 allows write operations on the Cache.asMap() view][1]).
Of course, this static data might get evicted, if your cache is configured to evict entries...
The CachePopulator idea is interesting.