Committed by
Ray Milkey
Leadership construct includes List of NodeIds to describe current
leader/backups (candidates) for a topic. This includes removing the RoleInfo in LeadershipEvent, to deduplicate information. RoleInfo is also made a bit saner with the Optional leader field. part of: Device Mastership store on top of LeadershipService Reference: ONOS-76 Change-Id: I957c4d79125873d5a7280f60231d26d2806ab27f
Showing
4 changed files
with
66 additions
and
16 deletions
... | @@ -16,24 +16,46 @@ | ... | @@ -16,24 +16,46 @@ |
16 | package org.onosproject.cluster; | 16 | package org.onosproject.cluster; |
17 | 17 | ||
18 | import java.util.Objects; | 18 | import java.util.Objects; |
19 | +import java.util.List; | ||
19 | 20 | ||
20 | import org.joda.time.DateTime; | 21 | import org.joda.time.DateTime; |
21 | 22 | ||
22 | import com.google.common.base.MoreObjects; | 23 | import com.google.common.base.MoreObjects; |
24 | +import com.google.common.collect.ImmutableList; | ||
23 | 25 | ||
24 | /** | 26 | /** |
25 | - * Abstract leadership concept. | 27 | + * Abstract leadership concept. The information carried by this construct |
28 | + * include the topic of contention, the {@link NodeId}s of Nodes that could | ||
29 | + * become leader for the topic, the epoch when the term for a given leader | ||
30 | + * began, and the system time when the term began. Note: | ||
31 | + * <ul> | ||
32 | + * <li>The list of NodeIds may include the current leader at index 0, and the | ||
33 | + * rest in decreasing preference order.</li> | ||
34 | + * <li>The epoch is the logical age of a Leadership construct, and should be | ||
35 | + * used for comparing two Leaderships, but only of the same topic.</li> | ||
36 | + * </ul> | ||
26 | */ | 37 | */ |
27 | public class Leadership { | 38 | public class Leadership { |
28 | 39 | ||
29 | private final String topic; | 40 | private final String topic; |
30 | private final NodeId leader; | 41 | private final NodeId leader; |
42 | + private final List<NodeId> candidates; | ||
31 | private final long epoch; | 43 | private final long epoch; |
32 | private final long electedTime; | 44 | private final long electedTime; |
33 | 45 | ||
34 | public Leadership(String topic, NodeId leader, long epoch, long electedTime) { | 46 | public Leadership(String topic, NodeId leader, long epoch, long electedTime) { |
35 | this.topic = topic; | 47 | this.topic = topic; |
36 | this.leader = leader; | 48 | this.leader = leader; |
49 | + this.candidates = ImmutableList.of(leader); | ||
50 | + this.epoch = epoch; | ||
51 | + this.electedTime = electedTime; | ||
52 | + } | ||
53 | + | ||
54 | + public Leadership(String topic, NodeId leader, List<NodeId> candidates, | ||
55 | + long epoch, long electedTime) { | ||
56 | + this.topic = topic; | ||
57 | + this.leader = leader; | ||
58 | + this.candidates = ImmutableList.copyOf(candidates); | ||
37 | this.epoch = epoch; | 59 | this.epoch = epoch; |
38 | this.electedTime = electedTime; | 60 | this.electedTime = electedTime; |
39 | } | 61 | } |
... | @@ -55,12 +77,23 @@ public class Leadership { | ... | @@ -55,12 +77,23 @@ public class Leadership { |
55 | } | 77 | } |
56 | 78 | ||
57 | /** | 79 | /** |
80 | + * Returns an preference-ordered list of nodes that are in the leadership | ||
81 | + * race for this topic. | ||
82 | + * | ||
83 | + * @return a list of NodeIds in priority-order, or an empty list. | ||
84 | + */ | ||
85 | + public List<NodeId> candidates() { | ||
86 | + return candidates; | ||
87 | + } | ||
88 | + | ||
89 | + /** | ||
58 | * The epoch when the leadership was assumed. | 90 | * The epoch when the leadership was assumed. |
59 | * <p> | 91 | * <p> |
60 | - * Comparing epochs is only appropriate for leadership | 92 | + * Comparing epochs is only appropriate for leadership events for the same |
61 | - * events for the same topic. The system guarantees that | 93 | + * topic. The system guarantees that for any given topic the epoch for a new |
62 | - * for any given topic the epoch for a new term is higher | 94 | + * term is higher (not necessarily by 1) than the epoch for any previous |
63 | - * (not necessarily by 1) than the epoch for any previous term. | 95 | + * term. |
96 | + * | ||
64 | * @return leadership epoch | 97 | * @return leadership epoch |
65 | */ | 98 | */ |
66 | public long epoch() { | 99 | public long epoch() { |
... | @@ -83,7 +116,7 @@ public class Leadership { | ... | @@ -83,7 +116,7 @@ public class Leadership { |
83 | 116 | ||
84 | @Override | 117 | @Override |
85 | public int hashCode() { | 118 | public int hashCode() { |
86 | - return Objects.hash(topic, leader, epoch); | 119 | + return Objects.hash(topic, leader, candidates, epoch); |
87 | } | 120 | } |
88 | 121 | ||
89 | @Override | 122 | @Override |
... | @@ -95,6 +128,7 @@ public class Leadership { | ... | @@ -95,6 +128,7 @@ public class Leadership { |
95 | final Leadership other = (Leadership) obj; | 128 | final Leadership other = (Leadership) obj; |
96 | return Objects.equals(this.topic, other.topic) && | 129 | return Objects.equals(this.topic, other.topic) && |
97 | Objects.equals(this.leader, other.leader) && | 130 | Objects.equals(this.leader, other.leader) && |
131 | + Objects.equals(this.candidates, other.candidates) && | ||
98 | Objects.equals(this.epoch, other.epoch) && | 132 | Objects.equals(this.epoch, other.epoch) && |
99 | Objects.equals(this.electedTime, other.electedTime); | 133 | Objects.equals(this.electedTime, other.electedTime); |
100 | } | 134 | } |
... | @@ -106,6 +140,7 @@ public class Leadership { | ... | @@ -106,6 +140,7 @@ public class Leadership { |
106 | return MoreObjects.toStringHelper(this.getClass()) | 140 | return MoreObjects.toStringHelper(this.getClass()) |
107 | .add("topic", topic) | 141 | .add("topic", topic) |
108 | .add("leader", leader) | 142 | .add("leader", leader) |
143 | + .add("candidates", candidates) | ||
109 | .add("epoch", epoch) | 144 | .add("epoch", epoch) |
110 | .add("electedTime", new DateTime(electedTime)) | 145 | .add("electedTime", new DateTime(electedTime)) |
111 | .toString(); | 146 | .toString(); | ... | ... |
... | @@ -46,7 +46,14 @@ public class LeadershipEvent extends AbstractEvent<LeadershipEvent.Type, Leaders | ... | @@ -46,7 +46,14 @@ public class LeadershipEvent extends AbstractEvent<LeadershipEvent.Type, Leaders |
46 | * Signifies that the leader has been booted and lost leadership. The event subject is the | 46 | * Signifies that the leader has been booted and lost leadership. The event subject is the |
47 | * former leader. | 47 | * former leader. |
48 | */ | 48 | */ |
49 | - LEADER_BOOTED | 49 | + LEADER_BOOTED, |
50 | + | ||
51 | + /** | ||
52 | + * Signifies that the list of candidates for leadership for a resource | ||
53 | + * has changed. If the change in the backups list is accompanied by a | ||
54 | + * change in the leader, the event is subsumed by the leadership change. | ||
55 | + */ | ||
56 | + LEADER_CANDIDATES_CHANGED | ||
50 | } | 57 | } |
51 | 58 | ||
52 | /** | 59 | /** | ... | ... |
... | @@ -17,6 +17,7 @@ package org.onosproject.cluster; | ... | @@ -17,6 +17,7 @@ package org.onosproject.cluster; |
17 | 17 | ||
18 | import java.util.List; | 18 | import java.util.List; |
19 | import java.util.Objects; | 19 | import java.util.Objects; |
20 | +import java.util.Optional; | ||
20 | 21 | ||
21 | import com.google.common.base.MoreObjects; | 22 | import com.google.common.base.MoreObjects; |
22 | import com.google.common.collect.ImmutableList; | 23 | import com.google.common.collect.ImmutableList; |
... | @@ -27,21 +28,22 @@ import com.google.common.collect.ImmutableList; | ... | @@ -27,21 +28,22 @@ import com.google.common.collect.ImmutableList; |
27 | * master and a preference-ordered list of backup nodes. | 28 | * master and a preference-ordered list of backup nodes. |
28 | */ | 29 | */ |
29 | public class RoleInfo { | 30 | public class RoleInfo { |
30 | - private final NodeId master; | 31 | + private final Optional<NodeId> master; |
31 | private final List<NodeId> backups; | 32 | private final List<NodeId> backups; |
32 | 33 | ||
33 | public RoleInfo(NodeId master, List<NodeId> backups) { | 34 | public RoleInfo(NodeId master, List<NodeId> backups) { |
34 | - this.master = master; | 35 | + this.master = Optional.ofNullable(master); |
35 | this.backups = ImmutableList.copyOf(backups); | 36 | this.backups = ImmutableList.copyOf(backups); |
36 | } | 37 | } |
37 | 38 | ||
38 | public RoleInfo() { | 39 | public RoleInfo() { |
39 | - this.master = null; | 40 | + this.master = Optional.empty(); |
40 | this.backups = ImmutableList.of(); | 41 | this.backups = ImmutableList.of(); |
41 | } | 42 | } |
42 | 43 | ||
44 | + // This will return a Optional<NodeId> in the future. | ||
43 | public NodeId master() { | 45 | public NodeId master() { |
44 | - return master; | 46 | + return master.orElseGet(() -> null); |
45 | } | 47 | } |
46 | 48 | ||
47 | public List<NodeId> backups() { | 49 | public List<NodeId> backups() { |
... | @@ -74,7 +76,7 @@ public class RoleInfo { | ... | @@ -74,7 +76,7 @@ public class RoleInfo { |
74 | @Override | 76 | @Override |
75 | public String toString() { | 77 | public String toString() { |
76 | return MoreObjects.toStringHelper(this.getClass()) | 78 | return MoreObjects.toStringHelper(this.getClass()) |
77 | - .add("master", master) | 79 | + .add("master", master.orElseGet(() -> null)) |
78 | .add("backups", backups) | 80 | .add("backups", backups) |
79 | .toString(); | 81 | .toString(); |
80 | } | 82 | } | ... | ... |
... | @@ -20,9 +20,9 @@ import java.util.List; | ... | @@ -20,9 +20,9 @@ import java.util.List; |
20 | import org.junit.Test; | 20 | import org.junit.Test; |
21 | 21 | ||
22 | import com.google.common.collect.Lists; | 22 | import com.google.common.collect.Lists; |
23 | +import com.google.common.testing.EqualsTester; | ||
23 | 24 | ||
24 | import static org.junit.Assert.assertEquals; | 25 | import static org.junit.Assert.assertEquals; |
25 | -import static org.junit.Assert.assertNotEquals; | ||
26 | 26 | ||
27 | /** | 27 | /** |
28 | * Test to check behavioral correctness of the RoleInfo structure. | 28 | * Test to check behavioral correctness of the RoleInfo structure. |
... | @@ -39,14 +39,20 @@ public class RoleInfoTest { | ... | @@ -39,14 +39,20 @@ public class RoleInfoTest { |
39 | private static final RoleInfo RI1 = new RoleInfo(N1, BKUP1); | 39 | private static final RoleInfo RI1 = new RoleInfo(N1, BKUP1); |
40 | private static final RoleInfo RI2 = new RoleInfo(N1, BKUP2); | 40 | private static final RoleInfo RI2 = new RoleInfo(N1, BKUP2); |
41 | private static final RoleInfo RI3 = new RoleInfo(N2, BKUP1); | 41 | private static final RoleInfo RI3 = new RoleInfo(N2, BKUP1); |
42 | + private static final RoleInfo RI4 = new RoleInfo(null, BKUP2); | ||
43 | + | ||
44 | + @Test | ||
45 | + public void testEquality() { | ||
46 | + new EqualsTester() | ||
47 | + .addEqualityGroup(RI1, new RoleInfo(new NodeId("n1"), Lists.newArrayList(N2, N3))) | ||
48 | + .addEqualityGroup(RI3); | ||
49 | + } | ||
42 | 50 | ||
43 | @Test | 51 | @Test |
44 | public void basics() { | 52 | public void basics() { |
45 | assertEquals("wrong master", new NodeId("n1"), RI1.master()); | 53 | assertEquals("wrong master", new NodeId("n1"), RI1.master()); |
46 | assertEquals("wrong Backups", RI1.backups(), Lists.newArrayList(N2, N3)); | 54 | assertEquals("wrong Backups", RI1.backups(), Lists.newArrayList(N2, N3)); |
47 | - | 55 | + assertEquals("wrong empty master", RI4.master(), null); |
48 | - assertNotEquals("equals() broken", RI1, RI2); | ||
49 | - assertNotEquals("equals() broken", RI1, RI3); | ||
50 | 56 | ||
51 | List<NodeId> bkup3 = Lists.newArrayList(N3, new NodeId("n4")); | 57 | List<NodeId> bkup3 = Lists.newArrayList(N3, new NodeId("n4")); |
52 | assertEquals("equals() broken", new RoleInfo(N1, bkup3), RI2); | 58 | assertEquals("equals() broken", new RoleInfo(N1, bkup3), RI2); | ... | ... |
-
Please register or login to post a comment