spring-statemachine
spring-statemachine copied to clipboard
Unable to restore statemachine because of sterilization issues from com.esotericsoftware.kryo.KryoException
When restoring a state machine from the database, I am getting multiple errors from some of my objects
I added objects to the state machine like so:
stateMachine.getExtendedState().getVariables().put(PARAM_PROCESSOR, data);
When restoring, i get all different types of errors:
com.esotericsoftware.kryo.KryoException: Encountered unregistered class ID: 107
--
Serialization trace:
contributions (org.employmentclient.model.Employment)
employments (org.engine.client.model.ParticipantEmployment)
participantEmployment (org.yretirement.ycap.engine.client.model.LoanRequestContext)
at com.esotericsoftware.kryo.util.DefaultClassResolver.readClass(DefaultClassResolver.java:137) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readClass(Kryo.java:693) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:118) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:134) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:40) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:125) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:125) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:161) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:39) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na]
at org.springframework.statemachine.kryo.StateMachineContextSerializer.read(StateMachineContextSerializer.java:66) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
o.s.s.s.DefaultStateMachineService : Error handling context
--
com.esotericsoftware.kryo.KryoException: java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): -62
Serialization trace:
basicPaymentDate (org.employmentclient.model.Employment)
employments (org.ycap.engine.client.model.ParticipantEmployment)
participantEmployment (org.ycap.engine.client.model.LoanRequestContext)
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:144) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:134) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:40) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:125) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:125) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:161) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:39) ~[kryo-shaded-4.0.2.jar:na]
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na]
at org.springframework.statemachine.kryo.StateMachineContextSerializer.read(StateMachineContextSerializer.java:66) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
at org.springframework.statemachine.kryo.StateMachineContextSerializer.read(StateMachineContextSerializer.java:39) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:709) ~[kryo-shaded-4.0.2.jar:na]
at org.springframework.statemachine.kryo.KryoStateMachineSerialisationService.doDecode(KryoStateMachineSerialisationService.java:45) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService$3.execute(AbstractKryoStateMachineSerialisationService.java:147) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
at com.esotericsoftware.kryo.pool.KryoPoolQueueImpl.run(KryoPoolQueueImpl.java:58) ~[kryo-shaded-4.0.2.jar:na]
at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService.decode(AbstractKryoStateMachineSerialisationService.java:143) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService.decode(AbstractKryoStateMachineSerialisationService.java:130) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService.deserialiseStateMachineContext(AbstractKryoStateMachineSerialisationService.java:72) ~[spring-statemachine-kryo-3.0.0.jar:3.0.0]
More esoteric messages... `2022-08-17 09:32:59.100 ERROR 98020 --- [nio-8989-exec-9] o.s.s.s.DefaultStateMachineService : Error handling context
com.esotericsoftware.kryo.KryoException: java.lang.IndexOutOfBoundsException: Index 109 out of bounds for length 8 Serialization trace: status (org.yretirement.ycap.engine.client.model.workflow.CancellationRequest) at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:144) ~[kryo-shaded-4.0.2.jar:na] at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543) ~[kryo-shaded-4.0.2.jar:na] at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na] at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:161) ~[kryo-shaded-4.0.2.jar:na] at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:39) ~[kryo-shaded-4.0.2.jar:na] at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813) ~[kryo-shaded-4.0.2.jar:na] at org.springframework.statemachine.kryo.StateMachineContextSerializer.read(StateMachineContextSerializer.java:66) ~[spring-statemachine-kryo-3.2.0.jar:3.2.0] at org.springframework.statemachine.kryo.StateMachineContextSerializer.read(StateMachineContextSerializer.java:39) ~[spring-statemachine-kryo-3.2.0.jar:3.2.0] at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:709) ~[kryo-shaded-4.0.2.jar:na] at org.springframework.statemachine.kryo.KryoStateMachineSerialisationService.doDecode(KryoStateMachineSerialisationService.java:45) ~[spring-statemachine-kryo-3.2.0.jar:3.2.0] at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService$3.execute(AbstractKryoStateMachineSerialisationService.java:147) ~[spring-statemachine-kryo-3.2.0.jar:3.2.0] at com.esotericsoftware.kryo.pool.KryoPoolQueueImpl.run(KryoPoolQueueImpl.java:58) ~[kryo-shaded-4.0.2.jar:na] at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService.decode(AbstractKryoStateMachineSerialisationService.java:143) ~[spring-statemachine-kryo-3.2.0.jar:3.2.0] at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService.decode(AbstractKryoStateMachineSerialisationService.java:130) ~[spring-statemachine-kryo-3.2.0.jar:3.2.0] at org.springframework.statemachine.kryo.AbstractKryoStateMachineSerialisationService.deserialiseStateMachineContext(AbstractKryoStateMachineSerialisationService.java:72) ~[spring-statemachine-kryo-3.2.0.jar:3.2.0] at org.springframework.statemachine.data.RepositoryStateMachinePersist.read(RepositoryStateMachinePersist.java:76) ~[spring-statemachine-data-common-3.2.0.jar:3.2.0] at org.springframework.statemachine.data.jpa.JpaPersistingStateMachineInterceptor.read(JpaPersistingStateMachineInterceptor.java:70) ~[spring-statemachine-data-jpa-3.2.0.jar:3.2.0] at org.springframework.statemachine.service.DefaultStateMachineService.acquireStateMachine(DefaultStateMachineService.java:94) ~[spring-statemachine-core-3.2.0.jar:3.2.0] at org.springframework.statemachine.service.DefaultStateMachineService.acquireStateMachine(DefaultStateMachineService.java:79) ~[spring-statemachine-core-3.2.0.jar:3.2.0]`
I am getting the same error as well. I believe this is due to serialization issue with kryo. stateMachine.getExtendedState().getVariables().put(PARAM_PROCESSOR, data); The model class that your field 'data' refers to might have changed since the last time stateMachine was persisted in DB.
Please let me know if you find a solution.
Same issue here. I don't know if there is a most recent Kyro version to provide a better approach ignoring properties that don't exist in serialized data.
Same error when context object has its model updated. Added a field and now every state machine older to this change is broken. Anyone going through this? Any solution or workaround?
This was a pain so I ended up having to not use the extended state and create a JPA implementation and saving my data as json
create table state_machine_context_data
(
state_machine_context_data_id bigint identity
primary key,
state_machine_id bigint not null,
payload nvarchar(max),
created_date datetime
constraint df_created_state_machine_context_data default getdate(),
updated_date datetime,
context_class varchar(200) not null
)
go
Then used JPA to create the object
import java.io.Serializable;
import javax.persistence.*;
import java.sql.Timestamp;
@Entity
@Table(name="state_machine_context_data")
public class StateMachineContextData implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="state_machine_context_data_id")
private long stateMachineContextDataId;
@Column(name="context_class")
private String contextClass;
@Column(name="created_date")
private Timestamp createdDate;
@Lob
@Column
private String payload;
@Column(name="state_machine_id")
private long stateMachineId;
@Column(name="updated_date")
private Timestamp updatedDate;
public StateMachineContextData() {
}
public long getStateMachineContextDataId() {
return this.stateMachineContextDataId;
}
public void setStateMachineContextDataId(long stateMachineContextDataId) {
this.stateMachineContextDataId = stateMachineContextDataId;
}
public String getContextClass() {
return this.contextClass;
}
public void setContextClass(String contextType) {
this.contextClass = contextType;
}
public Timestamp getCreatedDate() {
return this.createdDate;
}
public void setCreatedDate(Timestamp createdDate) {
this.createdDate = createdDate;
}
public String getPayload() {
return this.payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
public long getStateMachineId() {
return this.stateMachineId;
}
public void setStateMachineId(long stateMachineId) {
this.stateMachineId = stateMachineId;
}
public Timestamp getUpdatedDate() {
return this.updatedDate;
}
public void setUpdatedDate(Timestamp updatedDate) {
this.updatedDate = updatedDate;
}
}
Then created a Repository
import java.util.List;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import org.yretirement.ycap.engine.data.table.StateMachineContextData;
public interface StateMachineContextDataRepository extends CrudRepository<StateMachineContextData, Long>{
List<StateMachineContextData>findByStateMachineId(long stateMachineId);
Optional<StateMachineContextData> findByStateMachineIdAndContextClass(long stateMachineId, String contextClass);
}
Created a Getter
public <T> Optional<T> getContextData(long machineId, Class<T> clazz) {
Optional<StateMachineContextData> context = contextRepository.findByStateMachineIdAndContextClass(machineId, clazz.getCanonicalName());
if(context.isPresent())
return Optional.of(decompressPayload(context.get(), clazz));
else
return Optional.empty();
}
private <T> T decompressPayload(StateMachineContextData context, Class<T> clazz) {
Gson gson = new Gson();
String payload = compressionService.tryDecompress(context.getPayload());
return gson.fromJson(payload, clazz);
}
and a setter
public void setContextData(long machineId, Object obj) {
Optional<StateMachineContextData> context = contextRepository.findByStateMachineIdAndContextClass(machineId, obj.getClass().getCanonicalName());
StateMachineContextData data = new StateMachineContextData();
if(context.isPresent()) {
data = context.get();
data.setPayload(compressPayload(obj));
data.setUpdatedDate(new Timestamp(System.currentTimeMillis()));
}else {
data.setStateMachineId(machineId);
data.setPayload(compressPayload(obj));
data.setContextClass(obj.getClass().getCanonicalName());
data.setCreatedDate(new Timestamp(System.currentTimeMillis()));
data.setUpdatedDate(new Timestamp(System.currentTimeMillis()));
}
contextRepository.save(data);
}
And I would use it like so
CancellationRequest request = getContextData(stateMachineId, CancellationRequest.class);
setContextData(stateMachineId, request);
I