Don't transfer leadership to a non-voting follower
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / LeadershipTransferIntegrationTest.java
1 /*
2  * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.cluster.raft;
9
10 import static akka.pattern.Patterns.ask;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNull;
13 import static org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor.clearMessages;
14 import static org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor.expectFirstMatching;
15 import akka.actor.ActorRef;
16 import akka.actor.Props;
17 import akka.pattern.Patterns;
18 import akka.testkit.TestActorRef;
19 import akka.util.Timeout;
20 import com.google.common.base.Stopwatch;
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.util.concurrent.Uninterruptibles;
23 import java.util.concurrent.TimeUnit;
24 import org.junit.Test;
25 import org.opendaylight.controller.cluster.notifications.LeaderStateChanged;
26 import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
27 import org.opendaylight.controller.cluster.raft.client.messages.GetOnDemandRaftState;
28 import org.opendaylight.controller.cluster.raft.client.messages.OnDemandRaftState;
29 import org.opendaylight.controller.cluster.raft.client.messages.Shutdown;
30 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
31 import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
32 import scala.concurrent.Await;
33 import scala.concurrent.Future;
34 import scala.concurrent.duration.FiniteDuration;
35
36 /**
37  * Tests leadership transfer end-to-end.
38  *
39  * @author Thomas Pantelis
40  */
41 public class LeadershipTransferIntegrationTest extends AbstractRaftActorIntegrationTest {
42
43     private final String follower3Id = factory.generateActorId("follower");
44     private TestActorRef<MessageCollectorActor> leaderNotifierActor;
45     private TestActorRef<MessageCollectorActor> follower1NotifierActor;
46     private TestActorRef<MessageCollectorActor> follower2NotifierActor;
47     private TestActorRef<MessageCollectorActor> follower3NotifierActor;
48     private TestActorRef<TestRaftActor> follower3Actor;
49     private ActorRef follower3CollectorActor;
50
51     @Test
52     public void testLeaderTransferOnShutDown() throws Throwable {
53         testLog.info("testLeaderTransferOnShutDown starting");
54
55         createRaftActors();
56
57         sendPayloadWithFollower2Lagging();
58
59         sendShutDownToLeaderAndVerifyLeadershipTransferToFollower1();
60
61         sendShutDown(follower2Actor);
62
63         testLog.info("testLeaderTransferOnShutDown ending");
64     }
65
66     private void sendShutDown(ActorRef actor) throws Exception {
67         testLog.info("sendShutDown for {} starting", actor.path());
68
69         FiniteDuration duration = FiniteDuration.create(5, TimeUnit.SECONDS);
70         Future<Boolean> stopFuture = Patterns.gracefulStop(actor, duration, new Shutdown());
71
72         Boolean stopped = Await.result(stopFuture, duration);
73         assertEquals("Stopped", Boolean.TRUE, stopped);
74
75         testLog.info("sendShutDown for {} ending", actor.path());
76     }
77
78     private void sendShutDownToLeaderAndVerifyLeadershipTransferToFollower1() throws Throwable {
79         testLog.info("sendShutDownToLeaderAndVerifyLeadershipTransferToFollower1 starting");
80
81         clearMessages(leaderNotifierActor);
82         clearMessages(follower1NotifierActor);
83         clearMessages(follower2NotifierActor);
84         clearMessages(follower3NotifierActor);
85
86         FiniteDuration duration = FiniteDuration.create(5, TimeUnit.SECONDS);
87         Future<Boolean> stopFuture = Patterns.gracefulStop(leaderActor, duration, new Shutdown());
88
89         assertNullLeaderIdChange(leaderNotifierActor);
90         assertNullLeaderIdChange(follower1NotifierActor);
91         assertNullLeaderIdChange(follower2NotifierActor);
92         assertNullLeaderIdChange(follower3NotifierActor);
93
94         verifyRaftState(follower1Actor, RaftState.Leader);
95
96         Boolean stopped = Await.result(stopFuture, duration);
97         assertEquals("Stopped", Boolean.TRUE, stopped);
98
99         follower2Actor.underlyingActor().stopDropMessages(AppendEntries.class);
100         ApplyState applyState = expectFirstMatching(follower2CollectorActor, ApplyState.class);
101         assertEquals("Apply sate index", 0, applyState.getReplicatedLogEntry().getIndex());
102
103         testLog.info("sendShutDownToLeaderAndVerifyLeadershipTransferToFollower1 ending");
104     }
105
106     private void sendPayloadWithFollower2Lagging() {
107         testLog.info("sendPayloadWithFollower2Lagging starting");
108
109         follower2Actor.underlyingActor().startDropMessages(AppendEntries.class);
110
111         sendPayloadData(leaderActor, "zero");
112
113         expectFirstMatching(leaderCollectorActor, ApplyState.class);
114         expectFirstMatching(follower1CollectorActor, ApplyState.class);
115         expectFirstMatching(follower3CollectorActor, ApplyState.class);
116
117         testLog.info("sendPayloadWithFollower2Lagging ending");
118     }
119
120     private void createRaftActors() {
121         testLog.info("createRaftActors starting");
122
123         follower1NotifierActor = factory.createTestActor(Props.create(MessageCollectorActor.class),
124                 factory.generateActorId(follower1Id + "-notifier"));
125         follower1Actor = newTestRaftActor(follower1Id, TestRaftActor.newBuilder().peerAddresses(
126                 ImmutableMap.of(leaderId, testActorPath(leaderId), follower2Id, testActorPath(follower2Id),
127                         follower3Id, testActorPath(follower3Id))).
128                 config(newFollowerConfigParams()).roleChangeNotifier(follower1NotifierActor));
129
130         follower2NotifierActor = factory.createTestActor(Props.create(MessageCollectorActor.class),
131                 factory.generateActorId(follower2Id + "-notifier"));
132         follower2Actor = newTestRaftActor(follower2Id,TestRaftActor.newBuilder().peerAddresses(
133                 ImmutableMap.of(leaderId, testActorPath(leaderId), follower1Id, follower1Actor.path().toString(),
134                         follower3Id, testActorPath(follower3Id))).
135                 config(newFollowerConfigParams()).roleChangeNotifier(follower2NotifierActor));
136
137         follower3NotifierActor = factory.createTestActor(Props.create(MessageCollectorActor.class),
138                 factory.generateActorId(follower3Id + "-notifier"));
139         follower3Actor = newTestRaftActor(follower3Id,TestRaftActor.newBuilder().peerAddresses(
140                 ImmutableMap.of(leaderId, testActorPath(leaderId), follower1Id, follower1Actor.path().toString(),
141                         follower2Id, follower2Actor.path().toString())).
142                 config(newFollowerConfigParams()).roleChangeNotifier(follower3NotifierActor));
143
144         peerAddresses = ImmutableMap.<String, String>builder().
145                 put(follower1Id, follower1Actor.path().toString()).
146                 put(follower2Id, follower2Actor.path().toString()).
147                 put(follower3Id, follower3Actor.path().toString()).build();
148
149         leaderConfigParams = newLeaderConfigParams();
150         leaderConfigParams.setElectionTimeoutFactor(3);
151         leaderNotifierActor = factory.createTestActor(Props.create(MessageCollectorActor.class),
152                 factory.generateActorId(leaderId + "-notifier"));
153         leaderActor = newTestRaftActor(leaderId, TestRaftActor.newBuilder().peerAddresses(peerAddresses).
154                 config(leaderConfigParams).roleChangeNotifier(leaderNotifierActor));
155
156         follower1CollectorActor = follower1Actor.underlyingActor().collectorActor();
157         follower2CollectorActor = follower2Actor.underlyingActor().collectorActor();
158         follower3CollectorActor = follower3Actor.underlyingActor().collectorActor();
159         leaderCollectorActor = leaderActor.underlyingActor().collectorActor();
160
161         leaderContext = leaderActor.underlyingActor().getRaftActorContext();
162         leaderContext.getPeerInfo(follower3Id).setVotingState(VotingState.NON_VOTING);
163
164         waitUntilLeader(leaderActor);
165
166         testLog.info("createRaftActors starting");
167     }
168
169     private void verifyRaftState(ActorRef raftActor, final RaftState expState) throws Throwable {
170         Timeout timeout = new Timeout(500, TimeUnit.MILLISECONDS);
171         Throwable lastError = null;
172         Stopwatch sw = Stopwatch.createStarted();
173         while(sw.elapsed(TimeUnit.SECONDS) <= 5) {
174             try {
175                 OnDemandRaftState raftState = (OnDemandRaftState)Await.result(ask(raftActor,
176                         GetOnDemandRaftState.INSTANCE, timeout), timeout.duration());
177                 assertEquals("getRaftState", expState.toString(), raftState.getRaftState());
178                 return;
179             } catch (Exception | AssertionError e) {
180                 lastError = e;
181                 Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
182             }
183         }
184
185         throw lastError;
186     }
187
188     private void assertNullLeaderIdChange(TestActorRef<MessageCollectorActor> notifierActor) {
189         LeaderStateChanged change = expectFirstMatching(notifierActor, LeaderStateChanged.class);
190         assertNull("Expected null leader Id", change.getLeaderId());
191     }
192
193     @Test
194     public void testLeaderTransferAborted() throws Throwable {
195         testLog.info("testLeaderTransferAborted starting");
196
197         createRaftActors();
198
199         follower1Actor.underlyingActor().startDropMessages(AppendEntries.class);
200         follower2Actor.underlyingActor().startDropMessages(AppendEntries.class);
201
202         sendShutDown(leaderActor);
203
204         verifyRaftState(follower1Actor, RaftState.Follower);
205         verifyRaftState(follower2Actor, RaftState.Follower);
206         verifyRaftState(follower3Actor, RaftState.Follower);
207
208         testLog.info("testLeaderTransferOnShutDown ending");
209     }
210
211     @Test
212     public void testLeaderTransferSkippedOnShutdownWithNoFollowers() throws Throwable {
213         testLog.info("testLeaderTransferSkippedOnShutdownWithNoFollowers starting");
214
215         leaderActor = newTestRaftActor(leaderId, TestRaftActor.newBuilder().config(newLeaderConfigParams()));
216         waitUntilLeader(leaderActor);
217
218         sendShutDown(leaderActor);
219
220         testLog.info("testLeaderTransferSkippedOnShutdownWithNoFollowers ending");
221     }
222 }