How to have an asynchronous and non-concurrent scheduler in Spring? - java

I have in the main class that starts the app:
#SpringBootApplication
#EnableAsync
public class ExperianRequestBotApplication extends RefApplication {
public ExperianRequestBotApplication() throws RefException {
super();
}
public static void main(String[] args) throws RefException {
try {
new ExperianRequestBotApplication().start(args);
} catch (Exception e) {
System.out.println(" ------- OFFLINE ------- ");
System.out.println("La aplicación no esta disponible por :" + e);
}
}
}
and a scheduler
#Component
public class ScheduledTaskSincronizarContactos {
#Autowired
private ExperianRequestBotService experianRequestBotService;
private final static Logger LOG = LoggerFactory.getLogger(ScheduledTaskSincronizarContactos.class);
// Método Shedule encargado de sincronizar los usuarios modificados con Experian
#Async
#Scheduled(cron = "0 */15 * ? * *")
public void SincronizarContactos() throws Exception {
I want to prevent the scheduler from being launched again if the internal process takes more than 15 minutes to start the task again.
I can't find the way. I have tried implementing #DisallowConcurrentExecution with the quartz library but it is still concurrent.
Any ideas?

I've faced a similar issue with concurrency while using spring scheduling. We had a lot of jobs all running on the same service and interfering with each other. We switched to using Quartz Scheduling - felt simpler than the Spring multithreaded scheduler with a bunch of other features that we wanted. This repo was really helpful.
https://gitlab.com/johnjvester/jpa-spec-with-quartz/-/blob/master/src/main/java/com/gitlab/johnjvester/jpaspec/config/QuartzConfig.java
Quartz scheduling also has the advantage of being persistent - when it starts up, it will trigger all the missed jobs. One can also alter cron statements programmatically. This might be an overkill for your use case but it is worth a look. :) Also, what Wasif said - use delays to define when the job should be run vs a cron expression and you're guaranteed a win even with Spring!

By default, all scheduled tasks run on a single thread in Spring boot unless you define a thread pool implementation yourself.
Here is a link for more reference that came in handy for me once:
https://crmepham.github.io/spring-boot-multi-thread-scheduling/
I'm surprised to see that your having the issue with the tasks running concurrently in the first place.
How i'm currently doing scheduled tasks whenever i need to, i place #EnableScheduling annotation on the main class, and use fixed rate and initial delay.
Maybe you could use fixed rate and initial delay instead of cron to achieve what you are trying to do.

Remove #Async from the method. #Async causes the method to be called on a separate executor.

The problem is that your job might take longer than the time planned for its run and the time in between runs. One idea would be to store somewhere the last start time and the last end time of the cron job. If the last end time is earlier than the last start time, then at the next run, ignore everything. That is, you need something like this:
#SpringBootApplication
#EnableAsync
public class ExperianRequestBotApplication extends RefApplication {
public ExperianRequestBotApplication() throws RefException {
super();
}
public static void main(String[] args) throws RefException {
try {
if (ExperianRequestBotApplication.shouldRun()) {
new ExperianRequestBotApplication().start(args);
}
} catch (Exception e) {
System.out.println(" ------- OFFLINE ------- ");
System.out.println("La aplicación no esta disponible por :" + e);
}
}
}
Note that a conditional with a boolean validator is wrapped around the bulk of your job. That shouldRun should check whether the last start date is smaller than the last end date. You will also need to make sure that your job manages these values. It is true that with this approach your job is still being executed every 15 minutes, but, if the last run has not finished yet, then the current run will be extremely quick, it will just check whether the job is still running and if so, then it will avoid doing anything. Otherwise it will properly execute the job. This is simple to implement, protects you from ending up in running the same long job multiple times simultaneously or in sequence and will provide some breathing space for the system to perform other operations.

Thanks all for your answers, finally we've decide to use quartz library. The easiest way, using #DisallowConcurrentExecution we can have async jobs not executing concurrently.

Related

Is there a way to automatically "unschedule" recurring jobs with JobRunr and Spring Boot?

I'm using JobRunr 6.0.0 (OSS) to execute recurring jobs in a Spring Boot backend (2.7.4). I use the Spring Boot starter jobrunr-spring-boot-2-starter that allows me to schedule a job with the annotation #Recurring. This works very well. My problem is when I want to "unschedule" a recurring job: removing (or commenting) the #Recurring annotation does not unschedule the job.
To be more specific, here's an example of how I use JobRunr to schedule a task that should happen every 30 seconds:
#Service
public class MyService {
#Recurring(cron = "*/30 * * * * *", id = "my-id")
public void doSomething() {
LOGGER.info("did something");
}
}
There's nothing very special in the configuration. The part of the application.properties related to JobRunr looks like this:
org.jobrunr.background-job-server.enabled=true
org.jobrunr.dashboard.enabled=false
org.jobrunr.background-job-server.worker-count=2
org.jobrunr.background-job-server.poll-interval-in-seconds=10
So far, everything is working as expected. The job is indeed executed every 30 seconds. And an entry in the JobRunr table in charge of keeping tracks of the recurring jobs (jobrunr_recurring_jobs) is properly added.
My problem is when I remove the #Recurring annotation or when I completely remove the bit of code that was scheduled recurently (because the code of my backend has to evolve and this particular bit is not useful anymore, or because there was a bit of refactoring, or...) For instance, my code would look like this:
#Service
public class MyService {
// #Recurring(cron = "*/30 * * * * *", id = "my-id")
// public void doSomething() {
// LOGGER.info("did something");
// }
}
In this situation, the code that was previously scheduled has disappeared. But the entry corresponding to the old job is still present in the jobrunr_recurring_jobs table. This results in many errors in the logs because JobRunr is trying to execute a bit of code that does not exist anymore.
I could manually delete the entry in the jobrunr_recurring_jobs table or use the provided dashboard to do so (though, I don't plan to keep the dashboard in the production environment). This is quite error-prone.
Is there a way to configure JobRunr so that it would "clean automatically" the recurring jobs that are not scheduled anymore?
There are a couple of ways to delete Recurring Jobs:
there is the dashboard as you mentioned - if you can keep it accessible internally only in your PRD environment, you can delete it via there
give your recurring job an id - then you can delete it programmatically
JobRunr Pro comes with a migration api that will not only allow you to migrate these jobs (or delete them if you want) but also gives you the option to add a test case so that your CI/CD fails if you have jobs that are scheduled in PRD but not available anymore in your current code.
To give your recurring jobs an id and delete them, see the example below:
#Service
public class MyService {
#Recurring(id = "my-id", cron = "*/30 * * * * *")
public void doSomething() {
LOGGER.info("did something");
}
}
And to delete them:
jobScheduler.delete("my-id");

Liferay 7.1 - Kill a thread execution from scheduler jobs

I have my following job in Liferay 7.1 :
#Component(
immediate = true, property = {"cron.expression=0 5 10 * * ? *"},
service = CustomJob.class
)
public class CustomJob extends BaseMessageListener {
....
#Override
protected void doReceive(Message message) throws Exception {
// HERE I CALL A SERVICE FUNCTION TO INACTIVATE USER, SEND MAILS, READ FILES TO IMPORT DATA
RunnableService rs = new RunnableService();
rs.run();
}
....
}
And my RunnableService :
public class RunnableService implements Runnable {
#Override
public synchronized void run() {
// DO MY STUFF
}
}
The job is working great, but another instance of the job can be started even when the service execution from the first call hasn't finished.
Is there any solutions to kill the first process ?
Thanks,
Sounds like there are several options, depending on what you want to achieve with this:
You shouldn't interrupt threads with technical measures. Rather have your long-running task check frequently if it should still be running, otherwise terminate gracefully - with the potential of cleaning up after itself
You can implement your functionality with Liferay's MessageBus - without the need to start a thread (which isn't good behavior in a webapp anyway). The beauty of this is that even in a cluster you end up with only one concurrent execution.
You can implement your functionality outside of the Liferay process and just interact with Liferay's API in order to do anything that needs to have an impact on Liferay. The beauty of this approach is that both can be separated to different machines - e.g. scale.

How do I interrupt Java code that doesnt finish and doesnt throw ThreadInterruptionException

A load of tasks are submitted to my application but it keeps hanging and I track it down to this code:
uk.ac.shef.wit.simmetrics.tokenisers.TokeniserWhitespace.tokenizeToArrayList(TokeniserWhitespace.java:133)
uk.ac.shef.wit.simmetrics.similaritymetrics.CosineSimilarity.getSimilarity(CosineSimilarity.java:142)
com.jthink.songkong.match.Scorer.compareValues(Scorer.java:74)
com.jthink.songkong.match.Scorer.calculateTitle(Scorer.java:704)
com.jthink.songkong.match.Scorer.matchRecordingOrTrackTitle(Scorer.java:652)
com.jthink.songkong.match.Scorer.calculateTrackScoreWithinRelease(Scorer.java:538)
com.jthink.songkong.match.Scorer.scoreMatrix(Scorer.java:396)
com.jthink.songkong.match.Scorer.calculateReleaseScore(Scorer.java:1234)
It is basically an string matching algorithm in a 3rd party library but it does not throw ThreadInterruptedException so does this mean I cannot interrupt it, but is certainly a long running process having run for 30 minutes. I have a reference to the thread monitoring this code but is there any way to stop it.
Of course I am looking to fix the 3rd party library but in the meantime I need a way to stop my application hanging by cancelling these hanging tasks. What makes it worse for me is that I use Hibernate, and a hibernate session is created in this stack and then because this TokeniserWhitespace method never finishes my session (and hence database connection) is never released therefore eventually I ran out of database connections and the application completely hangs.
I would try Thread.interrupt() as even though the interrupted exception isn't thrown it doesn't necessarily mean that the 3rd party library doesn't handle it correctly in some other way. If that doesn't work then I would consider Thread.stop() even though it is depricated in the hopes that the library's design can handle sudden termination and doesn't end up in an inconsistent state (due to the transactional nature in which you are using it, I doubt there are many shared objects to get corrupted). Thread.stop() will cause the event to stop immediately and throw a ThreadDeath exception no matter what it is doing and it was deprecated by Sun due to the chance of leaving shared objects in an unknown and inconsistent state.
You can can use the Akka library to wrap the problematic code - this will let you kill and restart the code if it hangs.
import scala.concurrent.Future;
public interface ProblemCode {
public Future<String> stringMatch(String string);
}
public class ProblemCodeImpl implements ProblemCode {
public Future<String> stringMatch(String string) {
// implementation
}
}
import akka.actor.ActorSystem;
import akka.actor.TypedActor;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.actor.TypedProps;
public class Controller {
private final ActorSystem system = ActorSystem.create("Name");
private ProblemCode problemCodeActor = getProblemCodeActor();
private ProblemCode getProblemCodeActor() {
return TypedActor.get(system).typedActorOf(new TypedProps<ProblemCodeImpl>(ProblemCode.class, ProblemCodeImpl.class));
}
public String controllerStringMatch(String string) {
while(true) {
try {
// wait for a Future to return a String
return Await.result(problemCodeActor.stringMatch(string), Duration.create(5000, TimeUnit.MILLISECONDS));
} catch (TimeoutException e) {
// Await timed out, so stop the actor and create a new one
TypedActor.get(system).stop(problemCodeActor);
problemCodeActor = getProblemCodeActor();
}
}
}
}
In other words, the controller calls stringMatch on the problem code that has been wrapped in an actor; if this times out (5000 milliseconds in my example) you stop and restart the actor and loop through the controller method again.
ProblemCode will only execute one method at a time (it will enqueue subsequent invocations), so if you want to concurrently execute stringMatch you'll need to create multiple actors and create a router out of them / place them in a queue / etc.

For Junit issue: when test multiple thread

I use JDK ScheduledThreadPoolExecutor to do schdule job. I give simple code as below.
class Job implements Callable<Void>{
public Long id;
#Override
public Void call() throws Exception {
if (!isOk(id)) {
return null;
}
_context.exe(id);
return null;
}
void setId(Long id) {
this.id = id;
}
}
Every time i add this job to schedule service:
public void schedule() {
Job job = new Job();
job.setId(1L);;
_scheduledExecutor.schedule(job, 1000, TimeUnit.MILLISECONDS) ;
}
this job will delay call context's exe method.
My Question: I want assert _context's exe method called or not? How can I do this?
What I do currently, I try to add log in call() method and verify UT by my eyes.
PS: For this UT, I also try to mock _context's exe method, but job run in other thread, So I cannot assert it in currently thread. anyone have idea to help me write assert for this case?
Currently I do below way, but I still think there better solution for this, Just I don't known.
_context is instance of Context, I extend from this class.
public class UTContext extends Context {
public UTTestCase utTestCase ;
#Override
public void exe(Long id) {
utTestCase.setIsCall(true);
}
public void setUtTestCase(UTTestCase utTestCase) {
this.utTestCase = utTestCase;
}
}
Then I will assert isCall var in UT.
Is any one have good idea for this , pls give me answer. Thank you very much.
You are testing a piece of the middle of the call hierarchy, namely the thread creator/dispatcher code. That means you have to drive the code from the top and test either from the top or the bottom. There are several patterns for how you do that.
Either you instrument the bottom (exe(id)) or you measure from the top. With the scheduling delay, measuring from the top becomes very difficult.
Does exe() have side effects? Is that side effect testable from your test code? Can you infer the operation of one invocation of exe()? Can you infer the invocation of more than one? If the answer to any of these is "no", then you will have to go further.
#RamonBoza has provided a good solution.
You could also create a testable version of class Job, thus:
class JobUT extends Job {
#Override
public Void call() throws Exception {
Void result = super.call();
// Report error if wrong return result
}
}
(I know there are problems with the above code as Void is not being handled properly. I'm not in a position to refactor your code.)
You may also achieve the same objective using Aspect Oriented Programming, where you intercept the call after it has completed and perform the same testing.
For asserting on multithreading I usually create 'N' threads, and assign a different value to set for each thread.
Then join them all and at the end just check if the data of each thread is ¿stored? for example.
Imagine, you create 1000 threads that stores an integer to a database, so after those 1000 threads has finished you have to check if in the database all the data is stored.
A more hard kind of test is for integration test and shall be perform with different scenarios and middleware (OpenNebula, Amazon cloud, etc).
After server day, I know how to verify it now. I attached other question for reference:
Assert times of expectLastCall
Service service = EasyMock.createMock(Service.class);
service.applyInitialDump(entities);
EasyMock.expectLastCall().times(100);
processor.processInitialDump(entities)
EasyMock.verify(service);

Is there a way to pause a unit test without blocking the thread

I'm using play 1.2.4 and I'm trying to set up a unit test to test a job.
My job runs at every 2 second and changes the status of certain objects based on some conditions. This is what I use to do this.
#Every("2s")
public class GameScheduler extends Job {
public void doJob(){
//Fetch of object from db and status change based on conditions happens here
}
}
Now in my unit test, I setup those conditions but I want the test to wait say 3 seconds before fetching one of the setup objects and do an assert Equals on its status to see if the job... well did it's job.
If I use
pause(3000);
or something like
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
The test stops but the job also stops. It seems like the job and the test are on the same thread. Is there a way to pause the test without stopping the job? Something like the await() method in the controller
You don't need to test the scheduler because it is supposed to work (since the framework is handling it). What you need is just test if the doJob method is doing its work. So, just write a test like this:
GameScheduler job = new GameScheduler();
job.doJob();
// assert whatever you want here
Though simply testing the job (without waiting for the scheduler to run it for you) would do the trick in most (read this as: probably all) situations, there are some in which it might be interesting to not have to trigger it manually.
For instance, you have a cluster of play apps that share a common configuration set. Change one config in one slave, all the others take note and do the same. Let's say the configuration is kept in memcached. One useful unit test is to manually change some setting using Cache.set, wait for the amount of time it takes for the configurationObserver job to run, then check that the internal config has been updated. This would be even more helpful if there would be a series of jobs updating the configuration, etc.
To do that, you must remember that play in DEV mode uses one thread (this helps debugging a lot, btw). You can simply add this line to your application.conf: %test.application.mode=prod and you'll have multiple threads.
Later edit: It appears that setting the mode to prod doesn't really help in this case. What does help is this: use some "await" magic.
#Test
public void myTest() {
final Lock lock = new ReentrantLock();
final Condition goAhead = lock.newCondition();
/* Here goes everything you need to do before "pausing" */
lock.lock();
try {
/**
* Set whatever time limit you want/need
* You can also use notifiers like goAhead.signal(), from within another thread
*/
goAhead.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assertTrue(whateverINeedToTest);
} finally {
lock.unlock();
}
}

Categories