I'm developing a Java EE 7 application on wildfly 8.2 and need to run a periodic background task. I inject an executor service and schedule a task, this part is working fine:
#Resource
private ManagedScheduledExecutorService executorService;
...
executorService.scheduleWithFixedDelay(() -> {
try {
// do some stuff
} catch (Throwable t) {
log.error("Error", t);
}
}, 0, 1, TimeUnit.MINUTES);
Now the (actually nice) feature is that upon redeploy the scheduled task is saved and therefore is still scheduled in the new deployment.
But how can I detect if the task is already scheduled to avoid scheduling it multiple times?
I tried to use a ScheduledFutureand cancel the task on #PreDestroy
and #PrePassivate
reloadTreeFuture = executorService.scheduleWithFixedDelay(() -> {
...
#PreDestroy
#PrePassivate
protected void shutdown() {
reloadTreeFuture.cancel(true);
}
This is working fine as long as the corresponding task is not executing at the very moment the cancel is fired. Since the task is long running and running frequently the chance of hitting it in the middle of an execution is somewhat high.
If the cancel is fired while the task is still executing the cancel seems to do nothing. It immediatly returns and the method ScheduledFuture.isDone() also returns true but from the logs I can see the task is still executing in the background until it hits a point where it needs an injected Bean which is not available due to the undeployment process. The process then ends with org.jboss.msc.service.ServiceNotFoundException - but is still scheduled.
reloadTreeFuture.cancel(true);
while (!reloadTreeFuture.isDone()) {
Thread.sleep(200); // I know this is bad - it's just for testing
}
So basic question: how can I make sure the task is not scheduled twice (or even more)?
You could write down the id of each task and check them before executing. Of course this may be not a best solution, but it works.
Related
Folks, is it possible to obtain currently used Scheduler within an operator?
The problem that I have is that Mono.fromFuture() is being executed on a native thread (AWS CRT Http Client in my case). As result all subsequent operators are also executed on that thread. And later code wants to obtain class loader context that is obviously null. I realize that I can call .publishOn(originalScheduler) after .fromFuture() but I don't know what scheduler is used to materialize Mono returned by my function.
Is there elegant way to deal with this?
fun myFunction(): Mono<String> {
return Mono.just("example")
.flatMap { value ->
Mono.fromFuture {
// invocation of 3rd party library that executes Future on the thread created in native code.
}
}
.map {
val resource = Thread.currentThread().getContextClassLoader().getResources("META-INF/services/blah_blah");
// NullPointerException because Thread.currentThread().getContextClassLoader() returns NULL
resource.asSequence().first().toString()
}
}
It is not possible, because there's no guarantee that there is a Scheduler at all.
The place where the subscription is made and the data starts flowing could simply be a Thread. There is no mechanism in Java that allows an external actor to submit a task to an arbitrary thread (you have to provide the Runnable at Thread construction).
So no, there's no way of "returning to the previous Scheduler".
Usually, this shouldn't be an issue at all. If your your code is reactive it should also be non-blocking and thus able to "share" whichever thread it currently runs on with other computations.
If your code is blocking, it should off-load the work to a blocking-compatible Scheduler anyway, which you should explicitly chose. Typically: publishOn(Schedulers.boundedElastic()). This is also true for CPU-intensive tasks btw.
I'm trying to complete a task in the background using UIApplication.shared.beginBackgroundTask but for some reason the expirationHandler is never called. The task I'm trying to complete is a video export from photo library but sometimes the export cannot be completed in time while the user is using the app in the foreground.
This is the code I'm using :
func applicationDidEnterBackground(_ application: UIApplication) {
if backgroundTask == .invalid && UploadQueue.instance.hasMoreWork() {
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "ExportQueue") {
NSLog("DriveLog - System is requesting end. Still more work to do ...")
self.endBackgroundTask()
}
print("Invalid? \(backgroundTask == .invalid)")
NSLog("DriveLog - Starting background task: %i", backgroundTask.rawValue)
}
}
func endBackgroundTask() {
NSLog("DriveLog - End called")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
I'm also calling :
(UIApplication.shared.delegate as! AppDelegate).endBackgroundTask()
during my task if I finish it earlier.
However I never see my expirationHandler being called in the log.
I have also tried putting beginBackgroundTask when starting the task in foreground but I get a warning message about task expiration while being in foreground.
You have not understood what the expiration handler is. It is called only if your time expires. Hence the name.
As soon as you call begin, start your task in the next line (not in the expiration handler). And when you are finished, call end.
You thus need to end the background task in two places: in the expiration handler, and outside it after actually performing your task.
It is very important to call end in both places, because if you fail to do so, the system will decide that you are a bad citizen and will never grant you any extra background time at all.
So, this is the diagram of the flow you need to construct:
Also note that this has nothing to do with UIBackgroundModes. That's a totally different mechanism.
matt's answer covers everything. I'm just going to try to give the same answer in different words because your edit suggests that matt's answer wasn't clear to you. (Read it again, though, it really does cover everything I'm going to say here, just in different words.)
You should not call beginBackgroundTask in applicationDidEnterBackground. You call it when you start whatever task you want time for. In your example that's going to be somewhere inside of UploadQueue. You don't call beginBackgroundTask when going into the background. You call it when you're starting a task that you would like to finish even if you go into the background.
Background tasks generally do not belong to the UIAppDelegate. They belong to the thing that creates the task (in your case: UploadQueue). You can create all the background tasks you want. They cost almost nothing. It's not just one "I want background" at the app level. Read matt's flow chart closely.
It's unclear from your question why you expect the expiration handler to be called. Do you expect your task to task to take so long that the OS forces you to stop it? That's what the expiration handler is for. If you've built your system correctly, it should rarely be called. Your task should end long before it's expired.
For full docs on how to do this, see Extending Your App's Background Execution Time. In particular note the caution:
Don’t wait until your app moves to the background to call the beginBackgroundTask(withName:expirationHandler:) method. Call the method before performing any long-running task.
When using Quartz.net to schedule jobs, I occasionally receive an exception when instantiating a job. This, in turn causes Quartz to set the trigger for the job to an error state. When this occurs, the trigger will cease firing until some manual intervention occurs (restarting the service since I'm using in-memory job scheduling).
How can I prevent the error state from being set, or at the very least, tell Quartz to retry triggers that are in the error state?
The reason for the exception is due to flaky network calls that are required to get configuration data that is passed in to the job's constructor. I'm using a custom IJobFactory to do this.
I've seen other references to this without resolutions:
https://groups.google.com/forum/#!topic/quartznet/8qaT70jfJPw
http://forums.terracotta.org/forums/posts/list/2881.page
For the record, I consider this a design flaw of Quartz. If a job can't be constructed once, that doesn't mean it can't always be constructed. This is a transient error and should be treated as such. Stopping all future scheduled jobs violates the principle of least astonishment.
Anyway, my hack solution is to catch any errors that are the result of my job construction and instead of throwing an error or returning null to return a custom IJob instead that simply logs an error. This isn't perfect, but at least it doesn't prevent future triggering of the job.
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
var job = this.container.Resolve(bundle.JobDetail.JobType) as IJob;
return job;
}
catch (Exception ex)
{
this.logger.Error(ex, "Exception creating job. Giving up and returning a do-nothing logging job.");
return new LoggingJob(this.logger);
}
}
When exception occurs on trigger instatiating IJob class, then trigger change it TRIGGER_STATE to ERROR, and then trigger in this state will no longer fire.To reenable trigger your need to change it state to WAITING, and then it could to fire again.
Here the example how your can reenable yours misfired trigger.
var trigerKey = new TriggerKey("trigerKey", "trigerGroup");
if (scheduler.GetTriggerState(trigerKey) == TriggerState.Error)
{
scheduler.ResumeTrigger(trigerKey);
}
Actually the best way to reset Trigger from ERROR state is:
private final SchedulerFactoryBean schedulerFactoryBean;
Scheduler scheduler = schedulerFactoryBean.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);
if (scheduler.getTriggerState(triggerKey).equals(Trigger.TriggerState.ERROR)) {
scheduler.resetTriggerFromErrorState(triggerKey);
}
Note:
You should never modify the records in a table from a third-party library or software manually. All changes should be made through the API to that library if there is any functionality.
JobStoreSupport.resetTriggerFromErrorState
How can I prevent the error state from being set, or at the very least, tell Quartz to retry triggers that are in the error state?
Unfortunately, in current version, you cannot retry those triggers. As per the documentation of Quartz,
It should be extremely rare for this method to throw an exception -
basically only the case where there is no way at all to instantiate
and prepare the Job for execution. When the exception is thrown, the
Scheduler will move all triggers associated with the Job into the state, which will require human
intervention (e.g. an application restart after fixing whatever
configuration problem led to the issue with instantiating the Job).
Simply put, you should follow good object oriented practices: constructors should not throw exceptions. Try to move pulling of configuration data to job's execution phase (Execute method) where retries will be handled correctly. This might mean providing a service/func via constructor that allows pulling the data.
To change the trigger state to WAITING the author also suggests that a way could be to manually update the database.
[...] You might need to update database manually, but yeah - if jobs cannot be instantiated it's considered quite bad thing and Quartz will flag them as broken.
I created another job scheduled at app startup that updates the triggers in error state to recover them.
UPDATE QRTZ_TRIGGERS SET [TRIGGER_STATE] = 'WAITING' WHERE [TRIGGER_STATE] = 'ERROR'
More information in this github discussion.
My objective is to have Quartz.NET execute a job at precisely 25Hz or every 40ms.
I'm using the following trigger:
ITrigger MyTrigger = TriggerBuilder.Create().WithIdentity("T1").ForJob("MyJob").WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromMilliseconds(40)).RepeatForever()).Build();
and the following job:
[DisallowConcurrentExecution]
private class MyJob : Quartz.IJob
{
public void Execute(IJobExecutionContext context)
{
Idx++;
Console.WriteLine("Job {0} fired at {1}ms", Idx, MyStopWatch.ElapsedMilliseconds);
}
}
The problem is that the first 150 executions or so fire too quickly. For example the first 60 iterations all fire at either 20ms or 21ms on the stopwatch. Afterward they fire in bunches every 200ms until it becomes stable around 1000ms, and then starts firing every 40-42ms as intended.
How can I prevent Quartz from triggering a job if the previous job was fired within 40ms?
What is the source of this behavior?
You should not rely on Quartz for millisecond precision scheduling. Quartz has a lot of infrastructure (misfire checks, compensation etc) that will make it unsuitable for this kind of precision.
I'd argue that stable firing on bare virtual machine level could be a challenge too due to GC etc that might pause things a bit.
Quartz can do a lot for you but in this case I'd just go with custom Thread or Timer implementation if these are your requirements.
I am trying to understand how I shall port my Java chess engine to dart.
So I have understood that I should use an Isolates to run my engine in parallell with the GUI but how can I force the engine to terminate the search.
In java I just set some boolean that where shared between the engine thread and the gui thread.
Answer I got:
You should send a message to the isolate, telling it to stop. You can simply do something like:
port.send('STOP');
My request
Thanks for the clarification. What I don't understand is that if the chess engine isolate is busy due to a port.send('THINK') command how can it respond to a port.send('STOP') command
Each isolate is single-threaded. As long as your program is running nobody else will have the means to interfere with your execution.
If you want to be able to react to outside events (including messages from other isolates) you need to split your long running execution into smaller parts. A chess-engine probably has already some state to know where to look for the next move (assuming it's built with something like A*). In this case you could just periodically interrupt your execution and resume after a minimal timeout.
Example:
var state;
var stopwatch = new Stopwatch()..run();
void longRunning() {
while (true) {
doSomeWorkThatUpdatesTheState();
if (stopwatch.elapsedMilliseconds > 200) {
stopwatch.reset();
Timer.run(longRunning);
return;
}
}
}
The new API will contain a
isolate.kill(loopForever ? Isolate.IMMEDIATE : Isolate.AS_EVENT);
See https://code.google.com/p/dart/issues/detail?id=21189#c4 for a full example.