2015-09-20 14:30:00 +02:00
|
|
|
/**
|
2016-01-25 10:16:14 +01:00
|
|
|
* Copyright (C) 2015-2016 Typesafe Inc. <http://www.typesafe.com>
|
2015-09-20 14:30:00 +02:00
|
|
|
*/
|
|
|
|
|
package akka.cluster.ddata
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*
|
|
|
|
|
* Optimization for add/remove followed by merge and merge should just fast forward to
|
|
|
|
|
* the new instance.
|
|
|
|
|
*
|
|
|
|
|
* It's like a cache between calls of the same thread, you can think of it as a thread local.
|
|
|
|
|
* The Replicator actor invokes the user's modify function, which returns a new ReplicatedData instance,
|
|
|
|
|
* with the ancestor field set (see for example the add method in ORSet). Then (in same thread) the
|
|
|
|
|
* Replication calls merge, which makes use of the ancestor field to perform quick merge
|
|
|
|
|
* (see for example merge method in ORSet).
|
|
|
|
|
*
|
|
|
|
|
* It's not thread safe if the modifying function and merge are called from different threads,
|
|
|
|
|
* i.e. if used outside the Replicator infrastructure, but the worst thing that can happen is that
|
|
|
|
|
* a full merge is performed instead of the fast forward merge.
|
|
|
|
|
*/
|
|
|
|
|
private[akka] trait FastMerge { self: ReplicatedData ⇒
|
|
|
|
|
|
|
|
|
|
private var ancestor: FastMerge = null
|
|
|
|
|
|
|
|
|
|
/** INTERNAL API: should be called from "updating" methods */
|
|
|
|
|
private[akka] def assignAncestor(newData: T with FastMerge): T = {
|
|
|
|
|
newData.ancestor = if (this.ancestor eq null) this else this.ancestor
|
|
|
|
|
this.ancestor = null // only one level, for GC
|
|
|
|
|
newData
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** INTERNAL API: should be used from merge */
|
|
|
|
|
private[akka] def isAncestorOf(that: T with FastMerge): Boolean =
|
|
|
|
|
that.ancestor eq this
|
|
|
|
|
|
|
|
|
|
/** INTERNAL API: should be called from merge */
|
|
|
|
|
private[akka] def clearAncestor(): self.type = {
|
|
|
|
|
ancestor = null
|
|
|
|
|
this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|