spring-projects / spring-statemachine

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

Statemachine is not restoring from the database #1053

Closed AbstractAlao closed 2 years ago

AbstractAlao commented 2 years ago

I have been following the data persist examples outlined in the spring docs

When I acquire a state machine it doesn't pull it from the database but checks in memory. I see records being written but I just can't restore them. Here is my configuration I have defined the StateRepository and the

private final StateRepository<? extends RepositoryState> stateRepository;
private final TransitionRepository<? extends RepositoryTransition> transitionRepository;
private final StateMachineRuntimePersister<LoanEventStatus, LoanEventAction, String> stateMachineRuntimePersister;

Here are my beans based on the examples

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

@Bean
public DefaultStateMachineService<LoanEventStatus, LoanEventAction> stateMachineService(
        final StateMachineFactory<LoanEventStatus, LoanEventAction> stateMachineFactory,
        final StateMachinePersist<LoanEventStatus, LoanEventAction, String> stateMachinePersist) {

    return new DefaultStateMachineService<LoanEventStatus, LoanEventAction>(stateMachineFactory, stateMachinePersist);
}

@Bean
public StateMachinePersister<LoanEventStatus, LoanEventAction, String> persister(
        StateMachinePersist<LoanEventStatus, LoanEventAction, String> defaultPersist) {
    return new DefaultStateMachinePersister<>(defaultPersist);
}

I thought restoring them would be as simple as the following:

private final DefaultStateMachineService<LoanEventStatus, LoanEventAction> stateMachineService;
var stateMachine = stateMachineService.acquireStateMachine(id);

However, when I step into the DefaultStateMachineService I notice that the machines are empty. Shouldn't it restore when the application starts up or am i missing something

AbstractAlao commented 2 years ago

So I realized the problem was with this method.

stateMachineService.hasStateMachine(id)

Looking at the code it only looks for state machines that are in memory. To get the ones that are in the database you need to use JpaRepositoryStateMachine

StateMachineRepository<JpaRepositoryStateMachine> stateMachineRepository;

Then do the following to test if there is a state machine in the database and create one if there is not.

Optional<JpaRepositoryStateMachine> instance = stateMachineRepository.findById(Integer.toString(loanDetailId));

if (instance.isPresent()){

StateMachine<State, Event> stateMachine = stateMachineService.acquireStateMachine(id);
Oleksandr82 commented 2 years ago

It is possible to restore a state machine from a DB. Have you configured the persistence fully (especially, specified the runtime persister in the configuration class of your state machine)? Something like this:

// Persistence specific beans:

@Configuration
public class MachinePersist {

    @Bean
    public StateMachineRuntimePersister<DomainState, DomainEvent, String> stateMachineRuntimePersister(
            JpaStateMachineRepository jpaStateMachineRepository) {
        return new JpaPersistingStateMachineInterceptor<>(jpaStateMachineRepository);
    }
 
    @Bean
    public StateMachineService<DomainState, DomainEvent> stateMachineService(
            StateMachineFactory<DomainState, DomainEvent> stateMachineFactory,
            StateMachineRuntimePersister<DomainState, DomainEvent, String> stateMachineRuntimePersister) {
        return new DefaultStateMachineService<>(stateMachineFactory, stateMachineRuntimePersister);
    }
}

Additional configuration in your state machine configuration class:

// 1. Autowire the runtime persister
private final StateMachineRuntimePersister<DomainState, DomainEvent, String> stateMachineRuntimePersister;

// 2. Add a runtime persisted to the configure method
@Override
public void configure(StateMachineConfigurationConfigurer<DomainState, DomainEvent> config) throws Exception {
    config.withConfiguration().autoStartup(true);
    // here it is:
    config.withPersistence().runtimePersister(stateMachineRuntimePersister);
}

Technically, it should be enough to make acquireStateMachine(id) working.

AbstractAlao commented 2 years ago

So I realized the problem was with this method.

stateMachineService.hasStateMachine(id)

I was using this to check if I had a state machine that already existed. I was assuming it was querying the database.

I ended up doing the following:

StateMachineRepository<JpaRepositoryStateMachine> stateMachineRepository;

Optional<JpaRepositoryStateMachine> instance = stateMachineRepository.findById(Integer.toString(loanDetailId));

if (instance.isPresent()){

StateMachine<State, Event> stateMachine = stateMachineService.acquireStateMachine(id);