2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.controller.cluster.raft;
11 import akka.persistence.UntypedEventsourcedProcessor;
12 import org.opendaylight.controller.cluster.raft.behaviors.Candidate;
13 import org.opendaylight.controller.cluster.raft.behaviors.Follower;
14 import org.opendaylight.controller.cluster.raft.behaviors.Leader;
15 import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
17 import java.util.Collections;
20 * RaftActor encapsulates a state machine that needs to be kept synchronized
21 * in a cluster. It implements the RAFT algorithm as described in the paper
22 * <a href='https://ramcloud.stanford.edu/wiki/download/attachments/11370504/raft.pdf'>
23 * In Search of an Understandable Consensus Algorithm</a>
25 * RaftActor has 3 states and each state has a certain behavior associated
26 * with it. A Raft actor can behave as,
29 * <li> A Follower (or) </li>
30 * <li> A Candidate </li>
34 * A RaftActor MUST be a Leader in order to accept requests from clients to
35 * change the state of it's encapsulated state machine. Once a RaftActor becomes
36 * a Leader it is also responsible for ensuring that all followers ultimately
37 * have the same log and therefore the same state machine as itself.
40 * The current behavior of a RaftActor determines how election for leadership
41 * is initiated and how peer RaftActors react to request for votes.
44 * Each RaftActor also needs to know the current election term. It uses this
45 * information for a couple of things. One is to simply figure out who it
46 * voted for in the last election. Another is to figure out if the message
47 * it received to update it's state is stale.
50 * The RaftActor uses akka-persistence to store it's replicated log.
51 * Furthermore through it's behaviors a Raft Actor determines
54 * <li> when a log entry should be persisted </li>
55 * <li> when a log entry should be applied to the state machine (and) </li>
56 * <li> when a snapshot should be saved </li>
59 * <a href="http://doc.akka.io/api/akka/2.3.3/index.html#akka.persistence.UntypedEventsourcedProcessor">UntypeEventSourceProcessor</a>
61 public abstract class RaftActor extends UntypedEventsourcedProcessor {
64 * The current state determines the current behavior of a RaftActor
65 * A Raft Actor always starts off in the Follower State
67 private RaftActorBehavior currentBehavior;
70 * This context should NOT be passed directly to any other actor it is
71 * only to be consumed by the RaftActorBehaviors
73 private RaftActorContext context;
75 public RaftActor(String id){
76 context = new RaftActorContextImpl(this.getSelf(),
78 id, new ElectionTermImpl(id),
79 0, 0, new ReplicatedLogImpl());
80 currentBehavior = switchBehavior(RaftState.Follower);
83 @Override public void onReceiveRecover(Object message) {
84 throw new UnsupportedOperationException("onReceiveRecover");
87 @Override public void onReceiveCommand(Object message) {
88 RaftState state = currentBehavior.handleMessage(getSender(), message);
89 currentBehavior = switchBehavior(state);
92 private RaftActorBehavior switchBehavior(RaftState state){
93 RaftActorBehavior behavior = null;
94 if(state == RaftState.Candidate){
95 behavior = new Candidate(context, Collections.EMPTY_LIST);
96 } else if(state == RaftState.Follower){
97 behavior = new Follower(context);
99 behavior = new Leader(context, Collections.EMPTY_LIST);
104 private class ReplicatedLogImpl implements ReplicatedLog {
106 @Override public ReplicatedLogEntry get(long index) {
107 throw new UnsupportedOperationException("get");
110 @Override public ReplicatedLogEntry last() {
111 throw new UnsupportedOperationException("last");
114 @Override public void removeFrom(long index) {
115 throw new UnsupportedOperationException("removeFrom");
118 @Override public void append(ReplicatedLogEntry replicatedLogEntry) {
119 throw new UnsupportedOperationException("append");