spring-projects / spring-statemachine

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

Mongo Persistence not persisting UUID correctly #963

Open nhmarujo opened 3 years ago

nhmarujo commented 3 years ago

We have been trying to use the StateMachine with Mongo persistence, but so far is not working properly for us.

We setup the machineId as follow:

@Override
public void configure(StateMachineConfigurationConfigurer<AuthenticationStates, AuthenticationEvents> config) throws Exception {
    config.withConfiguration()
            .machineId("authentication-flow")
            .autoStartup(true)
            .withPersistence()
            .runtimePersister(stateMachineRuntimePersister);
}

But when the record is being persisted we noticed that the id is the same as the machineId (both show as authentication-flow), when in fact it should be the uuid generated for that instance. After a lot of debug we believe that there might be a bug in this line and it should pass (T)stateMachine.getUuid() instead of (T)stateMachine.getId().

This effectively means that all documents inserted have id=machineId=authentication-flow, so we end up with a single document being overriden by all instances.

kkochanski commented 2 years ago

Encountered the same. To fix that I've implemented custom MongoDbRepositoryStateMachinePersist. Example:

public class MongoDbRepositoryStateMachinePersist <S, E> extends RepositoryStateMachinePersist<Entity, S, E> {

    private final EntityRepository entityRepository;

    /**
     * Instantiates a new mongodb repository state machine persist.
     *
     * @param EntityRepository the mongodb state machine repository
     */
    public MongoDbRepositoryStateMachinePersist(EntityRepository entityRepository) {
        super();
        this.entityRepository = entityRepository;
    }

    /**
     * Instantiates a new mongodb repository state machine persist.
     *
     * @param EntityRepository the mongodb state machine repository
     * @param serialisationService          the serialisation service
     */
    public MongoDbRepositoryStateMachinePersist(EntityRepository entityRepository,
                                                StateMachineSerialisationService<S, E> serialisationService) {
        super(serialisationService);
        this.entityRepository = entityRepository;
    }

    @Override
    protected StateMachineRepository<Entity> getRepository() {
        return entityRepository;
    }

    @Override
    protected Entity build(StateMachineContext<S, E> context, Object contextObj, byte[] serialisedContext) {
        var id = contextObj.toString();
        var state = context.getState().toString();

        var detonation = entityRepository.findById(id).orElse(null);
        if (detonation == null) {
            return new Entity(
                contextObj.toString(),
                state,
                serialisedContext,
                List.of(new StateChange(state, Instant.now())),
                Instant.now(),
                Instant.now().plus(90, ChronoUnit.DAYS)
            );
        } else {
            return detonation
                .setState(state)
                .setStateMachineContext(serialisedContext)
                .addStateHistory(new StateChange(state, Instant.now()))
            ;

        }
    }
}