#22035 Make it possible to use anything as the key in a map
- All Map types are now generic in their key: ORMap, ORMultiMap, LWWMap, PNCounterMap - test for binary compatibility with previous version for serialization - entries are sorted for deterministic SHA-1 on same value
This commit is contained in:
parent
5c79b81e92
commit
8499ff6faf
28 changed files with 2231 additions and 584 deletions
|
|
@ -7,29 +7,29 @@ import akka.cluster.{ UniqueAddress, Cluster }
|
|||
|
||||
object ORMultiMap {
|
||||
|
||||
val _empty: ORMultiMap[Any] = new ORMultiMap(ORMap.empty)
|
||||
val _empty: ORMultiMap[Any, Any] = new ORMultiMap(ORMap.empty)
|
||||
/**
|
||||
* Provides an empty multimap.
|
||||
*/
|
||||
def empty[A]: ORMultiMap[A] = _empty.asInstanceOf[ORMultiMap[A]]
|
||||
def apply(): ORMultiMap[Any] = _empty
|
||||
def empty[A, B]: ORMultiMap[A, B] = _empty.asInstanceOf[ORMultiMap[A, B]]
|
||||
def apply(): ORMultiMap[Any, Any] = _empty
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*/
|
||||
def create[A](): ORMultiMap[A] = empty[A]
|
||||
def create[A, B](): ORMultiMap[A, B] = empty[A, B]
|
||||
|
||||
/**
|
||||
* Extract the [[ORMultiMap#entries]].
|
||||
*/
|
||||
def unapply[A](m: ORMultiMap[A]): Option[Map[String, Set[A]]] = Some(m.entries)
|
||||
def unapply[A, B](m: ORMultiMap[A, B]): Option[Map[A, Set[B]]] = Some(m.entries)
|
||||
|
||||
/**
|
||||
* Extract the [[ORMultiMap#entries]] of an `ORMultiMap`.
|
||||
*/
|
||||
def unapply(value: Any): Option[Map[String, Set[Any]]] = value match {
|
||||
case m: ORMultiMap[Any] @unchecked ⇒ Some(m.entries)
|
||||
case _ ⇒ None
|
||||
def unapply[A, B <: ReplicatedData](value: Any): Option[Map[A, Set[B]]] = value match {
|
||||
case m: ORMultiMap[A, B] @unchecked ⇒ Some(m.entries)
|
||||
case _ ⇒ None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -40,10 +40,10 @@ object ORMultiMap {
|
|||
* This class is immutable, i.e. "modifying" methods return a new instance.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORSet[A]])
|
||||
final class ORMultiMap[A, B] private[akka] (private[akka] val underlying: ORMap[A, ORSet[B]])
|
||||
extends ReplicatedData with ReplicatedDataSerialization with RemovedNodePruning {
|
||||
|
||||
override type T = ORMultiMap[A]
|
||||
override type T = ORMultiMap[A, B]
|
||||
|
||||
override def merge(that: T): T =
|
||||
new ORMultiMap(underlying.merge(that.underlying))
|
||||
|
|
@ -51,15 +51,15 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
/**
|
||||
* Scala API: All entries of a multimap where keys are strings and values are sets.
|
||||
*/
|
||||
def entries: Map[String, Set[A]] =
|
||||
def entries: Map[A, Set[B]] =
|
||||
underlying.entries.map { case (k, v) ⇒ k → v.elements }
|
||||
|
||||
/**
|
||||
* Java API: All entries of a multimap where keys are strings and values are sets.
|
||||
*/
|
||||
def getEntries(): java.util.Map[String, java.util.Set[A]] = {
|
||||
def getEntries(): java.util.Map[A, java.util.Set[B]] = {
|
||||
import scala.collection.JavaConverters._
|
||||
val result = new java.util.HashMap[String, java.util.Set[A]]
|
||||
val result = new java.util.HashMap[A, java.util.Set[B]]
|
||||
underlying.entries.foreach {
|
||||
case (k, v) ⇒ result.put(k, v.elements.asJava)
|
||||
}
|
||||
|
|
@ -69,17 +69,17 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
/**
|
||||
* Get the set associated with the key if there is one.
|
||||
*/
|
||||
def get(key: String): Option[Set[A]] =
|
||||
def get(key: A): Option[Set[B]] =
|
||||
underlying.get(key).map(_.elements)
|
||||
|
||||
/**
|
||||
* Scala API: Get the set associated with the key if there is one,
|
||||
* else return the given default.
|
||||
*/
|
||||
def getOrElse(key: String, default: ⇒ Set[A]): Set[A] =
|
||||
def getOrElse(key: A, default: ⇒ Set[B]): Set[B] =
|
||||
get(key).getOrElse(default)
|
||||
|
||||
def contains(key: String): Boolean = underlying.contains(key)
|
||||
def contains(key: A): Boolean = underlying.contains(key)
|
||||
|
||||
def isEmpty: Boolean = underlying.isEmpty
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
* Convenience for put. Requires an implicit Cluster.
|
||||
* @see [[#put]]
|
||||
*/
|
||||
def +(entry: (String, Set[A]))(implicit node: Cluster): ORMultiMap[A] = {
|
||||
def +(entry: (A, Set[B]))(implicit node: Cluster): ORMultiMap[A, B] = {
|
||||
val (key, value) = entry
|
||||
put(node, key, value)
|
||||
}
|
||||
|
|
@ -98,14 +98,14 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
* Scala API: Associate an entire set with the key while retaining the history of the previous
|
||||
* replicated data set.
|
||||
*/
|
||||
def put(node: Cluster, key: String, value: Set[A]): ORMultiMap[A] =
|
||||
def put(node: Cluster, key: A, value: Set[B]): ORMultiMap[A, B] =
|
||||
put(node.selfUniqueAddress, key, value)
|
||||
|
||||
/**
|
||||
* Java API: Associate an entire set with the key while retaining the history of the previous
|
||||
* replicated data set.
|
||||
*/
|
||||
def put(node: Cluster, key: String, value: java.util.Set[A]): ORMultiMap[A] = {
|
||||
def put(node: Cluster, key: A, value: java.util.Set[B]): ORMultiMap[A, B] = {
|
||||
import scala.collection.JavaConverters._
|
||||
put(node, key, value.asScala.toSet)
|
||||
}
|
||||
|
|
@ -113,8 +113,8 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def put(node: UniqueAddress, key: String, value: Set[A]): ORMultiMap[A] = {
|
||||
val newUnderlying = underlying.updated(node, key, ORSet.empty[A]) { existing ⇒
|
||||
private[akka] def put(node: UniqueAddress, key: A, value: Set[B]): ORMultiMap[A, B] = {
|
||||
val newUnderlying = underlying.updated(node, key, ORSet.empty[B]) { existing ⇒
|
||||
value.foldLeft(existing.clear(node)) { (s, element) ⇒ s.add(node, element) }
|
||||
}
|
||||
new ORMultiMap(newUnderlying)
|
||||
|
|
@ -124,38 +124,38 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
* Convenience for remove. Requires an implicit Cluster.
|
||||
* @see [[#remove]]
|
||||
*/
|
||||
def -(key: String)(implicit node: Cluster): ORMultiMap[A] =
|
||||
def -(key: A)(implicit node: Cluster): ORMultiMap[A, B] =
|
||||
remove(node, key)
|
||||
|
||||
/**
|
||||
* Remove an entire set associated with the key.
|
||||
*/
|
||||
def remove(node: Cluster, key: String): ORMultiMap[A] =
|
||||
def remove(node: Cluster, key: A): ORMultiMap[A, B] =
|
||||
remove(node.selfUniqueAddress, key)
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def remove(node: UniqueAddress, key: String): ORMultiMap[A] =
|
||||
private[akka] def remove(node: UniqueAddress, key: A): ORMultiMap[A, B] =
|
||||
new ORMultiMap(underlying.remove(node, key))
|
||||
|
||||
/**
|
||||
* Scala API: Add an element to a set associated with a key. If there is no existing set then one will be initialised.
|
||||
*/
|
||||
def addBinding(key: String, element: A)(implicit node: Cluster): ORMultiMap[A] =
|
||||
def addBinding(key: A, element: B)(implicit node: Cluster): ORMultiMap[A, B] =
|
||||
addBinding(node.selfUniqueAddress, key, element)
|
||||
|
||||
/**
|
||||
* Java API: Add an element to a set associated with a key. If there is no existing set then one will be initialised.
|
||||
*/
|
||||
def addBinding(node: Cluster, key: String, element: A): ORMultiMap[A] =
|
||||
def addBinding(node: Cluster, key: A, element: B): ORMultiMap[A, B] =
|
||||
addBinding(key, element)(node)
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def addBinding(node: UniqueAddress, key: String, element: A): ORMultiMap[A] = {
|
||||
val newUnderlying = underlying.updated(node, key, ORSet.empty[A])(_.add(node, element))
|
||||
private[akka] def addBinding(node: UniqueAddress, key: A, element: B): ORMultiMap[A, B] = {
|
||||
val newUnderlying = underlying.updated(node, key, ORSet.empty[B])(_.add(node, element))
|
||||
new ORMultiMap(newUnderlying)
|
||||
}
|
||||
|
||||
|
|
@ -163,22 +163,22 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
* Scala API: Remove an element of a set associated with a key. If there are no more elements in the set then the
|
||||
* entire set will be removed.
|
||||
*/
|
||||
def removeBinding(key: String, element: A)(implicit node: Cluster): ORMultiMap[A] =
|
||||
def removeBinding(key: A, element: B)(implicit node: Cluster): ORMultiMap[A, B] =
|
||||
removeBinding(node.selfUniqueAddress, key, element)
|
||||
|
||||
/**
|
||||
* Java API: Remove an element of a set associated with a key. If there are no more elements in the set then the
|
||||
* entire set will be removed.
|
||||
*/
|
||||
def removeBinding(node: Cluster, key: String, element: A): ORMultiMap[A] =
|
||||
def removeBinding(node: Cluster, key: A, element: B): ORMultiMap[A, B] =
|
||||
removeBinding(key, element)(node)
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def removeBinding(node: UniqueAddress, key: String, element: A): ORMultiMap[A] = {
|
||||
private[akka] def removeBinding(node: UniqueAddress, key: A, element: B): ORMultiMap[A, B] = {
|
||||
val newUnderlying = {
|
||||
val u = underlying.updated(node, key, ORSet.empty[A])(_.remove(node, element))
|
||||
val u = underlying.updated(node, key, ORSet.empty[B])(_.remove(node, element))
|
||||
u.get(key) match {
|
||||
case Some(s) if s.isEmpty ⇒ u.remove(node, key)
|
||||
case _ ⇒ u
|
||||
|
|
@ -192,13 +192,13 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
* and another one is added within the same Update. The order of addition and removal is important in order
|
||||
* to retain history for replicated data.
|
||||
*/
|
||||
def replaceBinding(key: String, oldElement: A, newElement: A)(implicit node: Cluster): ORMultiMap[A] =
|
||||
def replaceBinding(key: A, oldElement: B, newElement: B)(implicit node: Cluster): ORMultiMap[A, B] =
|
||||
replaceBinding(node.selfUniqueAddress, key, oldElement, newElement)
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def replaceBinding(node: UniqueAddress, key: String, oldElement: A, newElement: A): ORMultiMap[A] =
|
||||
private[akka] def replaceBinding(node: UniqueAddress, key: A, oldElement: B, newElement: B): ORMultiMap[A, B] =
|
||||
if (newElement != oldElement)
|
||||
addBinding(node, key, newElement).removeBinding(node, key, oldElement)
|
||||
else
|
||||
|
|
@ -218,16 +218,16 @@ final class ORMultiMap[A] private[akka] (private[akka] val underlying: ORMap[ORS
|
|||
override def toString: String = s"ORMulti$entries"
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case other: ORMultiMap[_] ⇒ underlying == other.underlying
|
||||
case _ ⇒ false
|
||||
case other: ORMultiMap[_, _] ⇒ underlying == other.underlying
|
||||
case _ ⇒ false
|
||||
}
|
||||
|
||||
override def hashCode: Int = underlying.hashCode
|
||||
}
|
||||
|
||||
object ORMultiMapKey {
|
||||
def create[A](id: String): Key[ORMultiMap[A]] = ORMultiMapKey(id)
|
||||
def create[A, B](id: String): Key[ORMultiMap[A, B]] = ORMultiMapKey(id)
|
||||
}
|
||||
|
||||
@SerialVersionUID(1L)
|
||||
final case class ORMultiMapKey[A](_id: String) extends Key[ORMultiMap[A]](_id) with ReplicatedDataSerialization
|
||||
final case class ORMultiMapKey[A, B](_id: String) extends Key[ORMultiMap[A, B]](_id) with ReplicatedDataSerialization
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue