All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.protocols.raft.Leader Maven / Gradle / Ivy

The newest version!
package org.jgroups.protocols.raft;

import org.jgroups.Address;
import org.jgroups.Message;
import org.jgroups.ObjectMessage;
import org.jgroups.logging.Log;
import org.jgroups.raft.util.CommitTable;
import org.jgroups.raft.util.RequestTable;
import org.jgroups.util.ExtendedUUID;
import org.jgroups.util.Util;

import java.util.function.Supplier;

/**
 * Implements the behavior of a RAFT leader
 * @author Bela Ban
 * @since  0.1
 */
public class Leader extends RaftImpl {
    protected final Supplier majority=() -> raft.majority();

    public Leader(RAFT raft) {
        super(raft);
    }


    public void init() {
        super.init();
        raft.createRequestTable();
        raft.createCommitTable();
    }

    public void destroy() {
        super.destroy();
        RequestTable reqTable = raft.request_table;
        raft.request_table=null;
        raft.commit_table=null;

        if (reqTable != null) reqTable.destroy(raft.notCurrentLeader());
    }


    @Override
    public void handleAppendEntriesResponse(Address sender, long term, AppendResult result) {
        RequestTable reqtab=raft.request_table;
        if(reqtab == null)
            throw new IllegalStateException("request table cannot be null in leader");
        ExtendedUUID uuid=(ExtendedUUID)sender;
        String sender_raft_id=Util.bytesToString(uuid.get(RAFT.raft_id_key));
        Log log=raft.getLog();
        if(log.isTraceEnabled())
            log.trace("%s: received AppendEntries response from %s for term %d: %s", raft.getAddress(), sender, term, result);
        switch(result.result) {
            case OK:
                raft.commit_table.update(sender, result.index(), result.index() + 1, result.commit_index, false);
                boolean done = reqtab.add(result.index, sender_raft_id, this.majority);
                if(done) {
                    raft.commitLogTo(result.index, true);
                }
                // Send commits immediately.
                // Note that, an entry is committed by a MAJORITY, this means that some of the nodes doesn't know the entry exist yet.
                // This way, send the commit messages any time we handle an append response.
                if(raft.send_commits_immediately) {
                    // Done is only true when reaching a majority threshold, we also need to check is committed to resend
                    // to slower nodes.
                    if (done || reqtab.isCommitted(result.index))
                        sendCommitMessageToFollowers();
                }
                break;
                // todo: change
            case FAIL_ENTRY_NOT_FOUND:
                raft.commit_table.update(sender, result.index(), result.index()+1, result.commit_index, true);
                break;
            case FAIL_CONFLICTING_PREV_TERM:
                raft.commit_table.update(sender, result.index()-1, result.index(), result.commit_index, true, true);
                break;
        }
    }

    private void sendCommitMessageToFollowers() {
        raft.commit_table.forEach(this::sendCommitMessageToFollower);
    }

    private void sendCommitMessageToFollower(Address member, CommitTable.Entry entry) {
        if(raft.commit_index > entry.commitIndex()) {
            long cterm=raft.currentTerm();
            short id=raft.getId();
            Address leader=raft.getAddress();
            Message msg=new ObjectMessage(member, null)
              .putHeader(id, new AppendEntriesRequest(leader, cterm, 0, 0, cterm, raft.commit_index));
            raft.down(msg);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy