spring-projects / spring-statemachine

Spring Statemachine is a framework for application developers to use state machine concepts with Spring.
1.56k stars 613 forks source link

StateMachine Actions do not maintain order when configuration is read from Database #1072

Open AdityaZagadeCT opened 2 years ago

AdityaZagadeCT commented 2 years ago

StateMachineFactory is configured to read configuration from db repository. However then statemachine is created, the order of actions is not maintained. I suspect, its because the results returned from DB are not in any particular order.

@Configuration
@EnableStateMachineFactory
public class Config extends StateMachineConfigurerAdapter<String, String> {
    @Autowired
    private StateRepository<? extends RepositoryState> stateRepository;

    @Autowired
    private TransitionRepository<? extends RepositoryTransition> transitionRepository;

    @Override
    public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
        model
                .withModel()
                .factory(modelFactory());
    }

    @Bean
    public StateMachineModelFactory<String, String> modelFactory() {
        return new RepositoryStateMachineModelFactory(stateRepository, transitionRepository);
    }

    @Bean
    public Action<String, String> action1() {
        return context -> System.out.println("Action1");
    }

    @Bean
    public Action<String, String> action2() {
        return context -> System.out.println("Action2");
    }
}

@SpringBootApplication
public class Main implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    @Autowired
    StateMachineFactory<String, String> factory;

    @Autowired
    ApplicationContext context;

    @Override
    public void run(String... args) throws Exception {
        JpaStateRepository stateRepository = context.getBean(JpaStateRepository.class);
        JpaTransitionRepository transitionRepository = context.getBean(JpaTransitionRepository.class);
        JpaActionRepository actionRepository = context.getBean(JpaActionRepository.class);

        JpaRepositoryState stateS1 = new JpaRepositoryState("UNPAID", true);
        JpaRepositoryState stateS2 = new JpaRepositoryState("DONE");

        stateRepository.save(stateS1);
        stateRepository.save(stateS2);

        var action1 = new JpaRepositoryAction();
        action1.setName("action1");

        var action2 = new JpaRepositoryAction();
        action2.setName("action2");

        actionRepository.save(action1);
        actionRepository.save(action2);

        JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(null, stateS1, stateS1, "TEST", Set.of(action1, action2));
        transitionRepository.save(transitionS1ToS2);

        for (int i = 0; i < 10; i++) {
            var sm = factory.getStateMachine();
            sm.startReactively().block();
            sm.sendEvent("TEST");
        }
    }
}

Logs 2022-10-12 22:01:19.474 INFO 58704 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 7082 (http) with context path '' 2022-10-12 22:01:19.482 INFO 58704 --- [ restartedMain] com.poc.Main : Started Main in 2.076 seconds (JVM running for 2.288) Action2 Action1 Action1 Action2 Action1 Action2 Action2 Action1 Action2 Action1 Action1 Action2 Action1 Action2 Action2 Action1 Action2 Action1 Action1 Action2

AdityaZagadeCT commented 2 years ago

Is there any way to maintain order in which actions will be executed, when an event is sent to statemachine?

When configuration is stored in DB.

When configuration is written directly in code, order is maintained.