Spring #Async cancel and start? - spring

I have a spring MVC app where a user can kick off a Report generation via button click. This process could take few minutes ~ 10-20 mins.
I use springs #Async annotation around the service call so that report generation happens asynchronously. While I pop a message to user indicating job is currently running.
Now What I want to do is, if another user (Admin) can kick off Report generation via the button which should cancel/stop currently running #Async task and restart the new task.
To do this, I call the
.. ..
future = getCurrentTask(id); // returns the current task for given report id
if (!future.isDone())
future.cancel(true);
service.generateReport(id);
How can make it so that "service.generateReport" waits while the future cancel task kills all the running threads?
According to the documentation, after i call future.cancel(true), isDone will return true as well as isCancelled will return true. So there is no way of knowing the job is actually cancelled.
I can only start new report generation when old one is cancelled or completed so that it would not dirty data.

From documentation about cancel() method,
Subsequent calls to isCancelled() will always return true if this method returned true
Try this.
future = getCurrentTask(id); // returns the current task for given report id
if (!future.isDone()){
boolean terminatedImmediately=future.cancel(true);
if(terminatedImmediately)
service.generateReport(id);
else
//Inform user existing job couldn't be stopped.And to try again later
}

Assuming the code above runs in thread A, and your recently cancelled report is running in thread B, then you need thread A to stop before service.generateReport(id) and wait until thread B is completes / cancelled.
One approach to achieve this is to use Semaphore. Assuming there can be only 1 report running concurrently, first create a semaphore object acccessible by all threads (normally on the report runner service class)
Semaphore semaphore = new Semaphore(1);
At any point on your code where you need to run the report, call the acquire() method. This method will block until a permit is available. Similarly when the report execution is finished / cancelled, make sure release() is called. Release method will put the permit back and wakes up other waiting thread.
semaphore.acquire();
// run report..
semaphore.release();

Related

quarkus/mutiny how to trigger a side job without waiting it

quarkus reactive uses mutiny to handle task asynchronously.
But, the flow is always wait every job to finish, then returns the result.
Sometime, I just want to trigger a job and let it run in the background without waiting it to be done.
Any suggestion or example?
Uni<Integer> mainJob() {
// fake logic
return Uni.createFrom().item(1);
}
Uni<Void> sideJob(int n) {
// fake logic
logger.log("result = " + n);
}
#Path("test")
Uni<Integer> testExample() {
return mainJob().onItem().call(n -> sideJob(n));
}
The upper code only returns after sideJob() is done. But, I just want to return the result immediately once mainJob is done, with sideJob triggered and run in background.
Any suggestion on it?
ManagedExecutor may be a way to do but it seems not natural in this case. The side job may/not be long running.
According to the Uni interface documentation:
To trigger the computation, a UniSubscriber must subscribe to the Uni. It will be notified of the outcome once there is an item or failure event fired by the observed Uni. A subscriber receives (asynchronously) a UniSubscription and can cancel the demand at any time.
Thus, the only way to start the execution of a Uni is by subscribing to it, even by calling uni.await().indefinitely() you are, in fact, subscribing to the Uni as we can see in the documentation of the indefinitely() method:
Subscribes to the Uni and waits (blocking the caller thread) indefinitely until a item event is fired or a failure event is fired by the upstream uni.
Invoking the call() method is nothing more than chaining a new function that will be included in the stream that will be executed when the Uni is subscribed. This way, when the testExample() method returns the result of the call(), it is not executing and waiting for the Uni to finish, it is actually returning the result immediately.
However, whoever is going to receive the final result must wait for the Uni stream to finish, so the client waiting for the HTTP response will be waiting for the sideJob() to finish in order to receive the original value, but once again, your testExample() method is not waiting for anything, it returns the Uni immediately without waiting for it to be executed.

Finish workflow when activity goes wrong

I have a workflow that executes a couple of activities. When the activity finish, it returns an Outcome either Done or Cancel, from outside and before running the next activity, I need to check if the previous activity was ok or not, in case not, I need to cancel the workflow. I have this
public class CreateEmployee : IWorkflow
{
public void Build(IWorkflowBuilder builder)
{
builder
.WithDisplayName(this.GetType().Name)
.Then<GetDataById>(x => x.WithDisplayName(x.ActivityType.Name))
.When(OutcomeNames.Cancel).Finish()
.Then<InsertEmployee>(x => x.WithDisplayName(x.ActivityType.Name))
.When(OutcomeNames.Cancel).Finish()
.Then<InsertMapping>(x => x.WithDisplayName(x.ActivityType.Name))
.When(OutcomeNames.Cancel).Finish();
}
}
For example, after executing activity GetDataById, if the return is "Cancel", I call Finish(), is this going to stop just the activity and continue the workflow or the workflow will stop completely? I'm not able to test it because I'm using DI and I need to prepare the whole unit test, because I didn't find anything directly related to cancel the whole workflow
I'm not sure if I have fully understood your question, but in the documentation about finish activity it's stated that:
when this activity is used within a workflow, the workflow instance
will enter the Finished state. When used in a child composite
activity, that activity will stop execution and yield back control to
its container. However, it will not stop workflow execution itself.

How to Stop the Spring Batch Job Execution Immediately or forcefully?

I have a spring batch job and it has 3 steps, the 3rd step has some tasklet as below. Now when we try to stop the job using,
jobOperator.stop(id);
it sends the STOP signal when STEP 3 is in progress and interrupts only when all the tasklet in the STEP 3 is completed. Let's say it has 10 tasks, although we sent the stop signal when it was STEP 3 and Task 1 in progress - it does not stop there. It finishes all the 10 tasks and then marks this STEP 3 status as COMPLETED. Is there any way we can stop step 3 while processing the first task? I did see the spring batch documentation and did not find much. Below is the sample code.
Job:
#Bean(JobConstants.CONTENT_UPGRADE_JOB)
public Job upgradeContentJob() {
Tasklet tasklet = context.getBean("incremental_deploy_tasklet", Tasklet.class);
SimpleJobBuilder jobBuilder = jobBuilderFactory.get(JobConstants.CONTENT_UPGRADE_JOB)
.listener(upgradeJobResultListener())
.start(initContent())
.next(stepBuilderFactory.get("create_snapshot_tasklet").tasklet(createSnapshotTasklet()).build())
.next(stepBuilderFactory.get("incremental_deploy_tasklet").tasklet(tasklet).build());
return jobBuilder.build();
}
Tasks:
packCompositionMap.put(incremental_content_deploy, Arrays.asList(
create_security_actions ,slice_content, upgrade_appmodule,
application_refresh,
install_reset_roles_bar, restore_reset_roles_bar,
populate_roles, add_roles,
replay_security_actions,
create_adw_connection,
apply_system_extensions,
replay_system_steps,
assign_service_admin
));
Here STOP signal for the id is sent when "incremental_deploy_tasklet" is just initiated and "create_security_actions" task is picked from the array list but the problem is it does not stops but completes all the item task in the array and then marks the status for this "incremental_deploy_tasklet" step as STOPPED and then overall status for this job is also marked as STOPPED.
What I am looking for is help on to STOP and interrupt at this "create_security_actions" task itself. Any help or input is appreciated, Thank you
After reading multiple docs and trying it- found that we cannot terminate the thread immediately. The control should come back to the framework.
The shutdown is not immediate, since there is no way to force an immediate shutdown, especially if the execution is currently in developer code that the framework has no control over, such as a business service. However, as soon as control is returned back to the framework, it will set the status of the current StepExecution to BatchStatus.STOPPED, save it, then do the same for the JobExecution before finishing.
Thank you.

Async feature in Servlets

I was just going back to Servlet-3.x features and exploring it. If I am not wrong, before Servlet-3.x it was thread per request model and it would run out of threads in the pool, for heavy incoming traffic.
So, with Servlet-3.x it says it is Asynchronous and doesn't keep the threads blocked , rather releases them immediately but just the task is delegated.
Here is my interpretation,
consider there are 2 threads in Server thread-pool
For a new Async Servlet request R1 there is a thread T1, this T1 would delegate the task to T2 and T1 responds back to client immediately.
Question: Is T2 created from Server thread-pool? If so, I don't get the point.
Case 1: If it was old Synchronous Servlet request T1 would have been busy running I/O task,
Case 2: If it was Asynchronous Servlet call T2 is busy running I/O task.
In both cases, one of them is busy.
I tried to check the same with a sample Async servlet in openliberty app server, below is the sample log captured from my sample demo Servlet.
Entering doGet() == thread name is = Default Executor-thread-116
Exiting doGet() == thread name is = Default Executor-thread-116
=== Long running task started ===
Thread executing #start of long running task = Default Executor-thread-54
Thread executing #end of long running task = Default Executor-thread-54
=== Long running task ended ===
As shown above, the Default Executor-thread-116 is released immediately and delegated long running task to the Default Executor-thread-54, but I am not sure if they are from the App Server thread pool. If so, why can't just Default Executor-thread-116 do the task instead of delegation?
Can someone throw some light on this async behavior of Servlets in JavaEE
In your example, where the work is synchronous and there's no separate executor/threadpool, there is nearly no point to use async servlets. Lots of samples/examples out there are just block on a 2nd thread because they're trying to illustrate just the syntax.
But there's no reason why you can't spin off a thread to do a little work, add your async context to some list, and then after some event (inbound JMS, websocket, whatever) provides the data needed to complete the async response. For example, a 2-player game server wouldn't wait for player 2 in a second thread, it would just have their async context floating around in memory waiting for a 2nd player to find it.

android: AsyncTask onPostExecute keep working even if start new activity on doInBackground

i am building an application for clients to get questions from server and answer it, if the server doesn't have questions i want to go to new screen and print message that try again in few minutes, getting questions is in AsyncTask , if the server doesn't have questions , it will sends in the header of the responds, a header isFindAQuestion with the value false, here is the code on client to ensure if false , i print on LogCat and i see the message = false, but my problems that even if i start new activity with the intent, this activity keep working and show me exception and it is null pointer exception because on the onPostExceute will take a parmeter null and try to process it, i put finish() in the end of false statement but doesn't finish the activity
if (response.getFirstHeader("isFindAQuestion").getValue()
.toString().equals("false")) {
Log.d("message", "false");
Bundle basket = new Bundle();
basket.putString("Message", "sorry no enought questions");
Intent goToAnswerQuestion = new Intent(AnswerQuestion.this,
FinishTime.class);
goToAnswerQuestion.putExtras(basket);
startActivity(goToAnswerQuestion);
finish();
}
Editis it because AsyncTask is working on thread so if the activity is finished, that thread will keep working? and if so how can i stop that thread?
doInBackground is not executed in the UI thread, but in a separeted thread:
invoked on the background thread immediately after onPreExecute()
finishes executing. This step is used to perform background
computation that can take a long time.
If you want to stop your background operation and perform some activities on the UI thread the better thing is to call cancel() and then perform all the stuff you want in the onCancelled callback wich is executed on the UI thread.
From the AsyncTask documentation:
A task can be cancelled at any time by invoking cancel(boolean).
Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns.
To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
protected void onCancelled (Result result)
Runs on the UI thread after cancel(boolean) is invoked and doInBackground(Object[]) has finished.
The default implementation simply invokes onCancelled() and ignores the result. If you write your own implementation, do not call super.onCancelled(result).

Resources