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()); }
// 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(); } }
/** * 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()); }
/** * 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(); }