2013-04-04 17:56:29 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
|
|
|
|
*/
|
|
|
|
|
package akka.cluster.protobuf
|
|
|
|
|
|
|
|
|
|
import akka.serialization.Serializer
|
|
|
|
|
import akka.cluster._
|
|
|
|
|
import scala.collection.breakOut
|
2013-06-13 15:43:37 -04:00
|
|
|
import scala.collection.immutable.TreeMap
|
2013-04-04 17:56:29 +02:00
|
|
|
import akka.actor.{ ExtendedActorSystem, Address }
|
|
|
|
|
import scala.Some
|
|
|
|
|
import scala.collection.immutable
|
|
|
|
|
import java.io.{ ByteArrayInputStream, ObjectOutputStream, ByteArrayOutputStream }
|
|
|
|
|
import com.google.protobuf.ByteString
|
|
|
|
|
import akka.util.ClassLoaderObjectInputStream
|
|
|
|
|
import java.{ lang ⇒ jl }
|
2013-05-14 21:38:43 +02:00
|
|
|
import java.util.zip.GZIPOutputStream
|
|
|
|
|
import java.util.zip.GZIPInputStream
|
|
|
|
|
import com.google.protobuf.MessageLite
|
|
|
|
|
import scala.annotation.tailrec
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-07-08 13:42:44 +02:00
|
|
|
/**
|
|
|
|
|
* Protobuf serializer of cluster messages.
|
|
|
|
|
*/
|
2013-04-04 17:56:29 +02:00
|
|
|
class ClusterMessageSerializer(val system: ExtendedActorSystem) extends Serializer {
|
|
|
|
|
|
2013-05-14 21:38:43 +02:00
|
|
|
private final val BufferSize = 1024 * 4
|
|
|
|
|
|
2013-04-04 17:56:29 +02:00
|
|
|
private val fromBinaryMap = collection.immutable.HashMap[Class[_ <: ClusterMessage], Array[Byte] ⇒ AnyRef](
|
2013-04-11 09:18:12 +02:00
|
|
|
classOf[InternalClusterAction.Join] -> {
|
2013-04-04 17:56:29 +02:00
|
|
|
case bytes ⇒
|
|
|
|
|
val m = msg.Join.defaultInstance.mergeFrom(bytes)
|
2013-04-11 09:18:12 +02:00
|
|
|
InternalClusterAction.Join(uniqueAddressFromProto(m.node), m.roles.toSet)
|
|
|
|
|
},
|
|
|
|
|
classOf[InternalClusterAction.Welcome] -> {
|
|
|
|
|
case bytes ⇒
|
2013-05-14 21:38:43 +02:00
|
|
|
val m = msg.Welcome.defaultInstance.mergeFrom(decompress(bytes))
|
2013-04-11 09:18:12 +02:00
|
|
|
InternalClusterAction.Welcome(uniqueAddressFromProto(m.from), gossipFromProto(m.gossip))
|
2013-04-04 17:56:29 +02:00
|
|
|
},
|
|
|
|
|
classOf[ClusterUserAction.Leave] -> (bytes ⇒ ClusterUserAction.Leave(addressFromBinary(bytes))),
|
|
|
|
|
classOf[ClusterUserAction.Down] -> (bytes ⇒ ClusterUserAction.Down(addressFromBinary(bytes))),
|
|
|
|
|
InternalClusterAction.InitJoin.getClass -> (_ ⇒ InternalClusterAction.InitJoin),
|
|
|
|
|
classOf[InternalClusterAction.InitJoinAck] -> (bytes ⇒ InternalClusterAction.InitJoinAck(addressFromBinary(bytes))),
|
|
|
|
|
classOf[InternalClusterAction.InitJoinNack] -> (bytes ⇒ InternalClusterAction.InitJoinNack(addressFromBinary(bytes))),
|
|
|
|
|
classOf[ClusterHeartbeatReceiver.Heartbeat] -> (bytes ⇒ ClusterHeartbeatReceiver.Heartbeat(addressFromBinary(bytes))),
|
|
|
|
|
classOf[ClusterHeartbeatReceiver.EndHeartbeat] -> (bytes ⇒ ClusterHeartbeatReceiver.EndHeartbeat(addressFromBinary(bytes))),
|
2013-08-27 15:14:53 +02:00
|
|
|
classOf[ClusterHeartbeatReceiver.EndHeartbeatAck] -> (bytes ⇒ ClusterHeartbeatReceiver.EndHeartbeatAck(addressFromBinary(bytes))),
|
2013-04-04 17:56:29 +02:00
|
|
|
classOf[ClusterHeartbeatSender.HeartbeatRequest] -> (bytes ⇒ ClusterHeartbeatSender.HeartbeatRequest(addressFromBinary(bytes))),
|
2013-04-28 22:28:20 +02:00
|
|
|
classOf[GossipStatus] -> gossipStatusFromBinary,
|
2013-04-04 17:56:29 +02:00
|
|
|
classOf[GossipEnvelope] -> gossipEnvelopeFromBinary,
|
|
|
|
|
classOf[MetricsGossipEnvelope] -> metricsGossipEnvelopeFromBinary)
|
|
|
|
|
|
|
|
|
|
def includeManifest: Boolean = true
|
|
|
|
|
|
|
|
|
|
def identifier = 5
|
|
|
|
|
|
2013-05-14 21:38:43 +02:00
|
|
|
def toBinary(obj: AnyRef): Array[Byte] =
|
|
|
|
|
obj match {
|
2013-06-26 19:23:42 +02:00
|
|
|
case ClusterHeartbeatReceiver.Heartbeat(from) ⇒ addressToProto(from).toByteArray
|
|
|
|
|
case m: GossipEnvelope ⇒ compress(gossipEnvelopeToProto(m))
|
|
|
|
|
case m: GossipStatus ⇒ gossipStatusToProto(m).toByteArray
|
|
|
|
|
case m: MetricsGossipEnvelope ⇒ compress(metricsGossipEnvelopeToProto(m))
|
|
|
|
|
case InternalClusterAction.Join(node, roles) ⇒ msg.Join(uniqueAddressToProto(node), roles.toVector).toByteArray
|
|
|
|
|
case InternalClusterAction.Welcome(from, gossip) ⇒ compress(msg.Welcome(uniqueAddressToProto(from), gossipToProto(gossip)))
|
|
|
|
|
case ClusterUserAction.Leave(address) ⇒ addressToProto(address).toByteArray
|
|
|
|
|
case ClusterUserAction.Down(address) ⇒ addressToProto(address).toByteArray
|
|
|
|
|
case InternalClusterAction.InitJoin ⇒ msg.Empty().toByteArray
|
|
|
|
|
case InternalClusterAction.InitJoinAck(address) ⇒ addressToProto(address).toByteArray
|
|
|
|
|
case InternalClusterAction.InitJoinNack(address) ⇒ addressToProto(address).toByteArray
|
|
|
|
|
case ClusterHeartbeatReceiver.EndHeartbeat(from) ⇒ addressToProto(from).toByteArray
|
2013-08-27 15:14:53 +02:00
|
|
|
case ClusterHeartbeatReceiver.EndHeartbeatAck(from) ⇒ addressToProto(from).toByteArray
|
2013-06-26 19:23:42 +02:00
|
|
|
case ClusterHeartbeatSender.HeartbeatRequest(from) ⇒ addressToProto(from).toByteArray
|
|
|
|
|
case _ ⇒ throw new IllegalArgumentException(s"Can't serialize object of type ${obj.getClass}")
|
2013-05-14 21:38:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def compress(msg: MessageLite): Array[Byte] = {
|
|
|
|
|
val bos = new ByteArrayOutputStream(BufferSize)
|
|
|
|
|
val zip = new GZIPOutputStream(bos)
|
|
|
|
|
msg.writeTo(zip)
|
|
|
|
|
zip.close()
|
|
|
|
|
bos.toByteArray
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def decompress(bytes: Array[Byte]): Array[Byte] = {
|
|
|
|
|
val in = new GZIPInputStream(new ByteArrayInputStream(bytes))
|
|
|
|
|
val out = new ByteArrayOutputStream()
|
|
|
|
|
val buffer = new Array[Byte](BufferSize)
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
@tailrec def readChunk(): Unit = in.read(buffer) match {
|
|
|
|
|
case -1 ⇒ ()
|
|
|
|
|
case n ⇒
|
2013-05-14 21:38:43 +02:00
|
|
|
out.write(buffer, 0, n)
|
|
|
|
|
readChunk()
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
readChunk()
|
2013-05-14 21:38:43 +02:00
|
|
|
out.toByteArray
|
|
|
|
|
}
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef =
|
2013-04-04 17:56:29 +02:00
|
|
|
clazz match {
|
|
|
|
|
case Some(c) ⇒ fromBinaryMap.get(c.asInstanceOf[Class[ClusterMessage]]) match {
|
|
|
|
|
case Some(f) ⇒ f(bytes)
|
2013-06-26 19:23:42 +02:00
|
|
|
case None ⇒ throw new IllegalArgumentException(s"Unimplemented deserialization of message class $c in ClusterSerializer")
|
2013-04-04 17:56:29 +02:00
|
|
|
}
|
|
|
|
|
case _ ⇒ throw new IllegalArgumentException("Need a cluster message class to be able to deserialize bytes in ClusterSerializer")
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def addressFromBinary(bytes: Array[Byte]): Address =
|
2013-04-04 17:56:29 +02:00
|
|
|
addressFromProto(msg.Address.defaultInstance.mergeFrom(bytes))
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def uniqueAddressFromBinary(bytes: Array[Byte]): UniqueAddress =
|
2013-04-11 09:18:12 +02:00
|
|
|
uniqueAddressFromProto(msg.UniqueAddress.defaultInstance.mergeFrom(bytes))
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def addressToProto(address: Address): msg.Address =
|
2013-04-04 17:56:29 +02:00
|
|
|
msg.Address(address.system, address.host.getOrElse(""), address.port.getOrElse(0), Some(address.protocol))
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def uniqueAddressToProto(uniqueAddress: UniqueAddress): msg.UniqueAddress =
|
2013-04-11 09:18:12 +02:00
|
|
|
msg.UniqueAddress(addressToProto(uniqueAddress.address), uniqueAddress.uid)
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def addressFromProto(address: msg.Address): Address =
|
2013-04-04 17:56:29 +02:00
|
|
|
Address(address.protocol.getOrElse(""), address.system, address.hostname, address.port)
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def uniqueAddressFromProto(uniqueAddress: msg.UniqueAddress): UniqueAddress =
|
2013-04-11 09:18:12 +02:00
|
|
|
UniqueAddress(addressFromProto(uniqueAddress.address), uniqueAddress.uid)
|
|
|
|
|
|
2013-04-04 17:56:29 +02:00
|
|
|
private val memberStatusToInt = scala.collection.immutable.HashMap[MemberStatus, Int](
|
|
|
|
|
MemberStatus.Joining -> msg.MemberStatus.Joining_VALUE,
|
|
|
|
|
MemberStatus.Up -> msg.MemberStatus.Up_VALUE,
|
|
|
|
|
MemberStatus.Leaving -> msg.MemberStatus.Leaving_VALUE,
|
|
|
|
|
MemberStatus.Exiting -> msg.MemberStatus.Exiting_VALUE,
|
|
|
|
|
MemberStatus.Down -> msg.MemberStatus.Down_VALUE,
|
|
|
|
|
MemberStatus.Removed -> msg.MemberStatus.Removed_VALUE)
|
|
|
|
|
|
|
|
|
|
private val memberStatusFromInt = memberStatusToInt.map { case (a, b) ⇒ (b, a) }
|
|
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
private val reachabilityStatusToInt = scala.collection.immutable.HashMap[Reachability.ReachabilityStatus, Int](
|
|
|
|
|
Reachability.Reachable -> msg.ReachabilityStatus.Reachable_VALUE,
|
|
|
|
|
Reachability.Unreachable -> msg.ReachabilityStatus.Unreachable_VALUE,
|
|
|
|
|
Reachability.Terminated -> msg.ReachabilityStatus.Terminated_VALUE)
|
|
|
|
|
|
|
|
|
|
private val reachabilityStatusFromInt = reachabilityStatusToInt.map { case (a, b) ⇒ (b, a) }
|
|
|
|
|
|
2013-04-04 17:56:29 +02:00
|
|
|
private def mapWithErrorMessage[T](map: Map[T, Int], value: T, unknown: String): Int = map.get(value) match {
|
|
|
|
|
case Some(x) ⇒ x
|
|
|
|
|
case _ ⇒ throw new IllegalArgumentException(s"Unknown ${unknown} [${value}] in cluster message")
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 09:18:12 +02:00
|
|
|
private def gossipToProto(gossip: Gossip): msg.Gossip = {
|
2013-06-26 19:23:42 +02:00
|
|
|
import scala.collection.breakOut
|
2013-08-27 15:14:53 +02:00
|
|
|
val allMembers = gossip.members.toVector
|
|
|
|
|
val allAddresses: Vector[UniqueAddress] = allMembers.map(_.uniqueAddress)
|
2013-04-04 17:56:29 +02:00
|
|
|
val addressMapping = allAddresses.zipWithIndex.toMap
|
2013-04-28 22:59:28 +02:00
|
|
|
val allRoles = allMembers.foldLeft(Set.empty[String])((acc, m) ⇒ acc ++ m.roles).to[Vector]
|
2013-04-04 17:56:29 +02:00
|
|
|
val roleMapping = allRoles.zipWithIndex.toMap
|
2013-09-02 13:50:46 +02:00
|
|
|
val allHashes = gossip.version.versions.keys.to[Vector]
|
2013-04-04 17:56:29 +02:00
|
|
|
val hashMapping = allHashes.zipWithIndex.toMap
|
|
|
|
|
|
2013-04-11 09:18:12 +02:00
|
|
|
def mapUniqueAddress(uniqueAddress: UniqueAddress) = mapWithErrorMessage(addressMapping, uniqueAddress, "address")
|
2013-04-04 17:56:29 +02:00
|
|
|
def mapRole(role: String) = mapWithErrorMessage(roleMapping, role, "role")
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def memberToProto(member: Member) =
|
2013-04-28 22:05:40 +02:00
|
|
|
msg.Member(mapUniqueAddress(member.uniqueAddress), member.upNumber,
|
2013-06-26 19:23:42 +02:00
|
|
|
msg.MemberStatus.valueOf(memberStatusToInt(member.status)), member.roles.map(mapRole)(breakOut))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
def reachabilityToProto(reachability: Reachability): Vector[msg.ObserverReachability] = {
|
|
|
|
|
reachability.versions.map {
|
|
|
|
|
case (observer, version) ⇒
|
|
|
|
|
val subjectReachability = reachability.recordsFrom(observer).map(r ⇒
|
|
|
|
|
msg.SubjectReachability(mapUniqueAddress(r.subject),
|
|
|
|
|
msg.ReachabilityStatus.valueOf(reachabilityStatusToInt(r.status)), r.version))
|
|
|
|
|
msg.ObserverReachability(mapUniqueAddress(observer), version, subjectReachability)
|
|
|
|
|
}(breakOut)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val reachability = reachabilityToProto(gossip.overview.reachability)
|
2013-06-26 19:23:42 +02:00
|
|
|
val members: Vector[msg.Member] = gossip.members.map(memberToProto)(breakOut)
|
2013-09-02 13:50:46 +02:00
|
|
|
val seen: Vector[Int] = gossip.overview.seen.map(mapUniqueAddress)(breakOut)
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
val overview = msg.GossipOverview(seen, reachability)
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-04-11 09:18:12 +02:00
|
|
|
msg.Gossip(allAddresses.map(uniqueAddressToProto),
|
2013-04-28 22:28:20 +02:00
|
|
|
allRoles, allHashes, members, overview, vectorClockToProto(gossip.version, hashMapping))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def vectorClockToProto(version: VectorClock, hashMapping: Map[String, Int]): msg.VectorClock = {
|
2013-06-26 19:23:42 +02:00
|
|
|
val versions: Vector[msg.VectorClock.Version] = version.versions.map({
|
2013-09-10 17:54:48 +02:00
|
|
|
case (n, t) ⇒ msg.VectorClock.Version(mapWithErrorMessage(hashMapping, n, "hash"), t)
|
2013-06-26 19:23:42 +02:00
|
|
|
})(breakOut)
|
|
|
|
|
msg.VectorClock(None, versions)
|
2013-04-11 09:18:12 +02:00
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def gossipEnvelopeToProto(envelope: GossipEnvelope): msg.GossipEnvelope =
|
|
|
|
|
msg.GossipEnvelope(uniqueAddressToProto(envelope.from), uniqueAddressToProto(envelope.to), gossipToProto(envelope.gossip))
|
2013-04-28 22:28:20 +02:00
|
|
|
|
|
|
|
|
private def gossipStatusToProto(status: GossipStatus): msg.GossipStatus = {
|
2013-06-26 19:23:42 +02:00
|
|
|
val allHashes = status.version.versions.keys.toVector
|
2013-04-28 22:28:20 +02:00
|
|
|
val hashMapping = allHashes.zipWithIndex.toMap
|
|
|
|
|
msg.GossipStatus(uniqueAddressToProto(status.from), allHashes, vectorClockToProto(status.version, hashMapping))
|
2013-04-04 17:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def gossipEnvelopeFromBinary(bytes: Array[Byte]): GossipEnvelope =
|
2013-05-14 21:38:43 +02:00
|
|
|
gossipEnvelopeFromProto(msg.GossipEnvelope.defaultInstance.mergeFrom(decompress(bytes)))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def gossipStatusFromBinary(bytes: Array[Byte]): GossipStatus =
|
2013-04-28 22:28:20 +02:00
|
|
|
gossipStatusFromProto(msg.GossipStatus.defaultInstance.mergeFrom(bytes))
|
|
|
|
|
|
2013-04-11 09:18:12 +02:00
|
|
|
private def gossipFromProto(gossip: msg.Gossip): Gossip = {
|
2013-06-26 19:23:42 +02:00
|
|
|
import scala.collection.breakOut
|
2013-04-11 09:18:12 +02:00
|
|
|
val addressMapping = gossip.allAddresses.map(uniqueAddressFromProto)
|
2013-04-04 17:56:29 +02:00
|
|
|
val roleMapping = gossip.allRoles
|
|
|
|
|
val hashMapping = gossip.allHashes
|
|
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
def reachabilityFromProto(observerReachability: immutable.Seq[msg.ObserverReachability]): Reachability = {
|
|
|
|
|
val recordBuilder = new immutable.VectorBuilder[Reachability.Record]
|
|
|
|
|
val versionsBuilder = new scala.collection.mutable.MapBuilder[UniqueAddress, Long, Map[UniqueAddress, Long]](Map.empty)
|
|
|
|
|
for (o ← observerReachability) {
|
|
|
|
|
val observer = addressMapping(o.addressIndex)
|
|
|
|
|
versionsBuilder += ((observer, o.version))
|
|
|
|
|
for (s ← o.subjectReachability) {
|
|
|
|
|
val subject = addressMapping(s.addressIndex)
|
|
|
|
|
val record = Reachability.Record(observer, subject, reachabilityStatusFromInt(s.status), s.version)
|
|
|
|
|
recordBuilder += record
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Reachability.create(recordBuilder.result(), versionsBuilder.result())
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def memberFromProto(member: msg.Member) =
|
2013-04-28 22:05:40 +02:00
|
|
|
new Member(addressMapping(member.addressIndex), member.upNumber, memberStatusFromInt(member.status.id),
|
2013-06-26 19:23:42 +02:00
|
|
|
member.rolesIndexes.map(roleMapping)(breakOut))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
val members: immutable.SortedSet[Member] = gossip.members.map(memberFromProto)(breakOut)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
|
|
|
|
val reachability = reachabilityFromProto(gossip.overview.observerReachability)
|
2013-09-02 13:50:46 +02:00
|
|
|
val seen: Set[UniqueAddress] = gossip.overview.seen.map(addressMapping)(breakOut)
|
2013-08-27 15:14:53 +02:00
|
|
|
val overview = GossipOverview(seen, reachability)
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-04-28 22:28:20 +02:00
|
|
|
Gossip(members, overview, vectorClockFromProto(gossip.version, hashMapping))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def vectorClockFromProto(version: msg.VectorClock, hashMapping: immutable.Seq[String]) = {
|
2013-06-13 15:43:37 -04:00
|
|
|
VectorClock(version.versions.map({
|
2013-09-10 17:54:48 +02:00
|
|
|
case msg.VectorClock.Version(h, t) ⇒ (VectorClock.Node.fromHash(hashMapping(h)), t)
|
2013-06-26 19:23:42 +02:00
|
|
|
})(breakOut))
|
2013-04-11 09:18:12 +02:00
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def gossipEnvelopeFromProto(envelope: msg.GossipEnvelope): GossipEnvelope =
|
|
|
|
|
GossipEnvelope(uniqueAddressFromProto(envelope.from), uniqueAddressFromProto(envelope.to), gossipFromProto(envelope.gossip))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def gossipStatusFromProto(status: msg.GossipStatus): GossipStatus =
|
|
|
|
|
GossipStatus(uniqueAddressFromProto(status.from), vectorClockFromProto(status.version, status.allHashes))
|
2013-04-28 22:28:20 +02:00
|
|
|
|
2013-04-04 17:56:29 +02:00
|
|
|
private def metricsGossipEnvelopeToProto(envelope: MetricsGossipEnvelope): msg.MetricsGossipEnvelope = {
|
|
|
|
|
val mgossip = envelope.gossip
|
|
|
|
|
val allAddresses = mgossip.nodes.foldLeft(Set.empty[Address])((s, n) ⇒ s + n.address).to[Vector]
|
|
|
|
|
val addressMapping = allAddresses.zipWithIndex.toMap
|
2013-06-26 19:23:42 +02:00
|
|
|
val allMetricNames = mgossip.nodes.foldLeft(Set.empty[String])((s, n) ⇒ s ++ n.metrics.iterator.map(_.name)).to[Vector]
|
2013-04-04 17:56:29 +02:00
|
|
|
val metricNamesMapping = allMetricNames.zipWithIndex.toMap
|
|
|
|
|
|
|
|
|
|
def mapAddress(address: Address) = mapWithErrorMessage(addressMapping, address, "address")
|
|
|
|
|
def mapName(name: String) = mapWithErrorMessage(metricNamesMapping, name, "address")
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def ewmaToProto(ewma: Option[EWMA]): Option[msg.NodeMetrics.EWMA] = ewma.map(x ⇒ msg.NodeMetrics.EWMA(x.value, x.alpha))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
|
|
|
|
def numberToProto(number: Number): msg.NodeMetrics.Number = {
|
|
|
|
|
import msg.NodeMetrics.Number
|
|
|
|
|
import msg.NodeMetrics.NumberType
|
|
|
|
|
number match {
|
|
|
|
|
case n: jl.Double ⇒ Number(NumberType.Double, None, Some(jl.Double.doubleToLongBits(n)), None)
|
|
|
|
|
case n: jl.Long ⇒ Number(NumberType.Long, None, Some(n), None)
|
|
|
|
|
case n: jl.Float ⇒ Number(NumberType.Float, Some(jl.Float.floatToIntBits(n)), None, None)
|
|
|
|
|
case n: jl.Integer ⇒ Number(NumberType.Integer, Some(n), None, None)
|
|
|
|
|
case _ ⇒
|
|
|
|
|
val bos = new ByteArrayOutputStream
|
|
|
|
|
val out = new ObjectOutputStream(bos)
|
|
|
|
|
out.writeObject(number)
|
|
|
|
|
out.close()
|
2013-06-26 19:23:42 +02:00
|
|
|
msg.NodeMetrics.Number(msg.NodeMetrics.NumberType.Serialized, None, None, Some(ByteString.copyFrom(bos.toByteArray)))
|
2013-04-04 17:56:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def metricToProto(metric: Metric): msg.NodeMetrics.Metric =
|
2013-04-04 17:56:29 +02:00
|
|
|
msg.NodeMetrics.Metric(mapName(metric.name), numberToProto(metric.value), ewmaToProto(metric.average))
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def nodeMetricsToProto(nodeMetrics: NodeMetrics): msg.NodeMetrics =
|
|
|
|
|
msg.NodeMetrics(mapAddress(nodeMetrics.address), nodeMetrics.timestamp, nodeMetrics.metrics.map(metricToProto)(breakOut))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
val nodeMetrics: Vector[msg.NodeMetrics] = mgossip.nodes.map(nodeMetricsToProto)(breakOut)
|
2013-04-04 17:56:29 +02:00
|
|
|
|
|
|
|
|
msg.MetricsGossipEnvelope(addressToProto(envelope.from),
|
|
|
|
|
msg.MetricsGossip(allAddresses.map(addressToProto), allMetricNames, nodeMetrics), envelope.reply)
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
private def metricsGossipEnvelopeFromBinary(bytes: Array[Byte]): MetricsGossipEnvelope =
|
2013-05-14 21:38:43 +02:00
|
|
|
metricsGossipEnvelopeFromProto(msg.MetricsGossipEnvelope.defaultInstance.mergeFrom(decompress(bytes)))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
|
|
|
|
private def metricsGossipEnvelopeFromProto(envelope: msg.MetricsGossipEnvelope): MetricsGossipEnvelope = {
|
|
|
|
|
val mgossip = envelope.gossip
|
|
|
|
|
val addressMapping = mgossip.allAddresses.map(addressFromProto)
|
|
|
|
|
val metricNameMapping = mgossip.allMetricNames
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def ewmaFromProto(ewma: Option[msg.NodeMetrics.EWMA]): Option[EWMA] = ewma.map(x ⇒ EWMA(x.value, x.alpha))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
|
|
|
|
def numberFromProto(number: msg.NodeMetrics.Number): Number = {
|
|
|
|
|
import msg.NodeMetrics.Number
|
|
|
|
|
import msg.NodeMetrics.NumberType
|
|
|
|
|
number match {
|
|
|
|
|
case Number(NumberType.Double, _, Some(n), _) ⇒ jl.Double.longBitsToDouble(n)
|
|
|
|
|
case Number(NumberType.Long, _, Some(n), _) ⇒ n
|
|
|
|
|
case Number(NumberType.Float, Some(n), _, _) ⇒ jl.Float.intBitsToFloat(n)
|
|
|
|
|
case Number(NumberType.Integer, Some(n), _, _) ⇒ n
|
|
|
|
|
case Number(NumberType.Serialized, _, _, Some(b)) ⇒
|
2013-06-26 19:23:42 +02:00
|
|
|
val in = new ClassLoaderObjectInputStream(system.dynamicAccess.classLoader, new ByteArrayInputStream(b.toByteArray))
|
2013-04-04 17:56:29 +02:00
|
|
|
val obj = in.readObject
|
|
|
|
|
in.close()
|
|
|
|
|
obj.asInstanceOf[jl.Number]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def metricFromProto(metric: msg.NodeMetrics.Metric): Metric =
|
2013-04-04 17:56:29 +02:00
|
|
|
Metric(metricNameMapping(metric.nameIndex), numberFromProto(metric.number), ewmaFromProto(metric.ewma))
|
|
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
def nodeMetricsFromProto(nodeMetrics: msg.NodeMetrics): NodeMetrics =
|
|
|
|
|
NodeMetrics(addressMapping(nodeMetrics.addressIndex), nodeMetrics.timestamp, nodeMetrics.metrics.map(metricFromProto)(breakOut))
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
val nodeMetrics: Set[NodeMetrics] = mgossip.nodeMetrics.map(nodeMetricsFromProto)(breakOut)
|
2013-04-04 17:56:29 +02:00
|
|
|
|
2013-06-26 19:23:42 +02:00
|
|
|
MetricsGossipEnvelope(addressFromProto(envelope.from), MetricsGossip(nodeMetrics), envelope.reply)
|
2013-04-04 17:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|