Support for type parameters or type members in Akka messages

Hi there,

when looking at the documentation about serialization https://doc.akka.io/docs/akka/current/serialization.html, I haven’t found mention or word of caution about what happens to message types bearing type parameters and/or type members.

If I understand well, the default jackson serializer equipped with the scala module will support messages involving “collection” fields such as Option, List, etc. but the message itself being generic won’t be directly supported without some intervention, correct?

I’m thinking it would be worth mentioning there maybe? - especially as locally in a single JVM it will appear to work, and when forcing serialization or scaling to multi-node, messages won’t make it through

1 Like

Hi Jonas!

From what you say I assume you know that generic type information is erased at runtime, even on the local JVM, so if you have a truly generic message going from one actor to the other, then you’ll need to include runtime type information within that message so that the receiver can make sense of the message — this is true locally and remote.

In this regard, you may either use ClassTags (which work only for shallowly generic types, i.e. they capture only the outermost type and leave their own type parameters erased at runtime), or you make your ActorRef more specific: if you statically know that along this reference you always send Option[String] then there is no need to include this knowledge at runtime, and this holds locally and remote as well. Of course the ref needs to correspond to an appropriately typed receiver, so if you obtain ActorRef[Option[String]] by narrowing an ActorRef[Option[Any]] then you’re in truly generic land again.

In the case of including runtime type information, the only requirement for remote communication is that the object containing this information be serializable, which is not special and therefore needs no specific mention in the serialization docs, I think.

Since you didn’t make your actual problem statement explicit, does this answer your question? I’d be interested in hearing more about your intended use-case.

Regards,

Roland

PS: I’m assuming that you use Option just as an example to illustrate your question, as actor messages should always be user-defined classes, not classes from the standard library. The reason is that the protocol of an Actor is tightly coupled to its meaning, its semantics, and you rarely implement an Actor for generically sorting any List that someone might send to it.

1 Like

Hi Roland!

Thank you very much for taking the time to answer, I really appreciate. Thank you also for your informative answer.

Our particular use case, since you kindly ask, is the following: we have adopted an approach where we keep a domain model as pure as possible, with commands, replies and events described without direct referencing to Akka types, e.g. with such definitions:

trait EntityCommand {
  type Reply
}

In what we call the “infrastructure” layer, we create some wrapper message type which wraps these higher-level commands:

trait CommandExpectingReply[Command <: EntityCommand] {
  val command: Command
  val replyTo: ActorRef[command.Reply]
}

(for which we have an implementation similar to this)

private case class Impl[Command <: EntityCommand, R](command: Command { type Reply = R }, replyTo: ActorRef[R])
    extends CommandExpectingReply[Command]

This indeed works fine locally, until we force serialization when running tests.

So I was craving for any information around how to deal with generically typed messages in a cluster environment, hence my question :slight_smile: If I understand well, we will indeed have to include some information with the command type, or use a completely custom serialization strategy. Seems like ClassTag should be enough to distinguish what command type we are tackling. So if I understood well, the built-in jackson configuration in akka supports embedding ClassTag values in messages?

Btw. if you are interested in finding out more about our motivations and our approach, we have written an article about it here: https://medium.com/bestmile/domain-driven-event-sourcing-with-akka-typed-5f5b8bbfb823
(please note that in that article, we use a slightly different typing strategy, based on type parameters rather than type members, the latter with which we are experimenting with at the moment)

Thanks again! any additional information welcome of course :slight_smile:

Thanks for the link to the blog post, very enlightening! As far as I can see that is a good way of separating the code along the lines you want to cut it, and from that code I cannot tell how it should fail — indeed it would be most interesting to hear more about the precise symptoms arising when forcing serialisation in the test suite. I suspect that this has less to do with generic message types than with non-serialisable closures.

In other words, you are already applying the “precise ActorRef pattern” I alluded to, and in particular you don’t need any runtime type information to make this work.

Regards,

Roland

So I’ve performed some more tests and indeed as you were expecting, serialization errors when using Java serialization were arising due to the $outer closure, due I believe to the fact that command types were enclosed within the test class. Moving them outside fixes the issue.

When activating jackson, we get what I understand as polymorphism-related errors (which is to be expected I suppose):

Cannot construct instance of `com.bestmile.helpers.akka.persistence.entity.EntityCommand` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

Thanks again for your help! I’ve learned some good things along the way.

Regards and happy :christmas_tree: