I have a class played as cache which uses a Map (either HashMap or ConcurrentHashMap), I'd like to clear my Map before executing each new (http) request, e.g
#Component
public Class MyCache {
Map cache = new ConcurrentHashMap();
get(key) {
cache.computeIfAbsent(key, fetchFromDB())
}
clearCache() {
cache.clear()
}
}
#Controller
public Class MyController {
#Autowired
MyCache myCache
#Get
Response getInfo(ids) {
// give me a fresh cache at beginning of every new request
myCache.clearCache()
// load and fetch from myCache of current request
ids.foreach(id -> {
myCache.get(id)
})
}
}
Above code idea is to
initially reset cache when a new request comes in
then for all id of input(could be hundreds), fetch from cache
if same id already stored in cache, we don't need to re-call fetchFromDB.
Everything works locally with single thread, but when calling with 2 or more threads, there are chances that during the execution of thread1, thread2 started and it would call myCache.clearCache(), somehow my thread1 suddenly found nothing stored in myCache anymore for all its processed items.
The reason is because my map was in class as singleton (e.g MyCache, Controller), while even each request deals with its own thread, they will take action on same instance
What's the best way that I would fix this issue if I still wants to get a clean cache for each request comes in? Anyway I can detect if there might be other threads still executing before my current thread clearCache()
I solved it by following how Google Guava Cache works with Concurrent Hashmap and Reentrance lock as Segment
Related
I am new to Spring Boot and just implemented a normal Spring Boot application with HTTP where endpoints receive data and put in a database. Now I want some data to put in both databases and a class with data structure. Since there will be continuous operations with this data I need to operate with it as a separate process.
#Service
public class RulesManager {
private HashMap<Integer, Rule> rules = new HashMap<Integer, Rule>();
public void addRule(Rule rule) {
// Add rule to the database
}
// should be running in the background
public void updateRules(){
// Continuous check of rules and update of this.rules HashMap
}
}
#SpringBootApplication
public class RulesApplication {
public static void main(String... args) {
SpringApplication.run(RulesApplication.class, args);
// How do I call RulesManager.updateRules() to run in the background and make changes to rules hashmap???
}
}
So while listening to HTTP requests I want my application to run background process which will never stop and repeat itself. I am not sure how to call that class from the main RulesApplication class so that both http requests and background process were able to make changes to this.rules HashMap. Will be grateful for any tip or advice.
If you are just looking to start a always on process when app starts ( even better when RuleManager gets initialized ), then you should simply create a new thread in the constructor of RuleManager :
methodCalledByConstructor()
{
new Thread(()->{
// loop start
// access and check the hashmap
// do what is necessary
// sleep for a sometime
// loop end
}).start();
}
But if the work is only required when some event occurs, then use observer pattern for more elegant solution.
Try to define a new Thread for example "LocalRulesHandling" and annotate it with #Component and inside this thread add your implementations regarding the rules hashmap.
In the RulesApplication class try to get the spring context and the get the execution thread bean and then start this thread.
ApplicationContext conttext = SpringApplication.run(RulesApplication.class, args);
LocalRulesHandling handling = context.getBean(LocalRulesHandling.class);
handling.start();
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 long-running operation in a Spring Boot web application.
This is how it works:
When the user clicks a button, a POST request is made and the operation starts.
Because the operation will take a long time, it is started asynchronously and a response is sent immediately.
Using JavaScript, I periodically send GET requests to find out if the operation has finished.
Here are the request handlers:
import java.util.concurrent.Future;
#RequestMapping(value = "/start", method = RequestMethod.POST)
#ResponseBody
String start(HttpSession session) {
Future<String> result = resultService.result();
session.setAttribute("result", result);
return "started";
}
#RequestMapping(value = "/status")
#ResponseBody
String status(HttpSession session) throws Exception {
#SuppressWarnings("unchecked")
Future<String> result = (Future<String>) session.getAttribute("result");
if (result != null && result.isDone()) {
return result.get();
} else {
return "working";
}
}
And this is the long-running operation (in a separate bean):
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
#Async
#Override
public Future<String> result() {
String result = computeResult(); // takes long
return new AsyncResult<String>(result);
}
The complete example is on GitLab.
Also, here's a GIF showing how it works.
Now, this works, but the problem is that SonarQube raised an issue:
Make "Future" and its parameters serializable or don't store it in the session.
It explained that
the session [...] may be written to disk anyway, as the server manages its memory use in a process called "passivation". Further, some servers automatically write their active sessions out to file at shutdown & deserialize any such sessions at startup.
See MITRE, CWE-579 - J2EE Bad Practices: Non-serializable Object Stored in Session
Since I can't make Future serializable, what would be a better way to keep track of the long-running operation between requests?
Now, this works, but the problem is that SonarQube raised an issue:
To fix the above issue, you can write a wrapper class implementing Serializable that contains the result of the Future object along with the Future object as transient. And you can place this wrapper object in the session instead of directly putting the Future object.
Ex:
public class ResultWrapper implements Serializable {
private String result = "working"; //String, since the resultService.result() is returning Future<String>
private transient Future future; //transient as it is not serializable.
public String getResult() {
if (future != null && future.isDone()) {
result = future.get();
//Once the future is done, call the session.setAttribute(...) so that value of result field is replicated across the JVM nodes.
}
return result;
}
}
Note that this just solves the issue you have raised regarding the SonarQube. But it doesn't really provide failover or handles activation/passivation even if the session replication is active.
If there are two nodes M1 & M2 on which the webapp is running with session replication in place, the async job computeResult(); will obviously be running only on one of the machines (the one which received the initial request) and if that machine goes down all the requests will be forwarded to the other active machine and the result will always return "working" forever.
Another issue which applies even the webapp is running on a single node is that, if the session gets passivated the future will not be passivated as it is transient and so you will loose reference to that object and reactivated wrapper will have future obj as null. Finally the result is same as above case.
I have an interesting task where I need to cache the results of my method, which is really simple with spring cache abstraction
#Cachable(...)
public String getValue(String key){
return restService.getValue(key);
}
The restService.getValue() targets a REST service, which can be answering or not if the end point is down.
I need to set a specific TTL for the cache value, lets say 5 minutes, but in case if the server is down I need to return the last value, even if it extends 5 minutes.
I was thinking about having a second cachable method which have no TTL and always returns the last value, it would be called from getValue if restService returns nothing, but maybe there is a better way?
I've been interested in doing this for a while too. Sorry to say, I have not found any trivial way of doing this. Spring will not do this for you, it's more a question of whether what cache implementation spring is wrapping can do it. I assume you are using the EhCache implementation. Unfortunately this functionality does not come out the box as far as I know.
There are various ways one can achieve something similar depending on your problem
1) use an eternal cache time and have a second class Thread which periodically loops over the cached data refreshing it. I have not done this exactly, but the Thread class would need to have to look something like this:
#Autowired
EhCacheCacheManager ehCacheCacheManager;
...
//in the infinite loop
List keys = ((Ehcache) ehCacheCacheManager.getCache("test").getNative Cache()).getKeys();
for (int i = 0; i < keys.size(); i++) {
Object o = keys.get(i);
Ehcache ehcache = (Ehcache)ehCacheCacheManager.getCache("test").getNativeCache()
Element item = (ehcache).get(o);
//get the data based on some info in the value, and if no exceptions
ehcache.put(new Element(element.getKey(), newValue));
}
benefits are this is very fast for the #Cacheable caller, downside is your server might get more hits than neccessary
2) You could make a CacheListener to listen to the eviction event, store the data temporarily. And should the server call fail, use that data and return from the method.
the ehcache.xml
<cacheEventListenerFactory class="caching.MyCacheEventListenerFactory"/>
</cache>
</ehcache>
The factory:
import net.sf.ehcache.event.CacheEventListener;
import net.sf.ehcache.event.CacheEventListenerFactory;
import java.util.Properties;
public class MyCacheEventListenerFactory extends CacheEventListenerFactory {
#Override
public CacheEventListener createCacheEventListener(Properties properties) {
return new CacheListener();
}
}
The Pseudo-implementation
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.event.CacheEventListener;
import java.util.concurrent.ConcurrentHashMap;
public class CacheListener implements CacheEventListener {
//prob bad practice to use a global static here - but its just for demo purposes
public static ConcurrentHashMap myMap = new ConcurrentHashMap();
#Override
public void notifyElementPut(Ehcache ehcache, Element element) throws CacheException {
//we can remove it since the put happens after a method return
myMap.remove(element.getKey());
}
#Override
public void notifyElementExpired(Ehcache ehcache, Element element) {
//expired item, we should store this
myMap.put(element.getKey(), element.getValue());
}
//....
}
A challenge here is that the key is not very useful, you might need to store something about the key in the returned value to be able to pick it up if the server call fails. This feels a bit hacky, and I have not determined if this is exactly bullet proof. It might need some testing.
3) A lot of effort but works:
#Cacheable("test")
public MyObject getValue(String data) {
try {
MyObject result = callServer(data);
storeResultSomewhereLikeADatabase(result);
} catch (Exception ex) {
return getStoredResult(data);
}
}
a Pro here is that it will work between server restarts, and you can extend it simply to allow shared caches between clustered servers.
I had a version in an 12 clustered environment where each one checked the database first to see if any other cluster had got the "expensive" data first
and then reused that rather than make the server call.
A slight variant would also be to use a second #Cacheable method together with #CachePut rather than a DB to store the data. But this would mean doubling up in memory usage. That might be acceptable depending on your result sizes.
Maybe you can use spel to change the used cache (one using ttl and the second not) if the condition (is the service up?) is true or false, I've never used spel this way (I used it to change the key based on some request params) but I think it could work
#Cacheable(value = "T(com.xxx.ServiceChecker).checkService()",...)
where checkService() is a static method that returns the name of the cache that should be used
I have some code which (in production):
In one thread, primes a cache with data from the db
In another thread, grabs the data from the cache, and starts iterating it's properties.
This threw a LazyInitializationException.
While I know how to fix the problem, I want to get a test around this. However I can't figure out how to recreate the exception in the correct part of the test.
I have to prime the DB with some test data, therefore my test is annotated with #Transactional. Failing to do so causes the set-up to fail with... you guessed it... LazyInitializationException.
Here's my current test:
#Transactional
public class UpdateCachedMarketPricingActionTest extends AbstractIntegrationTest {
#Autowired
private UpdateCachedMarketPricingAction action;
#Autowired
private PopulateMarketCachesTask populateMarketCachesTask;
#Test #SneakyThrows
public void updatesCachedValues()
{
// Populate the cache from a different thread, as this is how it happens in real life
Thread updater = new Thread(new Runnable() {
#Override
public void run() {
populateMarketCachesTask.populateCaches();
}
});
updater.start();
updater.join();
updateMessage = {...} //ommitted
action.processInstrumentUpdate(updateMessage);
}
So, I'm priming my cache in a separate thread, to try to get it outside of the current #Transaction scope. Additionally, I'm also calling entityManager.detatch(entity) inside the cache primer, to try to ensure that the entities that exist within the cache can't lazy-load their collections.
However, the test passes... no exception is thrown.
How can I forcibly get an entity to a state that when I next try to iterate it's collections, it will throw the LazyInitializationException?
You need to ensure that the transactions for each operation are committed, independent of each other. Annotating your test method or test class with #Tranactional leaves the current test transaction open and then rolls it back after execution of the entire test.
So one option is to do something like the following:
#Autowired
private PlatformTransactionManager transactionManager;
#Test
public void example() {
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// add your code here...
}
});
}
You could invoke your first operation in its own callback, and then invoke the second operation in a different callback. Then, when you access Hibernate or JPA entities after the callbacks, the entities will no longer be attached to the current unit of work (e.g., Hibernate Session). Consequently, accessing a lazy collection or field at that point would result in a LazyInitializationException.
Regards,
Sam
p.s. please note that this technique will naturally leave changes committed to your database. So if you need to clean up that modified state, consider doing so manually in an #AfterTransaction method.