Ayaka Koshibe
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
...@@ -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);
......