How to persist the state in the db as part of the entry to each state in spring state machine?

81 Views Asked by At

I have defined the SM like below in my codebase wherein OperationState Enum depicts the state for the StateMachine and the same property is part of the one of the hibernate Entity [OpInfo.java]

Basically I have below 2 questions:

1.As per my requirement, I need to persist the operation state to OpInfo table whenever there is a state change. Currently, I have configured the entry action i.e. updateOperationState() for each of the state but that looks very naive approach. I hope there must be some interface/functionality provided by StateMachine TPL which will do this persistence in much cleaner way or there should be some way to define common entryaction for each state.

2.During high concurrency, what I have observed is, the call to entryAction i.e. updateOperationState() is being missed by StateMachine. I would like to understand the root cause for the same and would like to know if there are any configuration exposed by SM to tune in regards of this problem.

    StateMachineBuilder.Builder<OperationState, OperationEvent> builder = StateMachineBuilder.builder();
                        builder.configureStates().withStates()
                                .states(EnumSet.allOf(OperationState.class))
                                .initial(genericOp.getOpState())
                                .state(CREATED, updateOperationState(), null)
                                .state(IDLE, updateOperationState(), null)
                                .state(APPLYING, updateOperationState(), null)
                                .state(APPLIED, updateOperationState(),null);
        
        builder.configureTransitions().withExternal()
                            .source(CREATED).target(APPLYING).event(TRIGGER)
                            .and().withExternal()
                            .source(CREATED).target(IDLE).event(SCHEDULE)
                            .and().withExternal()
                            .source(IDLE).target(APPLYING).event(TRIGGER)
                            .and().withExternal()
                            .source(APPLYING).target(APPLIED).event(SUCCESS)
                            .and().withExternal();
@Bean
private static Action<OperationState, OperationEvent> updateOperationState() {
    return context -> {
        OperationState tgtOpState = context.getStateMachine().getState().getId();
        if (context.getMessage() == null) {
            return;
        }
        opInfoRepository.updateOpState(opId, tgtOpState);
        log.debug("Updating the operation [{}] state to [{}]", opId, tgtOpState);

    };
}

@Entity
@Builder
public class OpInfo {
    @Id
    @ToString.Include
    private String id;
    
    @Enumerated(EnumType.STRING)
    @ToString.Include
    private OperationType operationType;
    
    @Enumerated(EnumType.STRING)
    OperationState operationState =  OperationState.CREATED;
}
1

There are 1 best solutions below

0
Dauren D On

Use can take advantage of the StateMachineListener interface and implement a callback on stateEntered or transitionEnded phase to save the current state of your state machine in database

See more here in Spring documentation