Clearing Cache using spring schedule? - java

I have the following code which implements a simple cache.
public Observable<String> getSomethingEveryoneWants(String key) {
final Map<String, String>localCacheReference = GlobalCache.cache;
return Observable.create(subscriber -> {
if(!localCacheReference.containsKey(key)) {
localCacheReference.put(key, doAHeavyCallToGetValueFor(key));
}
subscriber.onNext(localCacheReference.get(key));
subscriber.onCompleted();
}).subscribeOn(Schedulers.io()).map(String.class::cast);
}
I also want the ability to clear the cache depending upon some configuration: so I did something
//By default run every mid night. This should be defined in propFile
#Scheduled(cron = "${corn.cronString:0 0 0 * * *}")
public void clearCache() {
GlobalCache.cache = new ConcurrentHashMap<>();
}
Do you see any thing wrong with this approach?
My application starts OK and works as expected for a while. But starts to fail randomly after the clearCache run about 20-30 times. Is there any side affect i need to know?
Update: Its a Spring boot application. The application is throwing null pointer While executing restTemplate.exchange() after clearCache run about 20-30 times.
If i turn off/remove the #Schedule; I am not getting any errors restTemplate.exchange() works as expected. restTemplate.exchange() executes irrespective of #Schedule is running or not and does not depend upon the cache.
The issue is not appering if i clear the cache by any other method, like checking time while reading the cache, and clearing the cache.
I am not able to understand why the restTemplete is failing when #Scheduled is used.

Related

Multi Requests break same class instance during execution

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

Spring StateMachine reusable statemachine instance

I have spring state machine like in the image below:
I want the state machine to be started at the start of the app. And after that it should go in the Re-State where on some time (Scheduled) it go in State GetOrders with SubStates (GetA, GetB and GetC). After that if there is some error it should go in the error, otherwise if everything is okay it should go in the Re-State where it should wait for the Scheduler.
This is my config
#Override
public void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception {
config.withConfiguration()
.autoStartup(true)
.machineId("orderMachine");
}
And this is the method for the scheduler:
#Scheduled(cron = "0 0 1 1 * *")
public void startStateMachine() {
StateMachine<States, Events> sm = factory.getStateMachine();
sm.start();
sm.sendEvent(Events.ReState);
}
Everything is working okay but I have noticed that every time this method executes, the stateMachine that is starting have different UUID with the previous one but the Id is same. So I think I am making multiple instances of the state machine. Is it possible to reuse the same state machine or not finishing the process. Because in my case most of the time the state of the machine should be in Re-State. It can be considered as waiting state.
Try to use SpringStateMachineService to get a state machine instance instead of explicitly retrieving it from StateMachineFactory. You can use default implementation provided by Spring:
#Bean
public StateMachineService<State, Event> stateMachineService(
final StateMachineFactory<State, Event> stateMachineFactory) {
return new DefaultStateMachineService<>(stateMachineFactory);
}
So, your code will look like:
#Scheduled(cron = "0 0 1 1 * *")
public void startStateMachine() {
// here we need to get stateMachineId from some storage
stateMachineService.acquireStateMachine(stateMachineId, true)
.sendEvent(Events.ReState);
}
In the code above you need to provide particular state machine ids. Usually, I store them in the database, query them and instantiate state machine for each one.
Persisting state machine context is out of scope of the question. See Persisting State Machine
Well your state machine should be most likely public and static. So that you would make sure by that there is only one instance of your machine.

Spring JPA: Change attribute of instance that is simultaniously being modified in scheduled task

I'm running a scheduled task in my Spring application that runs a job. The job itself is fetched at the beginning of the task. After that a loop takes place that modifies the job in each iteration (++ a counter). After the loop I merge my instance using the entity manager. It works fairly well, but I'm facing an issue trying to modify the instance from another place. Since the instance has a 'paused' flag, I'm trying to set it. But whenever I do it's quickly reset again, due to the scheduled task unsetting it again (as far as I can tell).
Here's some code:
// This method is called using the #Scheduled annotation to be looping
// constantly with one second delay between invocations.
#Transactional
public void performActions() {
Job job = jobRepository.findFirstByPausedAtIsNull();
// Skip if no unpaused job exists
if(job == null) return;
// Iterate through batch of job actions
for(Action action : job.nextActions()) {
action.perform();
job.increaseActionsPerformedCount();
// Merge the action into the persistence context
entityManager.merge(action);
}
// Merge the job into the persistence context
entityManager.merge(job);
}
Now I'm trying to be able to pause the job at any time from the outside. I use a controller endpoint to call a pause method on the jobService. This method looks like this:
public Job pause(long id) throws JobNotFoundException, JobStatusException {
Job job = this.show(id);
if(job.getPausedAt() != null) throw new JobStatusException("The job is already paused");
job.pause(); // This sets the flag on the instance, same as job.setPausedAt(new Date())
return jobRepository.save(campaign); // Uses CrudRepository
}
Now calling the method works fine and it actually returns the Job with pausedAt set. But the value is reset quickly after.
I've tried just straight up fetching a fresh instance from the database at the end of performAction and setting the modified instance pausedAt to the freshly fetched one's value.
Any idea how this could be achieved properly?
As far as I understand , You need to stop the job when the pause flag is set ... you can achieve this by applying optimistic lock ... add a #Version field to Job .... apply LockModeType.OPTIMISTIC to the job that you retrieved in performAction() -either by adding it to the find() method or call a refresh() after retrieval -the first is better- ..... now if the other endpoint changes the pause flag the version field will be incremented and you will get OptimisticLockException at persisting .... this has some implications :
1- whatever state changes in the Job , the same behavior will happen (not only the pause field)
2- You will need to handle the Exception from inside the persistence context (i.e. inside performActions()) because after returning it might be mapped to any other exception type ... this is the idea I have now, may be there is something better that gives you more control (track only the pause attribute)

how to reduce quartz scheduling latency

Im using Quartz under Spring backed by a jdbc job store, and im trying to make it trigger a job "now":
Trigger adHocTrigger = TriggerBuilder.newTrigger().withIdentity(adHocTriggerKey).forJob(jobKey).startNow().build();
quartzScheduler.scheduleJob(adHocTrigger); //now
the measured latency between this call and the time the job actually starts executing is ~30 seconds by default. here's an example job to measure one-off trigger latency that i used:
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Trigger trigger = context.getTrigger();
if (!trigger.mayFireAgain()) {
//its a one-off, compute latency
long latency = System.currentTimeMillis() - trigger.getFinalFireTime().getTime();
logger.info("firing latency is {} millis",latency);
}
}
after reading the documentation i found the reason for the latency is the "org.quartz.scheduler.idleWaitTime" configuration parameter (basically the job store polling frequency?), which can be configured as low as 1000 millis (at which point the latency im getting is on the order of 900 millis).
I understand that setting it this low may cause db thrashing (im using a jdbc job store) and is therefore not recommended, but is there any way to achieve low latency without resorting to this?
is there no optional configuration property that will cause "fire now" to actually do?
One way to solve this without lowering idleWaitTime is to modify QuartzScheduler slightly to support transactions. I don't use Spring, so I don't know what that would look like there, but this is the basic idea.
class TransactionAwareScheduler extends QuartzScheduler {
#Override
protected void notifySchedulerThread(long candidateNewNextFireTime) {
if (insideTransaction) {
transaction.addCommitHook(() -> {
super.notifySchedulerThread(candidateNewNextFireTime);
});
}
} else {
super.notifySchedulerThread(candidateNewNextFireTime);
}
}
It resolved the issue completely for us.
The documentation says 'triggerJob' will fire the job immediately. Check whether this satisfies your requirement.
quartzScheduler.triggerJob(jobKey);

Timer Service in ejb 3.1 - schedule calling timeout issue

I have created simple example with #Singleton, #Schedule and #Timeout annotations to try if they would solve my problem.
The scenario is this: EJB calls 'check' function every 5 secconds, and if certain conditions are met it will create single action timer that would invoke some long running process in asynchronous fashion. (it's sort of queue implementation type of thing). It then continues to check, but while the long running process is there it won't start another one.
Below is the code I came up with, but this solution does not work, because it looks like asynchronous call I'm making is in fact blocking my #Schedule method.
#Singleton
#Startup
public class GenerationQueue {
private Logger logger = Logger.getLogger(GenerationQueue.class.getName());
private List<String> queue = new ArrayList<String>();
private boolean available = true;
#Resource
TimerService timerService;
#Schedule(persistent=true, minute="*", second="*/5", hour="*")
public void checkQueueState() {
logger.log(Level.INFO,"Queue state check: "+available+" size: "+queue.size()+", "+new Date());
if (available) {
timerService.createSingleActionTimer(new Date(), new TimerConfig(null, false));
}
}
#Timeout
private void generateReport(Timer timer) {
logger.info("!!--timeout invoked here "+new Date());
available = false;
try {
Thread.sleep(1000*60*2); // something that lasts for a bit
} catch (Exception e) {}
available = true;
logger.info("New report generation complete");
}
What am I missing here or should I try different aproach? Any ideas most welcome :)
Testing with Glassfish 3.0.1 latest build - forgot to mention
The default #ConcurrencyManagement for singletons is ConcurrencyManagementType.CONTAINER with default #Lock of LockType.WRITE. Basically, that means every method (including generateReports) is effectively marked with the synchronized keyword, which means that checkQueueState will block while generateReport is running.
Consider using ConcurrencyManagement(ConcurrencyManagementType.BEAN) or #Lock(LockType.READ). If neither suggestion helps, I suspect you've found a Glassfish bug.
As an aside, you probably want persistent=false since you probably don't need to guarantee that the checkQueueState method fires every 5 seconds even when your server is offline. In other words, you probably don't need the container to fire "catch ups" when you bring your server back online.

Categories

Resources