-1

I'm currently looking at making two different persistent actors communicate with each other. In particular:

Given an Actor A exists
When an Actor B is spawned
Then Actor B must have a reference to Actor A
And Actor B must be able to continuously send messages to Actor A even after relocation

I know that there are two options:

// With an EntityRef
val counterOne: EntityRef[Counter.Command] = sharding.entityRefFor(TypeKey, "counter-1")
counterOne ! Counter.Increment

// Entity id is specified via an `ShardingEnvelope`
shardRegion ! ShardingEnvelope("counter-1", Counter.Increment)

The second option seems like a nice way to go since I'll be delegating the resolution of the actual reference to the entity to Akka. I'll probably just need to pass some wrapper function to my Actor on instantiation. For example

val shardRegionA: ActorRef[ShardingEnvelope[Counter.Command]] =
  sharding.init(Entity(TypeA)(createBehavior = entityContext => A()))

def delegate_A(id,message) = {
    shardRegionA ! ShardingEnvelope(id,message)
}

val shardRegionB: ActorRef[ShardingEnvelope[Counter.Command]] =
  sharding.init(Entity(TypeB)(createBehavior = entityContext => B(delegate_A)))

--------

object B {
    def apply(delegate) = {
        ...somewhere inside the state...
        delegate("some_id_of_A", Message("Hello"))
        ...somewhere inside the state...
    }
}

But, I'd also like to understand whether the first option is simpler because the EntityRef might be safely persistable in the state/events.

object B {
    def apply(entityRefA : EntityRef[A]) = {
       EventSourcedBehavior[...](
           emptyState = State(entityRefA)
       )
    }
}

Anyone have any insights on this?

Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30
Rey Pader
  • 83
  • 1
  • 5

1 Answers1

0

EntityRef isn't safely persistable in state/events (barring some very fragile reflection-based serialization), since it doesn't expose the information which would allow a deserializer to rebuild an equivalent EntityRef. The default Jackson serialization also does not usefully deserialize EntityRefs.

There's a PR up as of the time of this answer to allow the "definitional" components of an EntityRef to be extracted for serialization (e.g. so EntityRef[Employee.Command] could be JSON serialized as { "entityId": "123456789", "typeKey": "EMPLOYEE" }. That PR would still require custom serialization for any messages, persisted events, or state (if snapshotting) which contain EntityRefs, but at least it would then be possible to include EntityRefs in such objects.

Until that time, you shouldn't put EntityRefs into messages, events, or snapshottable state: instead you basically have to put the IDs into those objects and send messages wrapped in ShardingEnvelopes to the shard region actor (which is what EntityRef.tell does anyway). In some cases, it might be reasonable to maintain a mapping of entity IDs to EntityRefs in a non-persistent child actor and send messages to EntityRefs via that child actor, or if willing to block or really contort your protocol, do asks to that child to resolve EntityRefs for you.

EDIT to note that as of Akka 2.6.13, it's possible to implement a custom serializer to handle EntityRefs; the Jackson serializers at this point do not support EntityRef. A means of resolving a type key and entity ID into an EntityRef would have to be injected into the serializer.

Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30