Serialization of Unit in akka
A common mistake in scala is using Unit, the companion object, as its value. The correct value of type Unit is (). Typically, one can get away with using Unit as the value, and
def unitType: Unit = Unit
def unitValue: Unit = ()behave similarly. However, this becomes an issue when serialization1 comes into play. Take, for example, the following actor2:
class UnitActor extends Actor {
override def receive: Receive = {
case Unit => sender ! "Received Unit Object"
case () => sender ! "Received Unit value"
}
}This simply returns a string to the sender upon receipt of a message. When run on the same VM, everything behaves as expected:
class NoSerializableTest extends TestKit(ActorSystem("SerializableTest")) with WordSpecLike with ImplicitSender {
"ActorSystem without requiring serialization" should {
val unitActor = TestActorRef[UnitActor]
"send Unit object" in {
unitActor ! Unit
within(1 second){
expectMsg("Received Unit Object")
}
}
"send unit value" in {
unitActor ! ()
within(1 second){
expectMsg("Received Unit value")
}
}
}
}However, if the same actor was used in a distributed system, messages would have to be serialized and Unit messages will not be sent across the wire.
class SerializableTest extends TestKit(ActorSystem("SerializableTest", ConfigFactory.parseString(
"akka.actor.serialize-messages = on"))) with WordSpecLike with ImplicitSender {
"ActorSystem with serialization" should {
val unitActor = TestActorRef[UnitActor]
"fail to send Unit Object" in {
unitActor ! Unit
within(1 second){
expectNoMsg()
}
}
"send unit value" in {
unitActor ! ()
within(1 second){
expectMsg("Received Unit value")
}
}
}
}Note that we have mimicked what would happen when messages are sent between VMs by setting akka.actor.serialize-messages = on3. Using the default java serializer, () messages are sent without a problem, but Unit messages are dropped with the following error being logged to STDOUT:
[ERROR] [05/17/2015 14:37:30.959] [pool-6-thread-3-ScalaTest-running-SerializableTest] [akka://SerializableTest/user/$$a] swallowing exception during message send
java.io.NotSerializableException: No configured serialization-bindings for class [scala.Unit$]Unit is just a regular companion object and plain objects are not serializable, only case objects are.
Take aways:
- Use
()as theUnitvalue and notUnititself. - Turn on
serialize-messageswhen testing actor systems that will leave a single VM.