spring-statemachine icon indicating copy to clipboard operation
spring-statemachine copied to clipboard

Issue with Serialization When Using stateMachineRedisPersistence.persist Method

Open DoNotBugPlz opened this issue 1 year ago • 4 comments

Springboot:3.2.2 JDK: 17 stateMachine: 4.0 stateMachine-redis: 4.0

When I call the stateMachineRedisPersistence.persist method, I encounter an error. The complete error message is: register this class use: kryo.register(org.springframework.statemachine.support.DefaultStateMachineContext.class); java.lang.IllegalArgumentException: Class is not registered: org.springframework.statemachine.support.DefaultStateMachineContext Note: To register this class use: kryo.register(org.springframework.statemachine.support.DefaultStateMachineContext.class); at com.esotericsoftware.kryo.Kryo.getRegistration(Kryo.java:579) at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:627) at org.springframework.statemachine.data.redis.RedisStateMachineContextRepository.serialize(RedisStateMachineContextRepository.java:94) at org.springframework.statemachine.data.redis.RedisStateMachineContextRepository.save(RedisStateMachineContextRepository.java:73) at org.springframework.statemachine.persist.RepositoryStateMachinePersist.write(RepositoryStateMachinePersist.java:46) at org.springframework.statemachine.persist.RepositoryStateMachinePersist.write(RepositoryStateMachinePersist.java:31) at org.springframework.statemachine.persist.AbstractStateMachinePersister.persist(AbstractStateMachinePersister.java:63) at [my service layer classes] I have already configured the serialization method for redisTemplate in my config, intending to use Jackson for serializing my state machine, but it seems not to take effect. Digging into the source code, I found that the actual implementation used within RepositoryStateMachinePersist is RedisStateMachineContextRepository, which uses a different serialization method than what I configured.

What should I do next to resolve this error? How can I ensure my configuration for Jackson serialization is applied correctly?

DoNotBugPlz avatar Apr 23 '24 08:04 DoNotBugPlz

I encountered the same problem, buddy, have you solved it?

lizqlife avatar Aug 20 '24 09:08 lizqlife

I encountered the same problem, buddy, have you solved it?

I solved it, though it seems like a rather ugly solution to me. I injected a Kryo class into the Spring container. Here's roughly how it was done: in a class annotated with @Configuration, I used the @Bean annotation to inject: Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false); kryo.register(DefaultStateMachineContext.class); return kryo; This worked, but I found that when storing in Redis, it was in binary. I didn't have a particularly good method for this. In a similar project where I was writing an order service, I switched to the cola-component-statemachine, which is a bit more lightweight. If your project isn't complete yet, you might want to try this component."

DoNotBugPlz avatar Aug 21 '24 01:08 DoNotBugPlz

I encountered the same problem, buddy, have you solved it?

I solved it, though it seems like a rather ugly solution to me. I injected a Kryo class into the Spring container. Here's roughly how it was done: in a class annotated with @configuration, I used the @bean annotation to inject: Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false); kryo.register(DefaultStateMachineContext.class); return kryo; This worked, but I found that when storing in Redis, it was in binary. I didn't have a particularly good method for this. In a similar project where I was writing an order service, I switched to the cola-component-statemachine, which is a bit more lightweight. If your project isn't complete yet, you might want to try this component."

Thank you bro, I will try the two solutions you provided, which is very helpful to me. Thanks again and best wishes.

lizqlife avatar Aug 21 '24 01:08 lizqlife

I found in AbstractKryoStateMachineSerialisationService that a Kryo object is created for the object created by KryoPoolQueueImpl (method borrow()) through a new KryoFactory. Therefore, I plan to override the configureKryoInstance method in KryoStateMachineserialisationService to obtain the Kryo object. This is my solution Step One

public class FsmKryoStateMachineSerialisationService<S, E> extends KryoStateMachineSerialisationService<S, E> {
    @Override
    protected void configureKryoInstance(Kryo kryo) {
        kryo.addDefaultSerializer(StateMachineContext.class, new StateMachineContextSerializer<S, E>());
        kryo.addDefaultSerializer(MessageHeaders.class, new MessageHeadersSerializer());
        kryo.addDefaultSerializer(UUID.class, new UUIDSerializer());
        // register DefaultStateMachineContext
        kryo.setRegistrationRequired(false);
        kryo.register(DefaultStateMachineContext.class);
    }
}

Step 2

    @Bean
    public StateMachinePersister<?, ?, String> persister(StateMachineRuntimePersister<?, ?, String> runtimePersister) {
        return new DefaultStateMachinePersister<>(runtimePersister);
    }

    @Bean
    public StateMachineRuntimePersister<?, ?, String> runtimePersister(JpaStateMachineRepository repository) {
        FsmKryoStateMachineSerialisationService<?, ?> service = new FsmKryoStateMachineSerialisationService<>();
        JpaRepositoryStateMachinePersist<?, ?> persist = new JpaRepositoryStateMachinePersist<>(repository, service);
        return new JpaPersistingStateMachineInterceptor<>(persist);
    }

vpen66 avatar Aug 26 '25 03:08 vpen66