Parallel execution in spring state machine - spring-statemachine

I'm trying to construct a state machine from thee follwoing UML model using Papyrus.
The entryActions for each of the stage is resgistered using DefaultStateMachineComponentResolver , to resolve to respective EntryAction classes in my spring app.
My requirement is
1) From the CS stage the execution should fork to two threads, upon getting the triggering event SUCCESS.
2) In one thread DE1 and TE1 should execute sequentially and in the other thread DE2 and TE2 should execute sequentially
3) Transition to END state should happen only if both the threads execute successfully,
ie from TE1 a transition should happen to join state signalled by event SUCCESS and from TE2 a transition should happen to join state signalled by event SUCCESS
4) ie. Transition to END state should happen after the successful execution of the 2 threads.
5) While executing each stage if any tasks fails (tasks are written in EntryAction classes),
the state machine should navigate to END state , the signals used being FAILURE, TERMINATED (based on the severity of error occured)
Here is the code I used to buildState machine and trigger execution
Builder<String, String> builder = StateMachineBuilder
.<String, String> builder();
builder.configureConfiguration()
.withConfiguration()
.autoStartup(false)
.listener(listener())
.beanFactory(
this.applicationContext.getAutowireCapableBeanFactory());//.taskExecutor(taskExecutor());
DefaultStateMachineComponentResolver<String, String> resolver = new DefaultStateMachineComponentResolver<>();
resolver.registerAction("startEntryAction", this.startEntryAction);
resolver.registerAction("apEntryAction", this.apEntryAction);
resolver.registerAction("psEntryAction", this.psEntryAction);
//all entry action classed are registered
...
...
UmlStateMachineModelFactory umlStateMachineModelFactory = new UmlStateMachineModelFactory("classpath:model.uml");
umlStateMachineModelFactory.setStateMachineComponentResolver(resolver);
builder.configureModel().withModel().factory(umlStateMachineModelFactory);
StateMachine<String, String> stateMachine = builder.build();
stateMachine.start()
Issues I faced
1) While using taskExecutor the state machine execution was not getting started.
2)After commenting out the taskExecutor the exectuion was triggered and in the console I got the logs from entryAction classes.
3)In each entry action classes I just added the below code to transit to next state, and for logging purpose
#Override
public void execute(StateContext<String, String> paramStateContext) {
LOGGER.debug("Start State entered! ");
paramStateContext.getStateMachine().sendEvent("SUCCESS");
}
4) But the problem is the state TE1 was never entered, after analysing the logs.
My requirement was END state should be entered after executing the tasks in TE1EntryAction and TE2EntryAction
Please find below the logs
[![enter image description here][1]][1]19:03:54.963 DEBUG o.i.r.p.a.StartEntryAction - Start State entered!
19:03:55.007 DEBUG o.i.r.p.a.APEntryAction - AP State entered!
19:03:55.007 DEBUG o.i.r.p.a.PSEntryAction - PS State entered!
19:03:55.007 DEBUG o.i.r.p.a.PBEntryAction - PB State entered!
19:03:55.007 DEBUG o.i.r.p.a.CSEntryAction - CS State entered!
19:03:55.007 DEBUG o.i.r.p.a.DE1EntryAction - DE1 State entered!
19:03:55.007 DEBUG o.i.r.p.a.DE2EntryAction - DE2 State entered!
19:03:55.007 DEBUG o.i.r.p.a.TE2EntryAction - TE2 State entered!
19:03:55.023 DEBUG o.i.r.p.a.EndStateEntryAction - END State entered!
Is the problem exists in the UML model I created.
if so, how should be the state Diagram look like
Thanks a ton for the help.

At least your fork/join is wrong as you can't have those without using orthogonal regions.
Source for above is in simple-forkjoin.uml
I'm not sure why papyrus allows you to draw fork/join like that as uml spec clearly states:
Fork Pseudostates serve to split an incoming Transition into two or more Transitions terminating on
Vertices in orthogonal Regions of a composite State.
I should probably add model verifier if user is trying to add forks/joins without using regions.
Also I'm not sure what should happen with transition from TE2 to END if machine is waiting join to happen so I'd try to avoid that.

Related

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.

How to use spring state machine with nested state machine

Good Day,
I just started learning spring state machine.
I have the following questions
I will like to know how to configure a state machine that uses a nested state machine.
How can this be done programmatically i.e. via state machine builder?
How can this be done via papyrus UML?
My second question is on how to fire events i.e. upon getting to the state that has the nested state machine. How can events be a trigger in the nested state machine?
My third question is how to exit a nested state machine by firing an event that moves from the parent state (i.e. the state that references the nested state machine)
to another state in the parent state machine.
I would really appreciate a reference to some examples.
After studying the javadoc and reading a few links
https://github.com/spring-projects/spring-statemachine/issues/121
I figured it out.
Programmatically
Configure the state and transitions for the parent state machine as usual
https://www.baeldung.com/spring-state-machine
Follow that link to see how.
For States that reference a nested state machine. See the below snippet
....
enter code here
*builder.configureStates()
.withStates()
.initial("contactList2")
.state("newContactSM", newContactSM())
.end("end1");*
....
The state "newContactSM" references a nested state machine. The nested state machine
is define
....
*
public StateMachine<String, String> newContactSM() throws Exception
{
logger.info(" ------ newContactSM() -------- ");
// checkCurrentFlow();
Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureConfiguration().withConfiguration().machineId("newContactBTF");
logger.info(" configure states ..");
builder.configureStates()
.withStates()
.initial("newContact")
.end("end2")
.states(new HashSet<String>(Arrays.asList("otherContact"))); // (Arrays.asList("S1", "S2", "S3")));
logger.info(" states configured ! ");
........ //
}
enter code here
....
To do it via UML
Just ensure that you reference the nested state machine in the state "newContactSM".
Once the set up is done. You can fire events as normal. spring state machine handles the rest.

Shutting down Threads Groups while finishing all the requests inside the transaction controller

I have a JMeter Test Plan to perform performance and load tests on a SAP Web Client. The Test plan contains 5 Thread Groups.
In each Thread Groups i have a Transaction Controller that contains N Requests where they execute Login - Process - Logoff .
I need to be able to run the Test Plan with 20 users on infinite loop and then shutdown after 1 hour BUT perform the logoff of all the users that are still on the Web Client (Last request on each transaction controller).
As of this moment the shutdown simply stops the test on the active thread, whatever it is, without finishing the Transaction Controller.
Any ideas?
This could be achieved by adding a While controller as a parent controller to the Transaction controllers. While controller should check the duration and exit when the duration is more than one hour.
Outside the While controlled add a Test Action Sampler and configure it to stop the current Thread.
Sample test plan with a solution is available # GitHub
You can introduce a JVM Shutdown Hook to intercept JMeter shutdown and be able to finish the running tasks.
Example implementation:
Add setUp Thread Group to your Test Plan
Add JSR223 Sampler to the setUp Thread Group
Put the following code into "Script" area:
def myHook = new Thread({ ->
try {
println('' + new Date() + '\tShutdown detected, waiting for 5 for logging out')
Thread.sleep(5000)
println('' + new Date() + '\tExiting...')
} catch (InterruptedException ignored) {
}
})
Runtime.getRuntime().addShutdownHook(myHook)
That's it, the hook will be called when you send termination signal to JMeter and there you can put whatever logic you need:

How to retry a failed action?

I went through Spring Statemachine documentation but did not find clear answers for some scenarios. I will greatly appreciate if some one can clarify my questions.
Scenario1: How to retry errors related to action failures? Lets say I have the following states S1, S2 and S3 and when we transition from S1 to S2 I want to perform action A2. If action A2 fails I want to retry it with some time intervals. Is that possible using Spring StateMachine?
Consider AWS state machine Step Functions for example. All work in the step functions States are done using Task. And Task can be configured for retry.
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action());
Scenario 2: Lets say Statemachine has states S1, S2 and S3. The current state is S2. If the server goes down on startup will the Statemachine execution pick up from where it left off or we will have to do it all over again?
Scenario 3: When a Guard returns false (possibly because of error condition) and prevents a transition what happens next?
How to retry a failed action?
There are two types of actions in Spring State Machine - transition actions and state actions. In scenario 1 you're talking about transition action.
When you specify a transition action, you can also specify an error handler if the action fails. This is clearly documented in the spring state machine documentation.
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action(), errorAction());
In your errorAction() method you can implement your logic.
Possible options are:
transition to an earlier state and go the same path
transition to a specific state (e.g. retry state) where you can have your retry logic (e.g. Task/Executor that retries the action N times, and transition to other states (e.g. action success => go normal flow; action failed after N retries => transition to a failure terminal state)
There's also the official Tasks example, that demonstrates recovery/retry logic (source code).

Spring #Async cancel and start?

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();

Resources