Description
Hi!
In this project https://github.com/matteo4diani/spring-statemachine-deployment-demo I am using Spring Statemachine 4.0.0 to model a simple sequential SM (the configuration class is attached below):
initial:READY --event:DEPLOY--> state:DEPLOYING --event:NAMESPACE_STATUS_CHANGE--> state:DEPLOYED --event:DELETE--> state:DELETING --event:NAMESPACE_STATUS_CHANGE--> end:DELETED
I am using the provided BootStateMachineMonitor
to monitor transitions, but I noticed that the external transition to the "end" state DELETED
is not shown in the traces. If instead the state is not marked as "end", the trace is present as expected.
Moreover I noticed that if I mark a state SF
as "end", and trigger a local transition TL
to SF
(from an intermediate state SI
) after an external transition TE
to the intermediate state SI
, the trace for the preceding external transition TE
is lost as well (if needed i can provide an example for this behavior but I didn't want to overcomplicate things).
Is this intended behavior? Am I doing anything wrong? By comparing my code to your samples and docs I cannot see any difference/mistake so I'm inclined to think this could be a bug.
Thanks for this project btw, we're enjoying it a lot! :)
Here is the actual response from localhost:8080/actuator/statemachinetrace
after the state machine has gone from READY
to DELETED
:
[
{
"timestamp": "2024-07-18T14:33:50.356+00:00",
"info": {
"duration": 1,
"machine": "machine-id",
"transition": "EXTERNAL_DEPLOYED_DELETING"
}
},
{
"timestamp": "2024-07-18T14:33:41.821+00:00",
"info": {
"duration": 1,
"machine": "machine-id",
"transition": "EXTERNAL_DEPLOYING_DEPLOYED"
}
},
{
"timestamp": "2024-07-18T14:33:21.879+00:00",
"info": {
"duration": 1,
"machine": "machine-id",
"transition": "EXTERNAL_READY_DEPLOYING"
}
},
{
"timestamp": "2024-07-18T14:33:21.877+00:00",
"info": {
"duration": 2,
"machine": "machine-id",
"transition": "INITIAL_READY"
}
}
]
I would expect to find a similar element in the traces array (which is present if DELETED
is not marked as an "end" state):
{
"timestamp": "2024-07-18T14:33:50.356+00:00",
"info": {
"duration": 1,
"machine": "machine-id",
"transition": "EXTERNAL_DELETING_DELETED"
}
}
This is the configuration class I'm using:
@Configuration
@EnableStateMachineFactory
@Slf4j
public class ApplicationStateMachineConfiguration
extends StateMachineConfigurerAdapter<ApplicationStates, ApplicationEvents> {
private final BootStateMachineMonitor<ApplicationStates, ApplicationEvents> stateMachineMonitor;
public ApplicationStateMachineConfiguration(
BootStateMachineMonitor<ApplicationStates, ApplicationEvents> stateMachineMonitor
) {
this.stateMachineMonitor = stateMachineMonitor;
}
@Bean
public CustomStateMachineService stateMachineService(
StateMachineFactory<ApplicationStates, ApplicationEvents> stateMachineFactory
) {
return new ApplicationStateMachineService(stateMachineFactory);
}
@Override
public void configure(
StateMachineConfigurationConfigurer<ApplicationStates, ApplicationEvents> config
) throws Exception {
config.withMonitoring().monitor(stateMachineMonitor);
config.withConfiguration().autoStartup(false);
}
@Override
public void configure(
StateMachineStateConfigurer<ApplicationStates, ApplicationEvents> states
) throws Exception {
states
.withStates()
.initial(ApplicationStates.READY)
.end(ApplicationStates.DELETED)
.states(
Set.of(
ApplicationStates.DEPLOYING,
ApplicationStates.DEPLOYED,
ApplicationStates.DELETING
)
);
}
@Override
public void configure(
StateMachineTransitionConfigurer<ApplicationStates, ApplicationEvents> transitions
) throws Exception {
transitions
.withExternal()
.source(ApplicationStates.READY)
.target(ApplicationStates.DEPLOYING)
.event(ApplicationEvents.DEPLOY)
.guard(context -> true)
.and()
.withExternal()
.source(ApplicationStates.DEPLOYING)
.target(ApplicationStates.DEPLOYED)
.event(ApplicationEvents.NAMESPACE_STATUS_CHANGE)
.guard(context -> true)
.and()
.withExternal()
.source(ApplicationStates.DEPLOYED)
.target(ApplicationStates.DELETING)
.event(ApplicationEvents.DELETE)
.and()
.withExternal()
.source(ApplicationStates.DELETING)
.target(ApplicationStates.DELETED)
.event(ApplicationEvents.NAMESPACE_STATUS_CHANGE)
.guard(context -> true);
}
}