Merge "Initial code/design for an Akka Raft implementation"
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / main / java / org / opendaylight / controller / cluster / raft / RaftActor.java
1 /*
2  * Copyright (c) 2014 Cisco 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
9 package org.opendaylight.controller.cluster.raft;
10
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;
16
17 import java.util.Collections;
18 import java.util.concurrent.atomic.AtomicLong;
19
20 /**
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>
25  * <p>
26  * RaftActor has 3 states and each state has a certain behavior associated
27  * with it. A Raft actor can behave as,
28  * <ul>
29  *     <li> A Leader </li>
30  *     <li> A Follower (or) </li>
31  *     <li> A Candidate </li>
32  * </ul>
33  *
34  * <p>
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.
39  *
40  * <p>
41  * The current behavior of a RaftActor determines how election for leadership
42  * is initiated and how peer RaftActors react to request for votes.
43  *
44  * <p>
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.
49  *
50  * <p>
51  * The RaftActor uses akka-persistence to store it's replicated log.
52  * Furthermore through it's behaviors a Raft Actor determines
53  *
54  * <ul>
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>
58  * </ul>
59  *
60  * <a href="http://doc.akka.io/api/akka/2.3.3/index.html#akka.persistence.UntypedEventsourcedProcessor">UntypeEventSourceProcessor</a>
61  */
62 public abstract class RaftActor extends UntypedEventsourcedProcessor {
63
64     /**
65      *  The current state determines the current behavior of a RaftActor
66      * A Raft Actor always starts off in the Follower State
67      */
68     private RaftActorBehavior currentBehavior;
69
70     /**
71      * This context should NOT be passed directly to any other actor it is
72      * only to be consumed by the RaftActorBehaviors
73      */
74     private RaftActorContext context;
75
76     public RaftActor(String id){
77         context = new RaftActorContextImpl(this.getSelf(),
78             this.getContext(),
79             id, new ElectionTermImpl(id),
80             new AtomicLong(0), new AtomicLong(0), new ReplicatedLogImpl());
81         currentBehavior = switchBehavior(RaftState.Follower);
82     }
83
84     @Override public void onReceiveRecover(Object message) {
85         throw new UnsupportedOperationException("onReceiveRecover");
86     }
87
88     @Override public void onReceiveCommand(Object message) {
89         RaftState state = currentBehavior.handleMessage(getSender(), message);
90         currentBehavior = switchBehavior(state);
91     }
92
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);
99         } else {
100             behavior = new Leader(context, Collections.EMPTY_LIST);
101         }
102         return behavior;
103     }
104
105     private class ReplicatedLogImpl implements ReplicatedLog {
106
107         @Override public ReplicatedLogEntry getReplicatedLogEntry(long index) {
108             throw new UnsupportedOperationException("getReplicatedLogEntry");
109         }
110
111         @Override public ReplicatedLogEntry last() {
112             throw new UnsupportedOperationException("last");
113         }
114     }
115 }