2018-10-29 17:19:37 +08:00
|
|
|
/*
|
2019-01-02 18:55:26 +08:00
|
|
|
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
2013-08-27 15:14:53 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka.cluster
|
|
|
|
|
|
|
|
|
|
import org.scalatest.WordSpec
|
2013-12-17 14:25:56 +01:00
|
|
|
import org.scalatest.Matchers
|
2013-08-27 15:14:53 +02:00
|
|
|
import akka.actor.Address
|
|
|
|
|
|
2013-12-17 14:25:56 +01:00
|
|
|
class ReachabilitySpec extends WordSpec with Matchers {
|
2013-08-27 15:14:53 +02:00
|
|
|
|
|
|
|
|
import Reachability.{ Reachable, Unreachable, Terminated, Record }
|
|
|
|
|
|
2016-12-01 18:49:38 +01:00
|
|
|
val nodeA = UniqueAddress(Address("akka.tcp", "sys", "a", 2552), 1L)
|
|
|
|
|
val nodeB = UniqueAddress(Address("akka.tcp", "sys", "b", 2552), 2L)
|
|
|
|
|
val nodeC = UniqueAddress(Address("akka.tcp", "sys", "c", 2552), 3L)
|
|
|
|
|
val nodeD = UniqueAddress(Address("akka.tcp", "sys", "d", 2552), 4L)
|
|
|
|
|
val nodeE = UniqueAddress(Address("akka.tcp", "sys", "e", 2552), 5L)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
|
|
|
|
"Reachability table" must {
|
|
|
|
|
|
|
|
|
|
"be reachable when empty" in {
|
|
|
|
|
val r = Reachability.empty
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(true)
|
|
|
|
|
r.allUnreachable should ===(Set.empty)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be unreachable when one observed unreachable" in {
|
|
|
|
|
val r = Reachability.empty.unreachable(nodeB, nodeA)
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(false)
|
|
|
|
|
r.allUnreachable should ===(Set(nodeA))
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"not be reachable when terminated" in {
|
|
|
|
|
val r = Reachability.empty.terminated(nodeB, nodeA)
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(false)
|
2013-08-27 15:14:53 +02:00
|
|
|
// allUnreachable doesn't include terminated
|
2015-01-16 11:09:59 +01:00
|
|
|
r.allUnreachable should ===(Set.empty)
|
|
|
|
|
r.allUnreachableOrTerminated should ===(Set(nodeA))
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"not change terminated entry" in {
|
|
|
|
|
val r = Reachability.empty.terminated(nodeB, nodeA)
|
2013-12-17 14:25:56 +01:00
|
|
|
r.reachable(nodeB, nodeA) should be theSameInstanceAs (r)
|
|
|
|
|
r.unreachable(nodeB, nodeA) should be theSameInstanceAs (r)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"not change when same status" in {
|
|
|
|
|
val r = Reachability.empty.unreachable(nodeB, nodeA)
|
2013-12-17 14:25:56 +01:00
|
|
|
r.unreachable(nodeB, nodeA) should be theSameInstanceAs (r)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be unreachable when some observed unreachable and others reachable" in {
|
|
|
|
|
val r = Reachability.empty.unreachable(nodeB, nodeA).unreachable(nodeC, nodeA).reachable(nodeD, nodeA)
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(false)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be reachable when all observed reachable again" in {
|
|
|
|
|
val r = Reachability.empty.unreachable(nodeB, nodeA).unreachable(nodeC, nodeA).
|
|
|
|
|
reachable(nodeB, nodeA).reachable(nodeC, nodeA).
|
|
|
|
|
unreachable(nodeB, nodeC).unreachable(nodeC, nodeB)
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(true)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 14:30:16 +01:00
|
|
|
"exclude observations from specific (downed) nodes" in {
|
|
|
|
|
val r = Reachability.empty.
|
|
|
|
|
unreachable(nodeC, nodeA).reachable(nodeC, nodeA).
|
|
|
|
|
unreachable(nodeC, nodeB).
|
|
|
|
|
unreachable(nodeB, nodeA).unreachable(nodeB, nodeC)
|
|
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(false)
|
|
|
|
|
r.isReachable(nodeB) should ===(false)
|
|
|
|
|
r.isReachable(nodeC) should ===(false)
|
|
|
|
|
r.allUnreachableOrTerminated should ===(Set(nodeA, nodeB, nodeC))
|
|
|
|
|
r.removeObservers(Set(nodeB)).allUnreachableOrTerminated should ===(Set(nodeB))
|
2015-01-30 14:30:16 +01:00
|
|
|
}
|
|
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
"be pruned when all records of an observer are Reachable" in {
|
|
|
|
|
val r = Reachability.empty.
|
|
|
|
|
unreachable(nodeB, nodeA).unreachable(nodeB, nodeC).
|
|
|
|
|
unreachable(nodeD, nodeC).
|
|
|
|
|
reachable(nodeB, nodeA).reachable(nodeB, nodeC)
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(true)
|
|
|
|
|
r.isReachable(nodeC) should ===(false)
|
|
|
|
|
r.records should ===(Vector(Record(nodeD, nodeC, Unreachable, 1L)))
|
2013-08-27 15:14:53 +02:00
|
|
|
|
|
|
|
|
val r2 = r.unreachable(nodeB, nodeD).unreachable(nodeB, nodeE)
|
2015-01-16 11:09:59 +01:00
|
|
|
r2.records.toSet should ===(Set(
|
2013-08-27 15:14:53 +02:00
|
|
|
Record(nodeD, nodeC, Unreachable, 1L),
|
|
|
|
|
Record(nodeB, nodeD, Unreachable, 5L),
|
|
|
|
|
Record(nodeB, nodeE, Unreachable, 6L)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"have correct aggregated status" in {
|
|
|
|
|
val records = Vector(
|
|
|
|
|
Reachability.Record(nodeA, nodeB, Reachable, 2),
|
|
|
|
|
Reachability.Record(nodeC, nodeB, Unreachable, 2),
|
|
|
|
|
Reachability.Record(nodeA, nodeD, Unreachable, 3),
|
|
|
|
|
Reachability.Record(nodeD, nodeB, Terminated, 4))
|
2016-06-02 14:06:57 +02:00
|
|
|
val versions = Map(nodeA → 3L, nodeC → 3L, nodeD → 4L)
|
2013-08-27 15:14:53 +02:00
|
|
|
val r = Reachability(records, versions)
|
2015-01-16 11:09:59 +01:00
|
|
|
r.status(nodeA) should ===(Reachable)
|
|
|
|
|
r.status(nodeB) should ===(Terminated)
|
|
|
|
|
r.status(nodeD) should ===(Unreachable)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"have correct status for a mix of nodes" in {
|
|
|
|
|
val r = Reachability.empty.
|
|
|
|
|
unreachable(nodeB, nodeA).unreachable(nodeC, nodeA).unreachable(nodeD, nodeA).
|
|
|
|
|
unreachable(nodeC, nodeB).reachable(nodeC, nodeB).unreachable(nodeD, nodeB).
|
|
|
|
|
unreachable(nodeD, nodeC).reachable(nodeD, nodeC).
|
|
|
|
|
reachable(nodeE, nodeD).
|
|
|
|
|
unreachable(nodeA, nodeE).terminated(nodeB, nodeE)
|
|
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.status(nodeB, nodeA) should ===(Unreachable)
|
|
|
|
|
r.status(nodeC, nodeA) should ===(Unreachable)
|
|
|
|
|
r.status(nodeD, nodeA) should ===(Unreachable)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.status(nodeC, nodeB) should ===(Reachable)
|
|
|
|
|
r.status(nodeD, nodeB) should ===(Unreachable)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.status(nodeA, nodeE) should ===(Unreachable)
|
|
|
|
|
r.status(nodeB, nodeE) should ===(Terminated)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.isReachable(nodeA) should ===(false)
|
|
|
|
|
r.isReachable(nodeB) should ===(false)
|
|
|
|
|
r.isReachable(nodeC) should ===(true)
|
|
|
|
|
r.isReachable(nodeD) should ===(true)
|
|
|
|
|
r.isReachable(nodeE) should ===(false)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.allUnreachable should ===(Set(nodeA, nodeB))
|
|
|
|
|
r.allUnreachableFrom(nodeA) should ===(Set(nodeE))
|
|
|
|
|
r.allUnreachableFrom(nodeB) should ===(Set(nodeA))
|
|
|
|
|
r.allUnreachableFrom(nodeC) should ===(Set(nodeA))
|
|
|
|
|
r.allUnreachableFrom(nodeD) should ===(Set(nodeA, nodeB))
|
2013-08-27 15:14:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.observersGroupedByUnreachable should ===(Map(
|
2016-06-02 14:06:57 +02:00
|
|
|
nodeA → Set(nodeB, nodeC, nodeD),
|
|
|
|
|
nodeB → Set(nodeD),
|
|
|
|
|
nodeE → Set(nodeA)))
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"merge by picking latest version of each record" in {
|
|
|
|
|
val r1 = Reachability.empty.unreachable(nodeB, nodeA).unreachable(nodeC, nodeD)
|
|
|
|
|
val r2 = r1.reachable(nodeB, nodeA).unreachable(nodeD, nodeE).unreachable(nodeC, nodeA)
|
|
|
|
|
val merged = r1.merge(Set(nodeA, nodeB, nodeC, nodeD, nodeE), r2)
|
|
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
merged.status(nodeB, nodeA) should ===(Reachable)
|
|
|
|
|
merged.status(nodeC, nodeA) should ===(Unreachable)
|
|
|
|
|
merged.status(nodeC, nodeD) should ===(Unreachable)
|
|
|
|
|
merged.status(nodeD, nodeE) should ===(Unreachable)
|
|
|
|
|
merged.status(nodeE, nodeA) should ===(Reachable)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
merged.isReachable(nodeA) should ===(false)
|
|
|
|
|
merged.isReachable(nodeD) should ===(false)
|
|
|
|
|
merged.isReachable(nodeE) should ===(false)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
|
|
|
|
val merged2 = r2.merge(Set(nodeA, nodeB, nodeC, nodeD, nodeE), r1)
|
2015-01-16 11:09:59 +01:00
|
|
|
merged2.records.toSet should ===(merged.records.toSet)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
2013-09-12 17:19:53 +02:00
|
|
|
"merge by taking allowed set into account" in {
|
|
|
|
|
val r1 = Reachability.empty.unreachable(nodeB, nodeA).unreachable(nodeC, nodeD)
|
|
|
|
|
val r2 = r1.reachable(nodeB, nodeA).unreachable(nodeD, nodeE).unreachable(nodeC, nodeA)
|
|
|
|
|
// nodeD not in allowed set
|
|
|
|
|
val allowed = Set(nodeA, nodeB, nodeC, nodeE)
|
|
|
|
|
val merged = r1.merge(allowed, r2)
|
|
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
merged.status(nodeB, nodeA) should ===(Reachable)
|
|
|
|
|
merged.status(nodeC, nodeA) should ===(Unreachable)
|
|
|
|
|
merged.status(nodeC, nodeD) should ===(Reachable)
|
|
|
|
|
merged.status(nodeD, nodeE) should ===(Reachable)
|
|
|
|
|
merged.status(nodeE, nodeA) should ===(Reachable)
|
2013-09-12 17:19:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
merged.isReachable(nodeA) should ===(false)
|
|
|
|
|
merged.isReachable(nodeD) should ===(true)
|
|
|
|
|
merged.isReachable(nodeE) should ===(true)
|
2013-09-12 17:19:53 +02:00
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
merged.versions.keySet should ===(Set(nodeB, nodeC))
|
2013-09-12 17:19:53 +02:00
|
|
|
|
|
|
|
|
val merged2 = r2.merge(allowed, r1)
|
2015-01-16 11:09:59 +01:00
|
|
|
merged2.records.toSet should ===(merged.records.toSet)
|
|
|
|
|
merged2.versions should ===(merged.versions)
|
2013-09-12 17:19:53 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
"merge correctly after pruning" in {
|
|
|
|
|
val r1 = Reachability.empty.unreachable(nodeB, nodeA).unreachable(nodeC, nodeD)
|
|
|
|
|
val r2 = r1.unreachable(nodeA, nodeE)
|
|
|
|
|
val r3 = r1.reachable(nodeB, nodeA) // nodeB pruned
|
|
|
|
|
val merged = r2.merge(Set(nodeA, nodeB, nodeC, nodeD, nodeE), r3)
|
|
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
merged.records.toSet should ===(Set(
|
2013-08-27 15:14:53 +02:00
|
|
|
Record(nodeA, nodeE, Unreachable, 1),
|
|
|
|
|
Record(nodeC, nodeD, Unreachable, 1)))
|
|
|
|
|
|
|
|
|
|
val merged3 = r3.merge(Set(nodeA, nodeB, nodeC, nodeD, nodeE), r2)
|
2015-01-16 11:09:59 +01:00
|
|
|
merged3.records.toSet should ===(merged.records.toSet)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"merge versions correctly" in {
|
2016-06-02 14:06:57 +02:00
|
|
|
val r1 = Reachability(Vector.empty, Map(nodeA → 3L, nodeB → 5L, nodeC → 7L))
|
|
|
|
|
val r2 = Reachability(Vector.empty, Map(nodeA → 6L, nodeB → 2L, nodeD → 1L))
|
2013-08-27 15:14:53 +02:00
|
|
|
val merged = r1.merge(Set(nodeA, nodeB, nodeC, nodeD, nodeE), r2)
|
|
|
|
|
|
2016-06-02 14:06:57 +02:00
|
|
|
val expected = Map(nodeA → 6L, nodeB → 5L, nodeC → 7L, nodeD → 1L)
|
2015-01-16 11:09:59 +01:00
|
|
|
merged.versions should ===(expected)
|
2013-08-27 15:14:53 +02:00
|
|
|
|
|
|
|
|
val merged2 = r2.merge(Set(nodeA, nodeB, nodeC, nodeD, nodeE), r1)
|
2015-01-16 11:09:59 +01:00
|
|
|
merged2.versions should ===(expected)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"remove node" in {
|
|
|
|
|
val r = Reachability.empty.
|
|
|
|
|
unreachable(nodeB, nodeA).
|
|
|
|
|
unreachable(nodeC, nodeD).
|
|
|
|
|
unreachable(nodeB, nodeC).
|
|
|
|
|
unreachable(nodeB, nodeE).
|
|
|
|
|
remove(Set(nodeA, nodeB))
|
|
|
|
|
|
2015-01-16 11:09:59 +01:00
|
|
|
r.status(nodeB, nodeA) should ===(Reachable)
|
|
|
|
|
r.status(nodeC, nodeD) should ===(Unreachable)
|
|
|
|
|
r.status(nodeB, nodeC) should ===(Reachable)
|
|
|
|
|
r.status(nodeB, nodeE) should ===(Reachable)
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
|
2016-12-16 12:25:37 +01:00
|
|
|
"remove correctly after pruning" in {
|
|
|
|
|
val r = Reachability.empty.
|
|
|
|
|
unreachable(nodeB, nodeA).unreachable(nodeB, nodeC).
|
|
|
|
|
unreachable(nodeD, nodeC).
|
|
|
|
|
reachable(nodeB, nodeA).reachable(nodeB, nodeC)
|
|
|
|
|
r.records should ===(Vector(Record(nodeD, nodeC, Unreachable, 1L)))
|
|
|
|
|
val r2 = r.remove(List(nodeB))
|
|
|
|
|
r2.allObservers should ===(Set(nodeD))
|
|
|
|
|
r2.versions.keySet should ===(Set(nodeD))
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 13:16:35 +01:00
|
|
|
"be able to filter records" in {
|
|
|
|
|
val r = Reachability.empty
|
|
|
|
|
.unreachable(nodeC, nodeB)
|
|
|
|
|
.unreachable(nodeB, nodeA)
|
|
|
|
|
.unreachable(nodeB, nodeC)
|
|
|
|
|
|
|
|
|
|
val filtered1 = r.filterRecords(record ⇒ record.observer != nodeC)
|
|
|
|
|
filtered1.isReachable(nodeB) should ===(true)
|
|
|
|
|
filtered1.isReachable(nodeA) should ===(false)
|
|
|
|
|
filtered1.allObservers should ===(Set(nodeB))
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
}
|
|
|
|
|
}
|