You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am having an issue restoring a persisted state machine with >=4 levels of submachine hierarchy to its correct state.
I believe the issue is with the AbstractStateMachine.resetStateMachineReactively method, but I am not quite sure.
I first found this issue working on a project which was using v3.3 of the spring-statemachine project. So I upgraded to v4.0, but the issue is still present
I have created 3 simple JUnit test cases to demonstrate the problem.
Test Case 1) 4 Levels of Hierarchy, with persist and restore between each event (FAILS)
The first test case is with 4 levels of submachine hierarchy (machines M1, M2, M3, and M4). When we arrive at submachine M4, I noticed that persisting and restoring always brings us back to the first state of M4, not the correct state of M4
java.lang.AssertionError: State machine state chain before and after persist/restore should be the same, but was not.
Before persist: [M1_M2, M2_M3, M3_M4, M4_MIDDLE]
After restore: [M1_M2, M2_M3, M3_M4, M4_START]
at com.example.demo.HierarchicalSM4LevelsPersistRestoreTest.whenStateMachineHas4LevelsOfHierarchy_shouldPersistAndRestoreToSameState(HierarchicalSM4LevelsPersistRestoreTest.java:118)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Test Case 2) 4 Levels of Hierarchy, with no persist and restore between each event (PASSES)
This test case is to rule out the idea that the state machine is mis-configured from the start. When we do not persist and restore the machine between each event, it moves to all the states that we expect.
The helper class has a few inner classes used throughout the test cases:
InMemoryStateMachinePersist -- Simple in-memory persistence to mock storing StateMachineContext to a database
StateMachineStateEntryCollectorListener -- to collect the list of states that the state machine has entered
packagecom.example.demo;
importorg.springframework.statemachine.StateContext;
importorg.springframework.statemachine.StateMachine;
importorg.springframework.statemachine.StateMachineContext;
importorg.springframework.statemachine.StateMachinePersist;
importorg.springframework.statemachine.listener.StateMachineListenerAdapter;
importjava.util.*;
publicclassHierarchicalStateMachineTestHelper {
/** * Helper method to get the current state chain of a state machine; * The "state chain" is the list of state IDs, one for each machine and submachine, * from the top-level state machine to the bottom-level submachine whose state we are in * @param stateMachine the state machine * @return the current state chain * @param <S> the state type */publicstatic <S> List<S> getCurrentStateChain(StateMachine<S, ?> stateMachine) {
varcurrentState = stateMachine.getState();
if (currentState == null) {
returnnull;
}
returncurrentState.getIds() == null ? null : currentState.getIds().stream().toList();
}
/** * In-memory implementation of state machine persistence * @param <S> the state type * @param <E> the event type * @param <T> the context object type */publicstaticclassInMemoryStateMachinePersist<S, E, T> implementsStateMachinePersist<S, E, T> {
/** * The map of context objects to state machine contexts */privatefinalMap<T, StateMachineContext<S, E>> contexts = newHashMap<>();
@Overridepublicvoidwrite(StateMachineContext<S, E> context, TcontextObj) {
contexts.put(contextObj, context);
}
@OverridepublicStateMachineContext<S, E> read(TcontextObj) {
returncontexts.get(contextObj);
}
}
/** * State machine listener that collects the states entered * @param <S> the state type * @param <E> the event type */publicstaticclassStateMachineStateEntryCollectorListener<S, E> extendsStateMachineListenerAdapter<S, E> {
privatefinalList<List<S>> statesEnteredStateChains = newArrayList<>();
/** * Get the state chains of states entered * @return the state chains of states entered */publicList<List<S>> getStatesEnteredStateChains() {
returnstatesEnteredStateChains;
}
@OverridepublicvoidstateContext(StateContext<S, E> stateContext) {
if (Objects.equals(stateContext.getStage(), StateContext.Stage.STATE_ENTRY)) {
statesEnteredStateChains.add(stateContext.getStateMachine().getState().getIds().stream().toList());
}
}
}
}
Please let me know if you notice anything about the way I configured the state machines and submachines or the way I am persisting and restoring that is not correct
The text was updated successfully, but these errors were encountered:
I am having an issue restoring a persisted state machine with >=4 levels of submachine hierarchy to its correct state.
I believe the issue is with the
AbstractStateMachine.resetStateMachineReactively
method, but I am not quite sure.I first found this issue working on a project which was using v3.3 of the spring-statemachine project. So I upgraded to v4.0, but the issue is still present
I have created 3 simple JUnit test cases to demonstrate the problem.
Test Case 1) 4 Levels of Hierarchy, with persist and restore between each event (FAILS)
The first test case is with 4 levels of submachine hierarchy (machines M1, M2, M3, and M4). When we arrive at submachine M4, I noticed that persisting and restoring always brings us back to the first state of M4, not the correct state of M4
Below is the failure:
Test Case 2) 4 Levels of Hierarchy, with no persist and restore between each event (PASSES)
This test case is to rule out the idea that the state machine is mis-configured from the start. When we do not persist and restore the machine between each event, it moves to all the states that we expect.
Test Case 3) 3 Levels of Hierarchy, with persist and restore between each event (PASSES)
When there are only three levels of submachine hierarchy, the issue with restoring to the correct state does not appear.
Helper Class used throughout test cases
The helper class has a few inner classes used throughout the test cases:
InMemoryStateMachinePersist
-- Simple in-memory persistence to mock storingStateMachineContext
to a databaseStateMachineStateEntryCollectorListener
-- to collect the list of states that the state machine has enteredPlease let me know if you notice anything about the way I configured the state machines and submachines or the way I am persisting and restoring that is not correct
The text was updated successfully, but these errors were encountered: