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;
18 import java.util.concurrent.atomic.AtomicLong;
21 * RaftActor encapsulates a state machine that needs to be kept synchronized
22 * in a cluster. It implements the RAFT algorithm as described in the paper
23 * <a href='https://ramcloud.stanford.edu/wiki/download/attachments/11370504/raft.pdf'>
24 * In Search of an Understandable Consensus Algorithm</a>
26 * RaftActor has 3 states and each state has a certain behavior associated
27 * with it. A Raft actor can behave as,
30 * <li> A Follower (or) </li>
31 * <li> A Candidate </li>
35 * A RaftActor MUST be a Leader in order to accept requests from clients to
36 * change the state of it's encapsulated state machine. Once a RaftActor becomes
37 * a Leader it is also responsible for ensuring that all followers ultimately
38 * have the same log and therefore the same state machine as itself.
41 * The current behavior of a RaftActor determines how election for leadership
42 * is initiated and how peer RaftActors react to request for votes.
45 * Each RaftActor also needs to know the current election term. It uses this
46 * information for a couple of things. One is to simply figure out who it
47 * voted for in the last election. Another is to figure out if the message
48 * it received to update it's state is stale.
51 * The RaftActor uses akka-persistence to store it's replicated log.
52 * Furthermore through it's behaviors a Raft Actor determines
55 * <li> when a log entry should be persisted </li>
56 * <li> when a log entry should be applied to the state machine (and) </li>
57 * <li> when a snapshot should be saved </li>
60 * <a href="http://doc.akka.io/api/akka/2.3.3/index.html#akka.persistence.UntypedEventsourcedProcessor">UntypeEventSourceProcessor</a>
62 public abstract class RaftActor extends UntypedEventsourcedProcessor {
65 * The current state determines the current behavior of a RaftActor
66 * A Raft Actor always starts off in the Follower State
68 private RaftActorBehavior currentBehavior;
71 * This context should NOT be passed directly to any other actor it is
72 * only to be consumed by the RaftActorBehaviors
74 private RaftActorContext context;
76 public RaftActor(String id){
77 context = new RaftActorContextImpl(this.getSelf(),
79 id, new ElectionTermImpl(id),
80 new AtomicLong(0), new AtomicLong(0), new ReplicatedLogImpl());
81 currentBehavior = switchBehavior(RaftState.Follower);
84 @Override public void onReceiveRecover(Object message) {
85 throw new UnsupportedOperationException("onReceiveRecover");
88 @Override public void onReceiveCommand(Object message) {
89 RaftState state = currentBehavior.handleMessage(getSender(), message);
90 currentBehavior = switchBehavior(state);
93 private RaftActorBehavior switchBehavior(RaftState state){
94 RaftActorBehavior behavior = null;
95 if(state == RaftState.Candidate){
96 behavior = new Candidate(context, Collections.EMPTY_LIST);
97 } else if(state == RaftState.Follower){
98 behavior = new Follower(context);
100 behavior = new Leader(context, Collections.EMPTY_LIST);
105 private class ReplicatedLogImpl implements ReplicatedLog {
107 @Override public ReplicatedLogEntry getReplicatedLogEntry(long index) {
108 throw new UnsupportedOperationException("getReplicatedLogEntry");
111 @Override public ReplicatedLogEntry last() {
112 throw new UnsupportedOperationException("last");