Compare commits

...

No commits in common. "c4cc30027ab0247eda9a647c406c47a275c9cc80" and "b8701b4aa7c06086c1e91cd8887bee9f5d487534" have entirely different histories.

2 changed files with 35 additions and 36 deletions

View File

@ -188,41 +188,36 @@ class TinyRaft extends EventEmitter
{ {
this.leader = msg.leader; this.leader = msg.leader;
this.term = msg.term; this.term = msg.term;
this.state = FOLLOWER; this.state = CANDIDATE;
this.followers = null;
this._nextTerm(this.heartbeatTimeout*2 + this.electionTimeout); this._nextTerm(this.heartbeatTimeout*2 + this.electionTimeout);
this.emit('change', { state: this.state, term: this.term, leader: this.leader }); this.emit('change', { state: this.state, term: this.term, leader: this.leader });
} }
this.send(from, { type: VOTE, term: this.term, leader: this.leader }); this.send(from, { type: VOTE, term: this.term, leader: this.leader });
} }
else if (msg.type == VOTE) else if (msg.type == VOTE && msg.term == this.term)
{
if (msg.term == this.term)
{ {
this.voted++; this.voted++;
this.votes[msg.leader] = this.votes[msg.leader] || []; this.votes[msg.leader] = this.votes[msg.leader] || [];
this.votes[msg.leader].push(from); this.votes[msg.leader].push(from);
const n = this.votes[msg.leader].length; const n = this.votes[msg.leader].length;
if (n > this.nodes.length/2 && n-1 <= this.nodes.length/2) if (n == 1 + (0 | this.nodes.length/2) && msg.leader == this.nodeId)
{ {
this.leader = msg.leader; this.leader = msg.leader;
this.state = (this.leader == this.nodeId ? LEADER : FOLLOWER); this.state = LEADER;
if (this.state == LEADER)
{
this._nextTerm(this.leadershipTimeout > 0 ? this.leadershipTimeout : -1); this._nextTerm(this.leadershipTimeout > 0 ? this.leadershipTimeout : -1);
this.followers = this.votes[this.nodeId]; this.followers = this.votes[this.nodeId];
this.emit('change', { state: this.state, term: this.term, leader: this.nodeId, followers: this.votes[this.nodeId] }); for (const follower of this.followers)
}
else
{ {
this.followers = null; // Send a heartbeat to confirm leadership
this._nextTerm(this.heartbeatTimeout*2 + this.electionTimeout); this.send(follower, { type: PING, term: this.term });
this.emit('change', { state: this.state, term: this.term, leader: this.leader });
} }
this.emit('change', { state: this.state, term: this.term, leader: this.nodeId, followers: this.votes[this.nodeId] });
} }
else if (n > this.nodes.length/2 && this.state == LEADER && msg.leader == this.nodeId) else if (n > this.nodes.length/2 && this.state == LEADER && msg.leader == this.nodeId)
{ {
this.followers = this.votes[this.nodeId]; this.followers = this.votes[this.nodeId];
// Send a heartbeat to confirm leadership
this.send(from, { type: PING, term: this.term });
this.emit('change', { state: this.state, term: this.term, leader: this.nodeId, followers: this.votes[this.nodeId] }); this.emit('change', { state: this.state, term: this.term, leader: this.nodeId, followers: this.votes[this.nodeId] });
} }
else if (this._isVotingFailed()) else if (this._isVotingFailed())
@ -230,9 +225,13 @@ class TinyRaft extends EventEmitter
this._nextTerm(0); this._nextTerm(0);
} }
} }
}
else if (msg.type == PING) else if (msg.type == PING)
{ {
if (this.state == CANDIDATE && this.term == msg.term && from == this.leader)
{
this.state = FOLLOWER;
this.emit('change', { state: this.state, term: this.term, leader: this.nodeId });
}
if (this.state == FOLLOWER && from == this.leader) if (this.state == FOLLOWER && from == this.leader)
{ {
this.markAlive(); this.markAlive();

View File

@ -67,12 +67,12 @@ function checkQuorum(nodes, count)
function checkNoQuorum(nodes) function checkNoQuorum(nodes)
{ {
const leaders = Object.values(nodes).filter(n => n.state == TinyRaft.LEADER); const nc = Object.values(nodes).filter(n => n.state != TinyRaft.CANDIDATE);
if (leaders.length > 0) if (nc.length > 0)
{ {
throw new Error('we have a leader ('+leaders.map(n => n.nodeId).join(', ')+'), but we should not'); throw new Error('we have non-candidates ('+nc.map(n => n.nodeId).join(', ')+'), but we should not');
} }
console.log('OK: '+Object.keys(nodes).length+' nodes, no quorum'); console.log('OK: '+Object.keys(nodes).length+' candidates, no quorum');
} }
async function testStartThenRemoveNode() async function testStartThenRemoveNode()