@Override public boolean appendEntryRequest(Message message, int fromMemberId, int term, int prevLogTerm, int prevLogIndex, int commitIndex, boolean isHeartbeat, int entryTerm, byte[] buffer, int bufferOffset, int bufferLen, boolean lastCommand) throws Exception { cmember.addIncoming(message); return false; }
public void becomeCandidate() throws GondolaException { logger.info("[{}-{}] Becomes CANDIDATE{} {}", gondola.getHostId(), memberId, masterId >= 0 ? "-SLAVE" : "", isPrimary ? "(primary)" : ""); removeSlaves(); become(Role.CANDIDATE, -1); // Set time to send prevote prevoteTs = clock.now() + (long) ((Math.random() * prevotePeriod)); }
/** * If the term is > currentTerm, update currentTerm and become a follower. */ void updateCurrentTerm(int term, int fromMemberId) throws Exception { if (term > currentTerm) { int oldCterm = currentTerm; currentTerm = term; votedFor = -1; // Persist the new current term and voted for save(currentTerm, votedFor); // Rule for all roles if (!isFollower()) { becomeFollower(-1); logger.info("[{}-{}] Became a follower because term {} from {} is > currentTerm {}", gondola.getHostId(), memberId, term, fromMemberId, oldCterm); } } }
/** * See Member.enable(). */ public void enable(boolean on) throws GondolaException { logger.info("[{}-{}] {}", gondola.getHostId(), memberId, on ? "Enabling" : "Disabling"); enabled = on; if (isLeader()) { becomeFollower(-1); } }
if (showSummary(waitMs, false)) { logger.info(String.format("b1=%.1f, b2=%.1f, b3=%.1f, b4=%.1f b5=%.1f %.3fms/loop", 100.0 * b1 / bt, 100.0 * b2 / bt, 100.0 * b3 / bt, 100.0 * b4 / bt, 100.0 * b5 / bt, becomeFollower(-1); break; case UPDATE_STORAGE_INDEX: updateWaitingCommands(); break; case UPDATE_SAVED_INDEX: if (isLeader()) { advanceCommitIndex(); } else if (isFollower()) { sendAppendEntryReply(); updateWaitingCommands(); break; if (isLeader()) { sendHeartbeat(false); } else { logger.info("[{}-{}] Leader has not heard from enough followers", gondola.getHostId(), memberId); becomeCandidate(); if (isCandidate()) { sendRequestVote();
updateCurrentTerm(term, fromMemberId); if (term >= currentTerm && isCandidate() && voteGranted) { if (isPrevote) { if (prevotesOnly) { gondola.getHostId(), memberId, prevotes, currentTerm + 1); sendRequestVoteRequest(false); if (!isLeader() && votedFor == memberId) { long votes = peers.stream().filter(p -> p.votedTerm == currentTerm && p.votedFor == memberId) .count() + 1; becomeLeader();
updateCurrentTerm(term, fromMemberId); if (!isFollower()) { becomeFollower(fromMemberId); if (isFollower()) { peer.send(message); if (commitIndex > oldCommitIndex) { indexUpdated(false, false);
public CoreMember(Gondola gondola, Shard shard, int memberId, List<Integer> peerIds, boolean isPrimary) throws GondolaException { this.gondola = gondola; this.shard = shard; this.memberId = memberId; this.peerIds = peerIds; this.isPrimary = isPrimary; gondola.getConfig().registerForUpdates(configListener); // Acquire file lock to prevent another process running with the same member id try { fileLock(true); } catch (IOException e) { throw new GondolaException(e); } clock = gondola.getClock(); pool = gondola.getMessagePool(); storage = gondola.getStorage(); incomingQueue = new ArrayBlockingQueue<>(incomingQueueSize); saveQueue = new SaveQueue(gondola, this); commitQueue = new CommitQueue(gondola, this); gondola.getNetwork().register(memberId, channel -> acceptSlaveConnection(channel)); peerIds.forEach(id -> peers.put(id, new Peer(gondola, this, id))); // Initialize some convenience variables for use when calculating the commit index majority = (peers.size() + 1) / 2 + 1; matchIndices = new int[peers.size()]; reset(); }
public CoreMember(Gondola gondola, Cluster cluster, int memberId, List<Integer> peerIds, boolean isPrimary) throws Exception { this.gondola = gondola; this.cluster = cluster; this.memberId = memberId; this.isPrimary = isPrimary; gondola.getConfig().registerForUpdates(this); acquireFileLock(); clock = gondola.getClock(); pool = gondola.getMessagePool(); storage = gondola.getStorage(); incomingQueue = new ArrayBlockingQueue<>(incomingQueueSize); saveQueue = new SaveQueue(gondola, this); commitQueue = new CommitQueue(gondola, this); for (int id : peerIds) { Peer peer = new Peer(gondola, this, id); peers.add(peer); peerMap.put(peer.peerId, peer); } // Initialize some convenience variables for use when calculating the commit index majority = (peers.size() + 1) / 2 + 1; matchIndices = new int[peers.size()]; reset(); }
public void becomeCandidate() throws Exception { logger.info("[{}-{}] Becomes CANDIDATE {}", gondola.getHostId(), memberId, isPrimary ? "(primary)" : ""); become(Role.CANDIDATE, -1); // Set timeout requestVoteTs = clock.now() + (long) ((Math.random() * requestVotePeriod)); }
void checkHeartbeat() throws Exception { long late = clock.now() - electionTimeoutTs; if (late >= 0) { logger.info("[{}-{}] No heartbeat from {} in {}ms (timeout={}ms)", gondola.getHostId(), memberId, leaderId, electionTimeout + late, electionTimeout); becomeCandidate(); } }
Cluster(Gondola gondola, String clusterId) throws Exception { this.gondola = gondola; this.clusterId = clusterId; config = gondola.getConfig(); stats = gondola.getStats(); List<Config.ConfigMember> configMembers = config.getMembersInCluster(clusterId); List<Integer> peerIds = configMembers.stream() .filter(cm -> !cm.hostId.equals(gondola.getHostId())) .map(cm -> cm.memberId).collect(Collectors.toList()); // First create the local member, because it's needed when creating the remote members. for (int i = 0; i < configMembers.size(); i++) { Config.ConfigMember cm = configMembers.get(i); if (gondola.getHostId().equals(cm.hostId)) { // Local member boolean isPrimary = i == 0; cmember = new CoreMember(gondola, this, cm.memberId, peerIds, isPrimary); localMember = new Member(gondola, cmember, null); members.add(localMember); break; } } if (cmember == null) { throw new IllegalStateException(String.format("Host id %s not found in %d", config.getIdentifier())); } // Create list of peers for (Peer p : cmember.peers) { members.add(new Member(gondola, cmember, p)); } }
cmember.addCommand(this);
/** * Reinitializes the members after changing the contents of storage. */ public void reset() throws Exception { // Reset state variables becomeFollower(-1); lastSentTs = clock.now(); commitIndex = 0; prevotesOnly = true; // Clear queues incomingQueue.clear(); waitQueue.clear(); // Get the latest values from storage, which has been settled via becomeX(). currentTerm = storage.getCurrentTerm(memberId); votedFor = storage.getVotedFor(memberId); saveQueue.getLatest(savedRid, false); if (storageTracing) { logger.info("[{}] select({}): currentTerm={}, votedFor={} latest=({},{})", gondola.getHostId(), memberId, currentTerm, votedFor, savedRid.term, savedRid.index); } sentRid.set(savedRid); // Reset peers if (peers != null) { Arrays.fill(matchIndices, 0); for (Peer peer : peers) { peer.reset(); peer.setNextIndex(savedRid.index + 1, savedRid.index + 1); } } }
if (showSummary(waitMs, false)) { becomeFollower(-1); break; case UPDATE_STORAGE_INDEX: updateWaitingCommands(); break; case UPDATE_SAVED_INDEX: if (isLeader()) { advanceCommitIndex(); } else if (isFollower()) { sendAppendEntryReply(); updateWaitingCommands(); break; case EXECUTE: if (isLeader()) { sendHeartbeat(false); } else { logger.info("[{}-{}] Leader has not heard from enough followers", gondola.getHostId(), memberId); becomeCandidate(); if (isCandidate()) { sendPrevote();
updateCurrentTerm(term, fromMemberId); if (term >= currentTerm && isCandidate() && voteGranted) { if (isPrevote) { if (prevotesOnly) { gondola.getHostId(), memberId, prevotes, currentTerm + 1); sendRequestVoteRequest(false); if (!isLeader() && votedFor == memberId) { long votes = peers.values().stream() .filter(p -> p.votedTerm == currentTerm && p.votedFor == memberId) becomeLeader();
updateCurrentTerm(term, fromMemberId); if (!isFollower()) { becomeFollower(fromMemberId); if (isFollower()) { peer.send(message); if (commitIndex > oldCommitIndex) { indexUpdated(false, false);
public void becomeFollower(int leaderId) throws Exception { logger.info("[{}-{}] Becomes FOLLOWER of {} {}", gondola.getHostId(), memberId, leaderId, isPrimary ? "(primary)" : ""); become(Role.FOLLOWER, leaderId); // To avoid the case where this member becomes a candidate and an RV is received for the current term if (votedFor == -1 && leaderId != -1) { storage.saveVote(memberId, currentTerm, leaderId); } // Set timeout electionTimeoutTs = clock.now() + electionTimeout; }
void checkHeartbeat() throws GondolaException { long late = clock.now() - electionTimeoutTs; if (late >= 0) { logger.info("[{}-{}] No heartbeat from {} in {}ms (timeout={}ms)", gondola.getHostId(), memberId, leaderId, electionTimeout + late, electionTimeout); becomeCandidate(); } }
public Shard(Gondola gondola, String shardId) throws GondolaException { this.gondola = gondola; this.shardId = shardId; config = gondola.getConfig(); stats = gondola.getStats(); List<Config.ConfigMember> configMembers = config.getMembersInShard(shardId); List<Integer> peerIds = configMembers.stream() .filter(cm -> !cm.hostId.equals(gondola.getHostId())) .map(cm -> cm.memberId).collect(Collectors.toList()); // First create the local member, because it's needed when creating the remote members. for (int i = 0; i < configMembers.size(); i++) { Config.ConfigMember cm = configMembers.get(i); if (gondola.getHostId().equals(cm.hostId)) { // Local member boolean isPrimary = i == 0; cmember = new CoreMember(gondola, this, cm.memberId, peerIds, isPrimary); localMember = new Member(gondola, cmember, null); members.add(localMember); break; } } if (cmember == null) { throw new IllegalStateException(String.format("Host id %s not found in %d", config.getIdentifier())); } // Create list of peers for (Peer p : cmember.peers.values()) { members.add(new Member(gondola, cmember, p)); } }