在Kryo堆栈反序列化期间调试“无法还原的检查点”和UnsupportableException

nim*_*maj 1 corda

我在Corda中遇到了序列化问题。我的新代码路径不包含尚未序列化/反序列化的新类,但是会发生以下异常而爆炸。请注意,它在堆栈跟踪中不包含特定于CorDapp的类,这使得很难找出问题所在。

是否有任何关于如何开始调查此类异常的根本原因的提示/技巧?

谢谢 :-)

com.esotericsoftware.kryo.KryoException: java.lang.UnsupportedOperationException
Serialization trace:
dataObject (co.paralleluniverse.fibers.Stack)
stack (net.corda.node.services.statemachine.FlowStateMachineImpl)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:144)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:147)
    at com.esotericsoftware.kryo.Kryo.readObjectOrNull(Kryo.java:782)
    at co.paralleluniverse.io.serialization.kryo.ReplaceableObjectKryo.readObjectOrNull(ReplaceableObjectKryo.java:107)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:132)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:540)
    at co.paralleluniverse.fibers.Fiber$FiberSerializer.read(Fiber.java:2137)
    at co.paralleluniverse.fibers.Fiber$FiberSerializer.read(Fiber.java:2067)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813)
    at co.paralleluniverse.io.serialization.kryo.ReplaceableObjectKryo.readClassAndObject(ReplaceableObjectKryo.java:112)
    at net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme$deserialize$$inlined$use$lambda$1$1.invoke(KryoSerializationScheme.kt:97)
    at net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme$deserialize$$inlined$use$lambda$1$1.invoke(KryoSerializationScheme.kt:37)
    at net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme.withContext(KryoSerializationScheme.kt:80)
    at net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme.access$withContext(KryoSerializationScheme.kt:37)
    at net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme$deserialize$$inlined$use$lambda$1.execute(KryoSerializationScheme.kt:95)
    at com.esotericsoftware.kryo.pool.KryoPoolQueueImpl.run(KryoPoolQueueImpl.java:61)
    at net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme.deserialize(KryoSerializationScheme.kt:94)
    at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl$deserialize$1$1.invoke(SerializationScheme.kt:111)
    at net.corda.core.serialization.SerializationFactory.withCurrentContext(SerializationAPI.kt:66)
    at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl$deserialize$1.invoke(SerializationScheme.kt:111)
    at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl$deserialize$1.invoke(SerializationScheme.kt:86)
    at net.corda.core.serialization.SerializationFactory.asCurrent(SerializationAPI.kt:80)
    at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl.deserialize(SerializationScheme.kt:111)
    at net.corda.node.services.statemachine.StateMachineManagerImpl.deserializeFiber(StateMachineManagerImpl.kt:715)
    at net.corda.node.services.statemachine.StateMachineManagerImpl.access$deserializeFiber(StateMachineManagerImpl.kt:63)
    at net.corda.node.services.statemachine.StateMachineManagerImpl$updateCheckpoint$2.run(StateMachineManagerImpl.kt:551)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.UnsupportedOperationException
    at java.util.AbstractCollection.add(AbstractCollection.java:262)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:134)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:40)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813)
    at co.paralleluniverse.io.serialization.kryo.ReplaceableObjectKryo.readClassAndObject(ReplaceableObjectKryo.java:112)
    at de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer.read(UnmodifiableCollectionsSerializer.java:71)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731)
    at co.paralleluniverse.io.serialization.kryo.ReplaceableObjectKryo.readObject(ReplaceableObjectKryo.java:92)
    at com.esotericsoftware.kryo.serializers.DefaultArraySerializers$ObjectArraySerializer.read(DefaultArraySerializers.java:392)
    at com.esotericsoftware.kryo.serializers.DefaultArraySerializers$ObjectArraySerializer.read(DefaultArraySerializers.java:303)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731)
    at co.paralleluniverse.io.serialization.kryo.ReplaceableObjectKryo.readObject(ReplaceableObjectKryo.java:92)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:125)
    ... 28 more
[INFO ] 17:02:38,664 [Mock network] (FlowStateMachineImpl.kt:432) flow.[66a8e516-7d9e-4ff3-a922-c80dda86768e].initiateSession - Initiating flow session with party O=Notary Service, L=Zurich, C=CH. Session id for tracing purposes is SessionId(toLong=763122305110574148). {}
[ERROR] 17:02:38,660 [CheckpointChecker-1] (StateMachineManagerImpl.kt:417) flow.[66a8e516-7d9e-4ff3-a922-c80dda86768e].deserializeFiber - Encountered unrestorable checkpoint! {}
com.esotericsoftware.kryo.KryoException: java.lang.UnsupportedOperationException
Run Code Online (Sandbox Code Playgroud)

nim*_*maj 5

从Kat and Andras @ R3发布一些很棒的帮助,我们深入到了本期。我被要求将其发布在这里,以便有更多的Google帮助!

解决方案(TL; DR)

我有一个对象图,对于每个对象,我想调用一个实质上启动两个子流(在handleEvent方法下)的方法。

这是失败的:

modelDataMap.values.forEach { it.handleEvent(event, this, flowLogic, notary) }
Run Code Online (Sandbox Code Playgroud)

但这可以解决问题:

modelDataMap.values.toList().forEach { it.handleEvent(event, this, flowLogic, notary) }
Run Code Online (Sandbox Code Playgroud)

我认为在第一种情况下,Kryo将值反序列化为不可修改的集合,然后无法为其添加元素。toList导致Kryo创建了一个可以向其中添加值的集合,并且一切都很好。

如何解决这个问题

我知道触发此问题的已更改的代码路径。以我为例,在堆栈跟踪之前有一条日志行,向我提示从哪里开始。

此时,Andras指出了两个有用的工具。

缺少@Suspendable注解

如果将以下标志添加到测试中:

-Dco.paralleluniverse.fibers.verifyInstrumentation=true
Run Code Online (Sandbox Code Playgroud)

您将被告知堆栈中没有@Suspendable批注的方法。这实际上是很有帮助的。

FlowStackSnapshot

Andras已向FlowLogic添加了一个工具,可用于打印出堆栈。我们有一个想法,就是为此添加一个方法,该方法将遍历整个堆栈,递归地对所有内容进行序列化和反序列化,以便可以确定问题的出处。

目前,在炸毁之前将其添加到合适的位置至少可以从Kryo的角度看到堆栈:

val snapshot = flowLogic.flowStackSnapshot()
Run Code Online (Sandbox Code Playgroud)

请注意,从Corda 3.2开始,flowStackSnapshot的代码路径中缺少@Suspendable,因此暂时不要在flowStackSnapshot中使用verifyInstrumentation标志:-)

再次-非常感谢Kat和Andras的帮助。