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

Unable to restore statemachine because of sterilization issues from com.esotericsoftware.kryo.KryoException

Open AbstractAlao opened this issue 2 years ago • 5 comments

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]

AbstractAlao avatar Aug 10 '22 19:08 AbstractAlao

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]`

AbstractAlao avatar Aug 17 '22 13:08 AbstractAlao

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.

Aaqib21 avatar Sep 16 '22 16:09 Aaqib21

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.

gustavodaquino avatar Apr 17 '23 18:04 gustavodaquino

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?

brunodisanto avatar Aug 25 '23 12:08 brunodisanto

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

AbstractAlao avatar Aug 25 '23 13:08 AbstractAlao