jameswilddev
jameswilddev

Reputation: 612

How does Akka.NET persistence handle replaying messages containing IActorRef?

If I send an Akka.NET actor a message which is an object containing an IActorRef, and then persist that message, the JSON written to the journal table looks like this:

{"$id":"1","$type":"LearningAkka.Program+BindReference, LearningAkka","Reference":{"$id":"2","$type":"Akka.Actor.ActorRefBase+Surrogate, Akka","Path":"akka://LearningAkka/user/$b#1222898859"}}

If I'm understanding this right, this is just a reference to an actor instance; the "Props" required to create it are not stored in this message.

Weirdly, I am seeing an object there after restarting the app. However, as expected, it is not as constructed before the restart. Where did this actor come from? Has Akka Persistence found an actor which is "similar enough" and used it instead?

The following C# test application creates an object and sends a message binding it to one of three others. After disposing of the actor system, that object is recreated from persistence (SQL Server) and the reference is checked.

My expected behaviour is any of the following (I'm not sure what's most appropriate):

Console output:

[WARNING][27/05/2017 21:02:27][Thread 0001][ActorSystem(LearningAkka)] NewtonSoftJsonSerializer has been detected as a default serializer. It will be obsoleted in Akka.NET starting from version 1.5 in the favor of Hyperion (for more info visit: http://getakka.net/docs/Serialization#how-to-setup-hyperion-as-default-serializer ). If you want to suppress this message set HOCON `akka.suppress-json-serializer-warning` config flag to on.
From the first run B

[WARNING][27/05/2017 21:02:28][Thread 0001][ActorSystem(LearningAkka)] NewtonSoftJsonSerializer has been detected as a default serializer. It will be obsoleted in Akka.NET starting from version 1.5 in the favor of Hyperion (for more info visit: http://getakka.net/docs/Serialization#how-to-setup-hyperion-as-default-serializer ). If you want to suppress this message set HOCON `akka.suppress-json-serializer-warning` config flag to on.
From the second run B

C#:

using Akka.Actor;
using Akka.Event;
using Akka.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LearningAkka
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var actorSystem = ActorSystem.Create("LearningAkka"))
            {
                var referenceA = actorSystem.ActorOf(Props.Create(() => new TestReferencedActor("From the first run A")));
                var referenceB = actorSystem.ActorOf(Props.Create(() => new TestReferencedActor("From the first run B")));
                var referenceC = actorSystem.ActorOf(Props.Create(() => new TestReferencedActor("From the first run C")));
                var actor = actorSystem.ActorOf(Props.Create(() => new TestActor()));
                actor.Tell(new BindReference { Reference = referenceB });
                actor.Tell(new CheckReference());
                Console.ReadLine();
            }

            using (var actorSystem = ActorSystem.Create("LearningAkka"))
            {
                var referenceA = actorSystem.ActorOf(Props.Create(() => new TestReferencedActor("From the second run A")));
                var referenceB = actorSystem.ActorOf(Props.Create(() => new TestReferencedActor("From the second run B")));
                var referenceC = actorSystem.ActorOf(Props.Create(() => new TestReferencedActor("From the second run C")));
                var actor = actorSystem.ActorOf(Props.Create(() => new TestActor()));
                actor.Tell(new CheckReference());
                Console.ReadLine();
            }
        }

        public struct BindReference { public IActorRef Reference; }
        public struct CheckReference { }

        public sealed class TestActor : ReceivePersistentActor
        {
            public override string PersistenceId => "test hardcoded";

            private IActorRef StoredFromMessage;

            public TestActor()
            {
                Command<CheckReference>(m => StoredFromMessage.Tell(m));
                Command<BindReference>(m => Persist(m, m2 => StoredFromMessage = m2.Reference));
                Recover<BindReference>(m => StoredFromMessage = m.Reference);
            }
        }

        public sealed class TestReferencedActor : ReceiveActor
        {
            public TestReferencedActor(string ourLabel)
            {
                Receive<CheckReference>(m => Console.WriteLine(ourLabel));
            }
        }
    }
}

HOCON:

      akka {
        persistence {
          journal {
            plugin = "akka.persistence.journal.sql-server"
            sql-server {
              class = "Akka.Persistence.SqlServer.Journal.SqlServerJournal, Akka.Persistence.SqlServer"
              connection-string = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=LearningAkka;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
              schema-name = dbo
              table-name = Journal
              auto-initialize = on
            }
          }
          snapshot-store {
            plugin = "akka.persistence.snapshot-store.sql-server"
            sql-server {
              class = "Akka.Persistence.SqlServer.Snapshot.SqlServerSnapshotStore, Akka.Persistence.SqlServer"
              connection-string = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=LearningAkka;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
              schema-name = dbo
              table-name = Snapshot
              auto-initialize = on
            }
          }
        }
      }

Could someone please comment on the behaviour here? Thank you.

Upvotes: 0

Views: 667

Answers (1)

Kantora
Kantora

Reputation: 111

As you can see from serialization data - your IActorRef points to this address akka://LearningAkka/user/$b. Where $b is usually placed for unnamed actors. So it will always be the second unnamed actor you create in the actor system root (as far as I know).

So you are right - the system behavior is undefined here.

Upvotes: 0

Related Questions