Skip to content

Conversation

@kajebiii
Copy link
Contributor

@kajebiii kajebiii commented Mar 17, 2025

While using Scala version 3.6.3, ZIO version 2.1.16, and altoo-ag/scala-kryo-serialization version 1.2.0

I encountered the following error when serializing NonEmptyChunk[A] with Kryo:

This issue possibly stems from an initialization order problem.
To avoid the NullPointerException, this PR changes val to def, ensuring proper initialization.

  Caused by java.lang.NullPointerException: Cannot invoke "scala.collection.IterableFactory.newBuilder()" because the return value of "scala.collection.Iterable.iterableFactory()" is null
    io.altoo.serialization.kryo.scala.serializer.ScalaCollectionSerializer.read(ScalaCollectionSerializer.scala:34)
    io.altoo.serialization.kryo.scala.serializer.ScalaCollectionSerializer.read(ScalaCollectionSerializer.scala:31)
    com.esotericsoftware.kryo.kryo5.Kryo.readObject(Kryo.java:796)
    com.esotericsoftware.kryo.kryo5.serializers.ReflectField.read(ReflectField.java:124)
    com.esotericsoftware.kryo.kryo5.serializers.FieldSerializer.read(FieldSerializer.java:130)
    com.esotericsoftware.kryo.kryo5.Kryo.readObjectOrNull(Kryo.java:847)
    com.esotericsoftware.kryo.kryo5.serializers.ReflectField.read(ReflectField.java:133)
    com.esotericsoftware.kryo.kryo5.serializers.FieldSerializer.read(FieldSerializer.java:130)
    com.esotericsoftware.kryo.kryo5.Kryo.readObject(Kryo.java:796)
    com.esotericsoftware.kryo.kryo5.serializers.ReflectField.read(ReflectField.java:124)
    com.esotericsoftware.kryo.kryo5.serializers.FieldSerializer.read(FieldSerializer.java:130)
    com.esotericsoftware.kryo.kryo5.Kryo.readClassAndObject(Kryo.java:877)
    io.altoo.serialization.kryo.scala.KryoSerializerBackend.fromBinary(KryoSerializerBackend.scala:61)
    io.altoo.serialization.kryo.scala.KryoSerializer.fromBinaryInternal(KryoSerializer.scala:183)
    io.altoo.serialization.kryo.scala.ScalaKryoSerializer.deserialize$$anonfun$1(ScalaKryoSerializer.scala:23)
    scala.util.Try$.apply(Try.scala:217)
    io.altoo.serialization.kryo.scala.ScalaKryoSerializer.deserialize(ScalaKryoSerializer.scala:23)
    com.devsisters.shardcake.KryoSerialization$$anon$1.decode$$anonfun$1(KryoSerialization.scala:29)
    zio.ZIO$.fromTry$$anonfun$1(ZIO.scala:3871)
    zio.ZIOCompanionVersionSpecific.attempt$$anonfun$1(ZIOCompanionVersionSpecific.scala:108)
    zio.ZIO$.suspendSucceed$$anonfun$1(ZIO.scala:4864)
    zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1063)
    zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1067)
    zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1067)
    zio.internal.FiberRuntime.evaluateEffect(FiberRuntime.scala:412)
    zio.internal.FiberRuntime.evaluateMessageWhileSuspended(FiberRuntime.scala:487)
    zio.internal.FiberRuntime.drainQueueOnCurrentThread(FiberRuntime.scala:249)
    zio.internal.FiberRuntime.run(FiberRuntime.scala:137)
    zio.internal.ZScheduler$$anon$3.run(ZScheduler.scala:380)

hearnadam
hearnadam previously approved these changes Mar 18, 2025
@ghostdogpr
Copy link
Member

ghostdogpr commented Mar 18, 2025

Unfortunately Mima is not happy. I wonder if there's a way to change it in a compatible way 🤔

Another way to deal with serialization specifically would be to mark the val as @transient I think, could you test if it works? EDIT: didn't solve the NullPointerException

@kajebiii kajebiii changed the title Change iterableFactory from val to def in ChunkLike Use @transient on iterableFactory in ChunkLike to avoid NPE Mar 18, 2025
@kajebiii
Copy link
Contributor Author

Unfortunately Mima is not happy. I wonder if there's a way to change it in a compatible way 🤔

Another way to deal with serialization specifically would be to mark the val as @transient I think, could you test if it works?

I tried adding @transient and tested it locally, but unfortunately, this solution does not resolve the Kryo issue. 😭

@kajebiii kajebiii changed the title Use @transient on iterableFactory in ChunkLike to avoid NPE Change iterableFactory from val to def in ChunkLike Mar 18, 2025
@ghostdogpr
Copy link
Member

Mima is complaining about the lack of a $init$ function. That synthetic function is in charge of initialization code but is deleted when there is no val. It is currently like this:

public interface ChunkLike<A> extends IndexedSeq<A>, StrictOptimizedSeqOps<A, Chunk, Chunk<A>> {
    static void $init$(final ChunkLike $this) {
        $this.zio$ChunkLike$_setter_$iterableFactory_$eq(.MODULE$);
    }

I tried adding a rogue () in ChunkLike and it creates $init$ as follows:

public interface ChunkLike<A> extends IndexedSeq<A>, StrictOptimizedSeqOps<A, Chunk, Chunk<A>> {
    static void $init$(final ChunkLike $this) {
    }

It's dirty but I think that would be okay?

Copy link
Member

@ghostdogpr ghostdogpr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving but not merging yet just in case someone has a more elegant solution 😄

@kyri-petrou kyri-petrou merged commit 7140a38 into zio:series/2.x Mar 18, 2025
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants