make pruning of CRDT garbage work, #21647

* fix merge issues of DataEnvelope and its pruning
* simplify by removing the tombstones, which didn't work in all cases anyway
* keep the PruningPerformed markers in the DataEnvelope until configured
  TTL has elapsed (wall clock)
* simplify PruningState structure
* also store the pruning markers in durable data
* collect removed nodes from the data, listing on MemberRemoved is not enough
* possibility to disable pruning altogether
* documented caveat for durable data
This commit is contained in:
Patrik Nordwall 2017-01-11 13:19:45 +01:00
parent c5d18c30d6
commit 952be31a7d
28 changed files with 951 additions and 229 deletions

View file

@ -11,36 +11,37 @@ import akka.cluster.UniqueAddress
* INTERNAL API
*/
private[akka] object PruningState {
sealed trait PruningPhase
final case class PruningInitialized(seen: Set[Address]) extends PruningPhase
case object PruningPerformed extends PruningPhase
final case class PruningInitialized(owner: UniqueAddress, seen: Set[Address]) extends PruningState {
override def addSeen(node: Address): PruningState = {
if (seen(node) || owner.address == node) this
else copy(seen = seen + node)
}
}
final case class PruningPerformed(obsoleteTime: Long) extends PruningState {
def isObsolete(currentTime: Long): Boolean = obsoleteTime <= currentTime
}
}
/**
* INTERNAL API
*/
private[akka] final case class PruningState(owner: UniqueAddress, phase: PruningState.PruningPhase) {
private[akka] sealed trait PruningState {
import PruningState._
def merge(that: PruningState): PruningState =
(this.phase, that.phase) match {
// FIXME this will add the PruningPerformed back again when one is None
case (PruningPerformed, _) this
case (_, PruningPerformed) that
case (PruningInitialized(thisSeen), PruningInitialized(thatSeen))
if (this.owner == that.owner)
copy(phase = PruningInitialized(thisSeen union thatSeen))
else if (Member.addressOrdering.compare(this.owner.address, that.owner.address) > 0)
(this, that) match {
case (p1: PruningPerformed, p2: PruningPerformed) if (p1.obsoleteTime >= p2.obsoleteTime) this else that
case (_: PruningPerformed, _) this
case (_, _: PruningPerformed) that
case (PruningInitialized(thisOwner, thisSeen), PruningInitialized(thatOwner, thatSeen))
if (thisOwner == thatOwner)
PruningInitialized(thisOwner, thisSeen union thatSeen)
else if (Member.addressOrdering.compare(thisOwner.address, thatOwner.address) > 0)
that
else
this
}
def addSeen(node: Address): PruningState = phase match {
case PruningInitialized(seen)
if (seen(node) || owner.address == node) this
else copy(phase = PruningInitialized(seen + node))
case _ this
}
def addSeen(node: Address): PruningState = this
}