Package org.robotninjas.barge.state

Source Code of org.robotninjas.barge.state.Candidate

/**
* Copyright 2013 David Rusek <dave dot rusek at gmail dot com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.robotninjas.barge.state;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.jetlang.fibers.Fiber;
import org.robotninjas.barge.RaftExecutor;
import org.robotninjas.barge.Replica;
import org.robotninjas.barge.api.RequestVote;
import org.robotninjas.barge.api.RequestVoteResponse;
import org.robotninjas.barge.log.RaftLog;
import org.robotninjas.barge.rpc.Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import javax.inject.Inject;
import java.util.List;
import java.util.Random;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.addCallback;
import static org.robotninjas.barge.state.MajorityCollector.majorityResponse;
import static org.robotninjas.barge.state.Raft.StateType.*;
import static org.robotninjas.barge.state.RaftPredicates.voteGranted;

@NotThreadSafe
class Candidate extends BaseState {

  private static final Logger LOGGER = LoggerFactory.getLogger(Candidate.class);
  private static final Random RAND = new Random(System.nanoTime());

  private final Fiber scheduler;
  private final long electionTimeout;
  private final Client client;
  private DeadlineTimer electionTimer;
  private ListenableFuture<Boolean> electionResult;

  @Inject
  Candidate(RaftLog log, @RaftExecutor Fiber scheduler,
            @ElectionTimeout long electionTimeout, Client client) {
    super(CANDIDATE, log);
    this.scheduler = scheduler;
    this.electionTimeout = electionTimeout;
    this.client = client;
  }

  @Override
  public void init(@Nonnull final RaftStateContext ctx) {

    final RaftLog log = getLog();

    log.currentTerm(log.currentTerm() + 1);
    log.votedFor(Optional.of(log.self()));

    LOGGER.debug("Election starting for term {}", log.currentTerm());

    List<ListenableFuture<RequestVoteResponse>> responses = Lists.newArrayList();
    // Request votes from peers
    for (Replica replica : log.members()) {
      responses.add(sendVoteRequest(ctx, replica));
    }
    // We always vote for ourselves
    responses.add(Futures.immediateFuture(RequestVoteResponse.newBuilder().setVoteGranted(true).build()));

    electionResult = majorityResponse(responses, voteGranted());

    long timeout = electionTimeout + (RAND.nextLong() % electionTimeout);
    electionTimer = DeadlineTimer.start(scheduler, new Runnable() {
      @Override
      public void run() {
        LOGGER.debug("Election timeout");
        ctx.setState(Candidate.this, CANDIDATE);
      }
    }, timeout);

    addCallback(electionResult, new FutureCallback<Boolean>() {
      @Override
      public void onSuccess(@Nullable Boolean elected) {
        checkNotNull(elected);
        //noinspection ConstantConditions
        if (elected) {
          ctx.setState(Candidate.this, LEADER);
        }
      }

      @Override
      public void onFailure(Throwable t) {
        if (!electionResult.isCancelled()) {
          LOGGER.debug("Election failed with exception:", t);
        }
      }

    });

  }

  @Override
  public void destroy(RaftStateContext ctx) {
    electionResult.cancel(false);
    electionTimer.cancel();
  }

  @VisibleForTesting
  ListenableFuture<RequestVoteResponse> sendVoteRequest(RaftStateContext ctx, Replica replica) {

    RaftLog log = getLog();
    RequestVote request =
      RequestVote.newBuilder()
        .setTerm(log.currentTerm())
        .setCandidateId(log.self().toString())
        .setLastLogIndex(log.lastLogIndex())
        .setLastLogTerm(log.lastLogTerm())
        .build();

    ListenableFuture<RequestVoteResponse> response = client.requestVote(replica, request);
    Futures.addCallback(response, checkTerm(ctx));

    return response;
  }

  private FutureCallback<RequestVoteResponse> checkTerm(final RaftStateContext ctx) {
    return new FutureCallback<RequestVoteResponse>() {
      @Override
      public void onSuccess(@Nullable RequestVoteResponse response) {
        if (response.getTerm() > getLog().currentTerm()) {
          getLog().currentTerm(response.getTerm());
          ctx.setState(Candidate.this, FOLLOWER);
        }
      }

      @Override
      public void onFailure(Throwable t) {
      }
    };
  }

}
TOP

Related Classes of org.robotninjas.barge.state.Candidate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.