Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel submachines break exitActions #1167

Open
wlfbck opened this issue Sep 30, 2024 · 1 comment
Open

Parallel submachines break exitActions #1167

wlfbck opened this issue Sep 30, 2024 · 1 comment
Labels
status/need-triage Team needs to triage and take a first look

Comments

@wlfbck
Copy link

wlfbck commented Sep 30, 2024

If you attach more then one submachine to a state using .parent("XYZ") it will break the exit actions of these submachines. See example below. This bug is hinted at by #969 but does not include the correct reason why.

Unfortunately i don't have any fancy graphical tool, so you get some excalidraw from me.

This one breaks the state machine:
grafik

This one is fine:
grafik

public class Testy {
    private static final Logger logger = LoggerFactory.getLogger(Testy.class);

    public static void main(String[] args) throws Exception {
        StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();
        builder.configureConfiguration().withConfiguration()
                .regionExecutionPolicy(RegionExecutionPolicy.SEQUENTIAL)
                .transitionConflictPolicy(TransitionConflictPolicy.PARENT)
                .listener(new StateMachineListenerAdapter<>() {
                    @Override
                    public void stateChanged(org.springframework.statemachine.state.State<String, String> from, org.springframework.statemachine.state.State<String, String> to) {
                        logger.info("from \n{} \nto \n{}", from, to);
                    }

                    @Override
                    public void eventNotAccepted(Message<String> event) {
                        //logger.info("event not accepted: {}", event);
                    }

                    @Override
                    public void transition(Transition<String, String> transition) {
                        logger.info("transition: {}", transition);
                    }
                })
                .machineId("GD24-machine");

        builder.configureStates().withStates()
                .initial("order-canExist")
                .state("order-Exists", ctx -> logger.info("hello order"), ctx -> logger.info("goodbye order"))
                .and().withStates()
                .parent("order-Exists")
                .initial("ncprogram-canExist")
                .state("ncprogram-Exists", ctx -> logger.info("hello ncprog"), ctx -> logger.info("goodbye ncprog"))
                .and().withStates()
                // change to "ncprogram-Exists" and it will magically work
                .parent("order-Exists")
                .initial("madeup-canExist")
                .state("madeup-Exists", ctx -> logger.info("hello madeup"), ctx -> logger.info("goodbye madeup"));

        builder.configureTransitions()
                .withExternal().source("order-canExist").target("order-Exists")
                .event("order-start")
                .and().withExternal().source("order-Exists").target("order-canExist")
                .event("order-end")
                .and().withExternal().source("ncprogram-canExist").target("ncprogram-Exists")
                .event("ncprogram-start")
                .and().withExternal().source("madeup-canExist").target("madeup-Exists")
                .event("madeup-start");

        StateMachine<String, String> stateMachine = builder.build();

        stateMachine.startReactively().subscribe();

        logger.info("sending ncprog start");
        Message<String> message1 = MessageBuilder.withPayload("ncprogram-start").build();
        stateMachine.sendEvent(Mono.just(message1)).subscribe();
        logger.info("sending order start");
        Message<String> message2 = MessageBuilder.withPayload("order-start").build();
        stateMachine.sendEvent(Mono.just(message2)).subscribe();
        logger.info("sending ncprog start");
        Message<String> message3 = MessageBuilder.withPayload("ncprogram-start").build();
        stateMachine.sendEvent(Mono.just(message3)).subscribe();
        logger.info("sending madeup start");
        Message<String> message4 = MessageBuilder.withPayload("madeup-start").build();
        stateMachine.sendEvent(Mono.just(message4)).subscribe();
        logger.info("sending order end");
        Message<String> message5 = MessageBuilder.withPayload("order-end").build();
        stateMachine.sendEvent(Mono.just(message5)).subscribe();
    }
}

Expected output from first example would be:

15:40:03,713 INFO  com.plansee.edge.flink.Testy                                 [] - goodbye madeup
15:40:03,720 INFO  com.plansee.edge.flink.Testy                                 [] - goodbye ncprog
15:40:03,720 INFO  com.plansee.edge.flink.Testy                                 [] - goodbye order

Instead, only this comes:

15:40:35,452 INFO  com.plansee.edge.flink.Testy                                 [] - goodbye order

I assume this is happening because the variant with 2 submachines is triggering a RegionState to ObjectState transition, while with 1 submachine it is triggering a StateMachineState to ObjectState

@github-actions github-actions bot added the status/need-triage Team needs to triage and take a first look label Sep 30, 2024
@wlfbck
Copy link
Author

wlfbck commented Sep 30, 2024

Looking at the debug output some more, it seems that while building the statemachine using the same parent twice turns the submachines into regions. That seems wrong to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status/need-triage Team needs to triage and take a first look
Projects
None yet
Development

No branches or pull requests

1 participant