Search in sources :

Example 16 with Status

use of io.dingodb.raft.Status in project dingo by dingodb.

the class NodeImpl method handleAppendEntriesRequest.

@Override
public Message handleAppendEntriesRequest(final RpcRequests.AppendEntriesRequest request, final RpcRequestClosure done) {
    boolean doUnlock = true;
    final long startMs = Utils.monotonicMs();
    this.writeLock.lock();
    final int entriesCount = request.getEntriesCount();
    try {
        if (!this.state.isActive()) {
            LOG.warn("Node {} is not in active state, currTerm={}.", getNodeId(), this.currTerm);
            return // 
            RpcFactoryHelper.responseFactory().newResponse(RpcRequests.AppendEntriesResponse.getDefaultInstance(), RaftError.EINVAL, "Node %s is not in active state, state %s.", getNodeId(), this.state.name());
        }
        final PeerId serverId = new PeerId();
        if (!serverId.parse(request.getServerId())) {
            LOG.warn("Node {} received AppendEntriesRequest from {} serverId bad format.", getNodeId(), request.getServerId());
            return // 
            RpcFactoryHelper.responseFactory().newResponse(RpcRequests.AppendEntriesResponse.getDefaultInstance(), RaftError.EINVAL, "Parse serverId failed: %s.", request.getServerId());
        }
        // Check stale term
        if (request.getTerm() < this.currTerm) {
            LOG.warn("Node {} ignore stale AppendEntriesRequest from {}, term={}, currTerm={}.", getNodeId(), request.getServerId(), request.getTerm(), this.currTerm);
            return // 
            RpcRequests.AppendEntriesResponse.newBuilder().setSuccess(// 
            false).setTerm(// 
            this.currTerm).build();
        }
        // Check term and state to step down
        checkStepDown(request.getTerm(), serverId);
        if (!serverId.equals(this.leaderId)) {
            LOG.error("Another peer {} declares that it is the leader at term {} which was occupied by leader {}.", serverId, this.currTerm, this.leaderId);
            // Increase the term by 1 and make both leaders step down to minimize the
            // loss of split brain
            stepDown(request.getTerm() + 1, false, new Status(RaftError.ELEADERCONFLICT, "More than one leader in the same term."));
            return // 
            RpcRequests.AppendEntriesResponse.newBuilder().setSuccess(// 
            false).setTerm(// 
            request.getTerm() + 1).build();
        }
        updateLastLeaderTimestamp(Utils.monotonicMs());
        if (entriesCount > 0 && this.snapshotExecutor != null && this.snapshotExecutor.isInstallingSnapshot()) {
            LOG.warn("Node {} received AppendEntriesRequest while installing snapshot.", getNodeId());
            return // 
            RpcFactoryHelper.responseFactory().newResponse(RpcRequests.AppendEntriesResponse.getDefaultInstance(), RaftError.EBUSY, "Node %s:%s is installing snapshot.", this.groupId, this.serverId);
        }
        final long prevLogIndex = request.getPrevLogIndex();
        final long prevLogTerm = request.getPrevLogTerm();
        final long localPrevLogTerm = this.logManager.getTerm(prevLogIndex);
        if (localPrevLogTerm != prevLogTerm) {
            final long lastLogIndex = this.logManager.getLastLogIndex();
            LOG.warn("Node {} reject term_unmatched AppendEntriesRequest from {}, term={}, prevLogIndex={}, prevLogTerm={}, localPrevLogTerm={}, lastLogIndex={}, entriesSize={}.", getNodeId(), request.getServerId(), request.getTerm(), prevLogIndex, prevLogTerm, localPrevLogTerm, lastLogIndex, entriesCount);
            return // 
            RpcRequests.AppendEntriesResponse.newBuilder().setSuccess(// 
            false).setTerm(// 
            this.currTerm).setLastLogIndex(// 
            lastLogIndex).build();
        }
        if (entriesCount == 0) {
            // heartbeat or probe request
            final RpcRequests.AppendEntriesResponse.Builder respBuilder = // 
            RpcRequests.AppendEntriesResponse.newBuilder().setSuccess(// 
            true).setTerm(// 
            this.currTerm).setLastLogIndex(this.logManager.getLastLogIndex());
            doUnlock = false;
            this.writeLock.unlock();
            // see the comments at FollowerStableClosure#run()
            this.ballotBox.setLastCommittedIndex(Math.min(request.getCommittedIndex(), prevLogIndex));
            return respBuilder.build();
        }
        // Parse request
        long index = prevLogIndex;
        final List<LogEntry> entries = new ArrayList<>(entriesCount);
        ByteBuffer allData = null;
        if (request.hasData()) {
            allData = request.getData().asReadOnlyByteBuffer();
        }
        final List<RaftOutter.EntryMeta> entriesList = request.getEntriesList();
        for (int i = 0; i < entriesCount; i++) {
            index++;
            final RaftOutter.EntryMeta entry = entriesList.get(i);
            final LogEntry logEntry = logEntryFromMeta(index, allData, entry);
            if (logEntry != null) {
                // Validate checksum
                if (this.raftOptions.isEnableLogEntryChecksum() && logEntry.isCorrupted()) {
                    long realChecksum = logEntry.checksum();
                    LOG.error("Corrupted log entry received from leader, index={}, term={}, expectedChecksum={}, realChecksum={}", logEntry.getId().getIndex(), logEntry.getId().getTerm(), logEntry.getChecksum(), realChecksum);
                    return // 
                    RpcFactoryHelper.responseFactory().newResponse(RpcRequests.AppendEntriesResponse.getDefaultInstance(), RaftError.EINVAL, "The log entry is corrupted, index=%d, term=%d, expectedChecksum=%d, realChecksum=%d", logEntry.getId().getIndex(), logEntry.getId().getTerm(), logEntry.getChecksum(), realChecksum);
                }
                entries.add(logEntry);
            }
        }
        final FollowerStableClosure closure = new FollowerStableClosure(request, RpcRequests.AppendEntriesResponse.newBuilder().setTerm(this.currTerm), this, done, this.currTerm);
        this.logManager.appendEntries(entries, closure);
        // update configuration after _log_manager updated its memory status
        checkAndSetConfiguration(true);
        return null;
    } finally {
        if (doUnlock) {
            this.writeLock.unlock();
        }
        this.metrics.recordLatency("handle-append-entries", Utils.monotonicMs() - startMs);
        this.metrics.recordSize("handle-append-entries-count", entriesCount);
    }
}
Also used : Status(io.dingodb.raft.Status) RaftOutter(io.dingodb.raft.entity.RaftOutter) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) ArrayList(java.util.ArrayList) ByteBuffer(java.nio.ByteBuffer) LogEntry(io.dingodb.raft.entity.LogEntry) PeerId(io.dingodb.raft.entity.PeerId)

Example 17 with Status

use of io.dingodb.raft.Status in project dingo by dingodb.

the class NodeImpl method handleReadIndexRequest.

/**
 * Handle read index request.
 */
@Override
public void handleReadIndexRequest(final RpcRequests.ReadIndexRequest request, final RpcResponseClosure<RpcRequests.ReadIndexResponse> done) {
    final long startMs = Utils.monotonicMs();
    this.readLock.lock();
    try {
        switch(this.state) {
            case STATE_LEADER:
                readLeader(request, RpcRequests.ReadIndexResponse.newBuilder(), done);
                break;
            case STATE_FOLLOWER:
                readFollower(request, done);
                break;
            case STATE_TRANSFERRING:
                done.run(new Status(RaftError.EBUSY, "Is transferring leadership."));
                break;
            default:
                done.run(new Status(RaftError.EPERM, "Invalid state for readIndex: %s.", this.state));
                break;
        }
    } finally {
        this.readLock.unlock();
        this.metrics.recordLatency("handle-read-index", Utils.monotonicMs() - startMs);
        this.metrics.recordSize("handle-read-index-entries", request.getEntriesCount());
    }
}
Also used : Status(io.dingodb.raft.Status)

Example 18 with Status

use of io.dingodb.raft.Status in project dingo by dingodb.

the class NodeImpl method handleElectionTimeout.

private void handleElectionTimeout() {
    boolean doUnlock = true;
    this.writeLock.lock();
    try {
        if (this.state != State.STATE_FOLLOWER) {
            return;
        }
        if (isCurrentLeaderValid()) {
            return;
        }
        resetLeaderId(PeerId.emptyPeer(), new Status(RaftError.ERAFTTIMEDOUT, "Lost connection from leader %s.", this.leaderId));
        // Judge whether to launch a election.
        if (!allowLaunchElection()) {
            return;
        }
        doUnlock = false;
        preVote();
    } finally {
        if (doUnlock) {
            this.writeLock.unlock();
        }
    }
}
Also used : Status(io.dingodb.raft.Status)

Example 19 with Status

use of io.dingodb.raft.Status in project dingo by dingodb.

the class NodeImpl method readLeader.

private void readLeader(final RpcRequests.ReadIndexRequest request, final RpcRequests.ReadIndexResponse.Builder respBuilder, final RpcResponseClosure<RpcRequests.ReadIndexResponse> closure) {
    final int quorum = getQuorum();
    if (quorum <= 1) {
        // Only one peer, fast path.
        // 
        respBuilder.setSuccess(true).setIndex(this.ballotBox.getLastCommittedIndex());
        closure.setResponse(respBuilder.build());
        closure.run(Status.OK());
        return;
    }
    final long lastCommittedIndex = this.ballotBox.getLastCommittedIndex();
    if (this.logManager.getTerm(lastCommittedIndex) != this.currTerm) {
        // Reject read only request when this leader has not committed any log entry at its term
        closure.run(new Status(RaftError.EAGAIN, "ReadIndex request rejected because leader has not committed any log entry at its term, logIndex=%d, currTerm=%d.", lastCommittedIndex, this.currTerm));
        return;
    }
    respBuilder.setIndex(lastCommittedIndex);
    if (request.getPeerId() != null) {
        // request from follower or learner, check if the follower/learner is in current conf.
        final PeerId peer = new PeerId();
        peer.parse(request.getServerId());
        if (!this.conf.contains(peer) && !this.conf.containsLearner(peer)) {
            closure.run(new Status(RaftError.EPERM, "Peer %s is not in current configuration: %s.", peer, this.conf));
            return;
        }
    }
    ReadOnlyOption readOnlyOpt = this.raftOptions.getReadOnlyOptions();
    if (readOnlyOpt == ReadOnlyOption.ReadOnlyLeaseBased && !isLeaderLeaseValid()) {
        // If leader lease timeout, we must change option to ReadOnlySafe
        readOnlyOpt = ReadOnlyOption.ReadOnlySafe;
    }
    switch(readOnlyOpt) {
        case ReadOnlySafe:
            final List<PeerId> peers = this.conf.getConf().getPeers();
            Requires.requireTrue(peers != null && !peers.isEmpty(), "Empty peers");
            final ReadIndexHeartbeatResponseClosure heartbeatDone = new ReadIndexHeartbeatResponseClosure(closure, respBuilder, quorum, peers.size());
            // Send heartbeat requests to followers
            for (final PeerId peer : peers) {
                if (peer.equals(this.serverId)) {
                    continue;
                }
                this.replicatorGroup.sendHeartbeat(peer, heartbeatDone);
            }
            break;
        case ReadOnlyLeaseBased:
            // Responses to followers and local node.
            respBuilder.setSuccess(true);
            closure.setResponse(respBuilder.build());
            closure.run(Status.OK());
            break;
    }
}
Also used : Status(io.dingodb.raft.Status) ReadOnlyOption(io.dingodb.raft.option.ReadOnlyOption) PeerId(io.dingodb.raft.entity.PeerId)

Example 20 with Status

use of io.dingodb.raft.Status in project dingo by dingodb.

the class NodeImpl method electSelf.

// should be in writeLock
private void electSelf() {
    long oldTerm;
    try {
        LOG.info("Node {} start vote and grant vote self, term={}.", getNodeId(), this.currTerm);
        if (!this.conf.contains(this.serverId)) {
            LOG.warn("Node {} can't do electSelf as it is not in {}.", getNodeId(), this.conf);
            return;
        }
        if (this.state == State.STATE_FOLLOWER) {
            LOG.debug("Node {} stop election timer, term={}.", getNodeId(), this.currTerm);
            this.electionTimer.stop();
        }
        resetLeaderId(PeerId.emptyPeer(), new Status(RaftError.ERAFTTIMEDOUT, "A follower's leader_id is reset to NULL as it begins to request_vote."));
        this.state = State.STATE_CANDIDATE;
        this.currTerm++;
        this.votedId = this.serverId.copy();
        LOG.debug("Node {} start vote timer, term={} .", getNodeId(), this.currTerm);
        this.voteTimer.start();
        this.voteCtx.init(this.conf.getConf(), this.conf.isStable() ? null : this.conf.getOldConf());
        oldTerm = this.currTerm;
    } finally {
        this.writeLock.unlock();
    }
    final LogId lastLogId = this.logManager.getLastLogId(true);
    this.writeLock.lock();
    try {
        // vote need defense ABA after unlock&writeLock
        if (oldTerm != this.currTerm) {
            LOG.warn("Node {} raise term {} when getLastLogId.", getNodeId(), this.currTerm);
            return;
        }
        for (final PeerId peer : this.conf.listPeers()) {
            if (peer.equals(this.serverId)) {
                continue;
            }
            if (!this.rpcService.connect(peer.getEndpoint())) {
                LOG.warn("Node {} channel init failed, address={}.", getNodeId(), peer.getEndpoint());
                continue;
            }
            final OnRequestVoteRpcDone done = new OnRequestVoteRpcDone(peer, this.currTerm, this);
            done.request = // 
            RpcRequests.RequestVoteRequest.newBuilder().setPreVote(// It's not a pre-vote request.
            false).setGroupId(// 
            this.groupId).setServerId(// 
            this.serverId.toString()).setPeerId(// 
            peer.toString()).setTerm(// 
            this.currTerm).setLastLogIndex(// 
            lastLogId.getIndex()).setLastLogTerm(// 
            lastLogId.getTerm()).build();
            this.rpcService.requestVote(peer.getEndpoint(), done.request, done);
        }
        this.metaStorage.setTermAndVotedFor(this.currTerm, this.serverId);
        this.voteCtx.grant(this.serverId);
        if (this.voteCtx.isGranted()) {
            becomeLeader();
        }
    } finally {
        this.writeLock.unlock();
    }
}
Also used : Status(io.dingodb.raft.Status) LogId(io.dingodb.raft.entity.LogId) PeerId(io.dingodb.raft.entity.PeerId)

Aggregations

Status (io.dingodb.raft.Status)112 PeerId (io.dingodb.raft.entity.PeerId)31 BaseKVStoreClosure (io.dingodb.store.row.storage.BaseKVStoreClosure)21 Message (com.google.protobuf.Message)19 Configuration (io.dingodb.raft.conf.Configuration)12 RpcRequests (io.dingodb.raft.rpc.RpcRequests)12 JRaftException (io.dingodb.raft.error.JRaftException)11 LogId (io.dingodb.raft.entity.LogId)10 RaftException (io.dingodb.raft.error.RaftException)9 ArrayList (java.util.ArrayList)9 ConfigurationEntry (io.dingodb.raft.conf.ConfigurationEntry)7 LogEntry (io.dingodb.raft.entity.LogEntry)7 ByteBuffer (java.nio.ByteBuffer)7 RaftOutter (io.dingodb.raft.entity.RaftOutter)6 BlockingWaitStrategy (com.lmax.disruptor.BlockingWaitStrategy)5 Disruptor (com.lmax.disruptor.dsl.Disruptor)5 ProducerType (com.lmax.disruptor.dsl.ProducerType)5 Closure (io.dingodb.raft.Closure)5 FSMCaller (io.dingodb.raft.FSMCaller)5 Node (io.dingodb.raft.Node)5