Example #1
0
  private void askWhoIsTheLeader() {
    logger.info("Node " + conf.getNodeId() + " is searching for the leader");
    MgmtHeader.Builder mhb = MgmtHeader.newBuilder();
    mhb.setOriginator(conf.getNodeId());
    mhb.setTime(System.currentTimeMillis());
    mhb.setSecurityCode(-999); // TODO add security

    VectorClock.Builder rpb = VectorClock.newBuilder();
    rpb.setNodeId(conf.getNodeId());
    rpb.setTime(mhb.getTime());
    rpb.setVersion(termId);
    mhb.addPath(rpb);

    LeaderElection.Builder elb = LeaderElection.newBuilder();
    elb.setTermId(termId);
    elb.setLastLogIndex(lastLogIndex);
    elb.setAction(ElectAction.WHOISTHELEADER);
    elb.setDesc("Node " + this.leaderNode + " is asking who the leader is");
    elb.setCandidateId(-1);
    elb.setExpires(-1);

    Management.Builder mb = Management.newBuilder();
    mb.setHeader(mhb.build());
    mb.setElection(elb.build());

    // now send it to the requester
    ConnectionManager.broadcast(mb.build());
  }
Example #2
0
  // gives back the current leader if the node is aware of who the leader is // else responds I do
  // not know who the leader is/
  private void respondToWhoIsTheLeader(Management mgmt) {
    if (this.leaderNode == null) {
      logger.info("----> I cannot respond to who the leader is! I don't know!");
      return;
    }

    logger.info(
        "Node "
            + conf.getNodeId()
            + " is replying to "
            + mgmt.getHeader().getOriginator()
            + "'s request who the leader is. Its Node "
            + this.leaderNode);

    MgmtHeader.Builder mhb = MgmtHeader.newBuilder();
    mhb.setOriginator(conf.getNodeId());
    mhb.setTime(System.currentTimeMillis());
    mhb.setSecurityCode(-999);

    VectorClock.Builder rpb = VectorClock.newBuilder();
    rpb.setNodeId(conf.getNodeId());
    rpb.setTime(mhb.getTime());
    rpb.setVersion(termId);
    mhb.addPath(rpb);

    LeaderElection.Builder elb = LeaderElection.newBuilder();
    elb.setTermId(termId);
    elb.setLastLogIndex(lastLogIndex);
    elb.setAction(ElectAction.THELEADERIS);
    elb.setDesc("Node " + this.leaderNode + " is the leader");
    elb.setCandidateId(this.leaderNode);
    elb.setExpires(-1);

    Management.Builder mb = Management.newBuilder();
    mb.setHeader(mhb.build());
    mb.setElection(elb.build());

    // now send it to the requester
    try {

      ConnectionManager.getConnection(mgmt.getHeader().getOriginator(), true).write(mb.build());
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
Example #3
0
  /**
   * initiate an election from within the server - most likely scenario is the heart beat manager
   * detects a failure of the leader and calls this method.
   *
   * <p>Depending upon the algo. used (bully, flood, lcr, hs) the manager.startElection() will
   * create the algo class instance and forward processing to it. If there was an election in
   * progress and the election ID is not the same, the new election will supplant the current
   * (older) election. This should only be triggered from nodes that share an edge with the leader.
   */
  public void startElection() {
    System.out.println("Inside start election");
    this.state = RState.Candidate;
    this.termId = electionInstance().createTermId();

    LeaderElection.Builder elb = LeaderElection.newBuilder();
    elb.setTermId(this.termId);
    elb.setLastLogIndex(this.lastLogIndex);
    elb.setAction(ElectAction.DECLAREELECTION);
    elb.setDesc("Node " + conf.getNodeId() + " detects no leader. Election!");
    elb.setCandidateId(conf.getNodeId()); // promote self
    elb.setExpires(2 * 60 * 1000 + System.currentTimeMillis()); // 1 minute

    // bias the voting with my number of votes (for algos that use vote
    // counting)

    // TODO use voting int votes = conf.getNumberOfElectionVotes();

    MgmtHeader.Builder mhb = MgmtHeader.newBuilder();
    mhb.setOriginator(conf.getNodeId());
    mhb.setTime(System.currentTimeMillis());
    mhb.setSecurityCode(-999); // TODO add security

    VectorClock.Builder rpb = VectorClock.newBuilder();
    rpb.setNodeId(conf.getNodeId());
    rpb.setTime(mhb.getTime());
    rpb.setVersion(termId);
    mhb.addPath(rpb);

    Management.Builder mb = Management.newBuilder();
    mb.setHeader(mhb.build());
    mb.setElection(elb.build());

    // now send it out to all my edges
    logger.info("Election started by node " + conf.getNodeId());
    ConnectionManager.broadcast(mb.build());
  }
  /*
   * (non-Javadoc)
   *
   * @see poke.server.election.Election#process(eye.Comm.LeaderElection)
   *
   * @return The Management instance returned represents the message to send
   * on. If null, no action is required. Not a great choice to convey to the
   * caller; can be improved upon easily.
   */
  @Override
  public Management process(Management mgmt) {
    if (!mgmt.hasElection()) return null;

    LeaderElection req = mgmt.getElection();
    if (req.getExpires() <= System.currentTimeMillis()) {
      // election has expired without a conclusion?
    }

    Management rtn = null;

    if (req.getAction().getNumber() == ElectAction.DECLAREELECTION_VALUE) {
      // an election is declared!

      // required to eliminate duplicate messages - on a declaration,
      // should not happen if the network does not have cycles
      List<VectorClock> rtes = mgmt.getHeader().getPathList();
      for (VectorClock rp : rtes) {
        if (rp.getNodeId() == this.nodeId) {
          // message has already been sent to me, don't use and
          // forward
          return null;
        }
      }

      // I got here because the election is unknown to me

      // this 'if debug is on' should cover the below dozen or so
      // println()s. It is here to help with seeing when an election
      // occurs.
      if (logger.isDebugEnabled()) {}

      System.out.println("\n\n*********************************************************");
      System.out.println("   RAFT ELECTION: Election declared");
      System.out.println("   Election ID:  " + req.getElectId());
      System.out.println("   Rcv from:     Node " + mgmt.getHeader().getOriginator());
      System.out.println("   Expires:      " + new Date(req.getExpires()));
      System.out.println("   Nominates:    Node " + req.getCandidateId());
      System.out.println("   Desc:         " + req.getDesc());
      System.out.print("   Routing tbl:  [");
      for (VectorClock rp : rtes)
        System.out.print(
            "Node " + rp.getNodeId() + " (" + rp.getVersion() + "," + rp.getTime() + "), ");
      System.out.println("]");
      System.out.println("*********************************************************\n\n");

      // sync master IDs to current election
      ElectionIDGenerator.setMasterID(req.getElectId());

      /**
       * a new election can be declared over an existing election.
       *
       * <p>TODO need to have an monotonically increasing ID that we can test
       */
      boolean isNew = updateCurrent(req);
      rtn = castVote(mgmt, isNew);

    } else if (req.getAction().getNumber() == ElectAction.DECLAREVOID_VALUE) {
      // no one was elected, I am dropping into standby mode
      logger.info("TODO: no one was elected, I am dropping into standby mode");
      this.clear();
      notify(false, null);
    } else if (req.getAction().getNumber() == ElectAction.DECLAREWINNER_VALUE) {
      // some node declared itself the leader
      logger.info(
          "Election "
              + req.getElectId()
              + ": Node "
              + req.getCandidateId()
              + " is declared the leader");
      updateCurrent(mgmt.getElection());
      current.active = false; // it's over
      notify(true, req.getCandidateId());
    } else if (req.getAction().getNumber() == ElectAction.ABSTAIN_VALUE) {
      // for some reason, a node declines to vote - therefore, do nothing
    } else if (req.getAction().getNumber() == ElectAction.NOMINATE_VALUE) {
      boolean isNew = updateCurrent(mgmt.getElection());
      rtn = castVote(mgmt, isNew);
    } else {
      // this is me!
    }

    return rtn;
  }
  /**
   * cast a vote based on what I know (my ID) and where the message has traveled.
   *
   * <p>This is not a pretty piece of code, nor is the problem as we cannot ensure consistent
   * behavior.
   *
   * @param mgmt
   * @param isNew
   * @return
   */
  private synchronized Management castVote(Management mgmt, boolean isNew) {
    ElectionState st = new ElectionState();

    if (!mgmt.hasElection()) return null;

    if (current == null || !current.isActive()) {
      return null;
    }

    LeaderElection req = mgmt.getElection();
    if (req.getExpires() <= System.currentTimeMillis()) {
      logger.info("Node " + this.nodeId + " says election expired - not voting");
      return null;
    }

    logger.info("casting vote in election " + req.getElectId());

    // DANGER! If we return because this node ID is in the list, we have a
    // high chance an election will not converge as the maxHops determines
    // if the graph has been traversed!
    boolean allowCycles = true;

    if (!allowCycles) {
      List<VectorClock> rtes = mgmt.getHeader().getPathList();
      for (VectorClock rp : rtes) {
        if (rp.getNodeId() == this.nodeId) {
          // logger.info("Node " + this.nodeId +
          // " already in the routing path - not voting");
          return null;
        }
      }
    }

    // okay, the message is new (to me) so I want to determine if I should
    // nominate myself

    LeaderElection.Builder elb = LeaderElection.newBuilder();
    MgmtHeader.Builder mhb = MgmtHeader.newBuilder();
    mhb.setTime(System.currentTimeMillis());
    mhb.setSecurityCode(-999); // TODO add security

    // reversing path. If I'm the farthest a message can travel, reverse the
    // sending
    if (elb.getHops() == 0) mhb.clearPath();
    else mhb.addAllPath(mgmt.getHeader().getPathList());

    mhb.setOriginator(mgmt.getHeader().getOriginator());

    elb.setElectId(req.getElectId());
    elb.setAction(ElectAction.NOMINATE);
    elb.setDesc(req.getDesc());
    elb.setExpires(req.getExpires());
    elb.setCandidateId(req.getCandidateId());

    // my vote
    if (req.getCandidateId() == this.nodeId) {
      // if I am not in the list and the candidate is myself, I can
      // declare myself to be the leader.
      //
      // this is non-deterministic as it assumes the message has
      // reached all nodes in the network (because we know the
      // diameter or the number of nodes).
      //
      // can end up with a partitioned graph of leaders if hops <
      // diameter!

      // this notify goes out to on-node listeners and will arrive before
      // the other nodes receive notice.
      // notify(true, this.nodeId);

      current.incrementVoteCount();
      // System.out.println("Candidate:"+current.getVoteCount());
      if (current.getVoteCount() > (ConnectionManager.getNumMgmtConnections()) / 2) {
        elb.setAction(ElectAction.DECLAREWINNER);
        elb.setHops(mgmt.getHeader().getPathCount());
      } else {
        System.out.println("No majority");
        // ElectionManager.getInstance().leaderNode=null;
        return null;
      }
      logger.info("Node " + this.nodeId + " is declaring itself the leader");
      ElectionManager.getInstance().leaderNode = this.nodeId;

    } else {
      current.incrementVoteCount();
      // System.out.println("Follower"+current.getVoteCount());

      if (req.getHops() == -1) elb.setHops(-1);
      else elb.setHops(req.getHops() - 1);

      if (elb.getHops() == 0) {
        // reverse travel of the message to ensure it gets back to
        // the originator
        elb.setHops(mgmt.getHeader().getPathCount());

        // no clear winner, send back the candidate with the highest
        // known ID. So, if a candidate sees itself, it will
        // declare itself to be the winner (see above).
      } else {
        // forwarding the message on so, keep the history where the
        // message has been
        mhb.addAllPath(mgmt.getHeader().getPathList());
      }
    }
    // add myself (may allow duplicate entries, if cycling is allowed)
    VectorClock.Builder rpb = VectorClock.newBuilder();
    rpb.setNodeId(this.nodeId);
    rpb.setTime(System.currentTimeMillis());
    rpb.setVersion(req.getElectId());
    mhb.addPath(rpb);

    Management.Builder mb = Management.newBuilder();
    mb.setHeader(mhb.build());
    mb.setElection(elb.build());

    return mb.build();
  }