akkadotnet/Hyperion

Deserializing a message with two ActorPaths to the same actor throws an invalid cast exception

jaydeboer opened this issue · 3 comments

Version Information
Akka.NET version 1.4.26 running on net5.0
Which Akka.NET Modules?
Akka.Persistence
Akka.Serialization.Hyperion

Describe the bug
When I have a message that contains a list of ActorPaths, deserialization with Hyperion fails if the same actor path is in the list multiple times. If there are unique actor paths, everything works as expected.

To Reproduce
Here are 2 xUnit tests, the one with a duplicate ActorPath will cause the exception and the object will never be deserialized, the other, with different actor paths, will deserialize properly and will pass.

using Akka.Actor;
using FluentAssertions;
using System.Collections.Generic;
using Xunit;

namespace TE.ActorSystem.Core.UnitTests
{
    public class ReproForAkka : Akka.TestKit.Xunit2.TestKit
    {
        public ReproForAkka()
            : base(@"
        akka.actor {
            serializers { hyperion = ""Akka.Serialization.HyperionSerializer, Akka.Serialization.Hyperion"" }
            serialization-bindings { ""System.Object"" = hyperion }
        }")
        { }

        public class ContainerClass
        {
            public List<ActorPath> Destinations { get; init; }
        }

        [Fact]
        public void MakeItBlowUp()
        {
            DoIt(new ContainerClass { Destinations = new List<ActorPath> { TestActor.Path, TestActor.Path } });
        }

        [Fact]
        public void ThisOnePasses()
        {
            DoIt(new ContainerClass { Destinations = new List<ActorPath> { TestActor.Path, CreateTestProbe().TestActor.Path } });
        }

        private void DoIt(ContainerClass src)
        {
            var serialization = Sys.Serialization;
            var serializer = serialization.FindSerializerFor(src);
            var actual = serializer.FromBinary<ContainerClass>(serializer.ToBinary(src));

            actual.Should().BeEquivalentTo(src);

        }
    }
}

Expected behavior
I expect that a message could contain multiple copies of the same ActorPath and be deserialized properly without the exception being thrown.

Actual behavior
An InvalidCastException is thrown.

Environment
Windows 10
Net 5.0

The problem stems that when preserve-object-references is set to true, DeserializeSession stores the deserialized reference to ActorPath.Surrogate instead of the final ActorPath instance. Exception is then thrown when the deserializer tries to add the surrogate into the calling List<ActorPath> deserializer

Nice work @Arkatufus - I'll review your PR.

Thanks for the quick work guys!