Search code examples
javaspringstate-machinespring-statemachine

Recommended approach when restoring a Spring State Machine instance


I am planning to use Spring State Machine to control an execution workflow. The system is expected to receive requests from multiple users and each user may be assigned to multiple workflows. My initial idea was to have one instance of SM per workflow and every time an user perform a step in the workflow, I would use its identifier to restore the machine from a persistent storage, input the new event and store the updated SM.

I've read around that initialising a SM is an expensive operation and some people recommend having a single instance of it, but "rehydrate" that instance with some data. My understanding is that this would be more effective, but I think it would become a "blocking" operation, in other words, one workflow would need to wait for the previous one to be finished/released before-hand. Since I'm newbie on this topic, can anyone shed some light on the best alternatives for my use case and perhaps pieces of code to illustrate the differences? (PS: I'm using v2.4.0)


Solution

  • I was first implementing the "rehydrate" mechanism because as you said, it made sense and was also used in the "persist" example of spring-statemachine.

    Howewer, running performance tests against my API showed that using a single instance fails when using the StateMachine as an @Autowired Bean with the prototype scope as it is described in that example. What happens is that simultaneous requests against my API override that Statemachine Bean and the first request fails as the statemachine changes when writing back to the DB (i used redis).

    So now I actually build a fresh statemachine everytime a request comes in and rehydrate that object:

        public String getStatesGuest(HttpServletRequest httpServletRequest) throws Exception {
            StateMachine<States, Events> stateMachine = stateMachineConfig.stateMachine();
            resetStateMachineFromStore(httpServletRequest.getSession().getId(), stateMachine);
            return convertToJson(buildGetStateResponse(stateMachine));
        }
    

    It still is very performant, I was testing with around 30 reqs/s and still got a median of 12ms. (Docker with 2 Cores for spring boot, 1 Core for redis).