Search in sources :

Example 11 with LogId

use of com.alipay.sofa.jraft.entity.LogId in project sofa-jraft by sofastack.

the class NodeImpl method init.

@Override
public boolean init(final NodeOptions opts) {
    Requires.requireNonNull(opts, "Null node options");
    Requires.requireNonNull(opts.getRaftOptions(), "Null raft options");
    Requires.requireNonNull(opts.getServiceFactory(), "Null jraft service factory");
    this.serviceFactory = opts.getServiceFactory();
    this.options = opts;
    this.raftOptions = opts.getRaftOptions();
    this.metrics = new NodeMetrics(opts.isEnableMetrics());
    this.serverId.setPriority(opts.getElectionPriority());
    this.electionTimeoutCounter = 0;
    if (this.serverId.getIp().equals(Utils.IP_ANY)) {
        LOG.error("Node can't started from IP_ANY.");
        return false;
    }
    if (!NodeManager.getInstance().serverExists(this.serverId.getEndpoint())) {
        LOG.error("No RPC server attached to, did you forget to call addService?");
        return false;
    }
    this.timerManager = TIMER_FACTORY.getRaftScheduler(this.options.isSharedTimerPool(), this.options.getTimerPoolSize(), "JRaft-Node-ScheduleThreadPool");
    // Init timers
    final String suffix = getNodeId().toString();
    String name = "JRaft-VoteTimer-" + suffix;
    this.voteTimer = new RepeatedTimer(name, this.options.getElectionTimeoutMs(), TIMER_FACTORY.getVoteTimer(this.options.isSharedVoteTimer(), name)) {

        @Override
        protected void onTrigger() {
            handleVoteTimeout();
        }

        @Override
        protected int adjustTimeout(final int timeoutMs) {
            return randomTimeout(timeoutMs);
        }
    };
    name = "JRaft-ElectionTimer-" + suffix;
    this.electionTimer = new RepeatedTimer(name, this.options.getElectionTimeoutMs(), TIMER_FACTORY.getElectionTimer(this.options.isSharedElectionTimer(), name)) {

        @Override
        protected void onTrigger() {
            handleElectionTimeout();
        }

        @Override
        protected int adjustTimeout(final int timeoutMs) {
            return randomTimeout(timeoutMs);
        }
    };
    name = "JRaft-StepDownTimer-" + suffix;
    this.stepDownTimer = new RepeatedTimer(name, this.options.getElectionTimeoutMs() >> 1, TIMER_FACTORY.getStepDownTimer(this.options.isSharedStepDownTimer(), name)) {

        @Override
        protected void onTrigger() {
            handleStepDownTimeout();
        }
    };
    name = "JRaft-SnapshotTimer-" + suffix;
    this.snapshotTimer = new RepeatedTimer(name, this.options.getSnapshotIntervalSecs() * 1000, TIMER_FACTORY.getSnapshotTimer(this.options.isSharedSnapshotTimer(), name)) {

        private volatile boolean firstSchedule = true;

        @Override
        protected void onTrigger() {
            handleSnapshotTimeout();
        }

        @Override
        protected int adjustTimeout(final int timeoutMs) {
            if (!this.firstSchedule) {
                return timeoutMs;
            }
            // Randomize the first snapshot trigger timeout
            this.firstSchedule = false;
            if (timeoutMs > 0) {
                int half = timeoutMs / 2;
                return half + ThreadLocalRandom.current().nextInt(half);
            } else {
                return timeoutMs;
            }
        }
    };
    this.configManager = new ConfigurationManager();
    this.applyDisruptor = // 
    DisruptorBuilder.<LogEntryAndClosure>newInstance().setRingBufferSize(// 
    this.raftOptions.getDisruptorBufferSize()).setEventFactory(// 
    new LogEntryAndClosureFactory()).setThreadFactory(// 
    new NamedThreadFactory("JRaft-NodeImpl-Disruptor-", true)).setProducerType(// 
    ProducerType.MULTI).setWaitStrategy(// 
    new BlockingWaitStrategy()).build();
    this.applyDisruptor.handleEventsWith(new LogEntryAndClosureHandler());
    this.applyDisruptor.setDefaultExceptionHandler(new LogExceptionHandler<Object>(getClass().getSimpleName()));
    this.applyQueue = this.applyDisruptor.start();
    if (this.metrics.getMetricRegistry() != null) {
        this.metrics.getMetricRegistry().register("jraft-node-impl-disruptor", new DisruptorMetricSet(this.applyQueue));
    }
    this.fsmCaller = new FSMCallerImpl();
    if (!initLogStorage()) {
        LOG.error("Node {} initLogStorage failed.", getNodeId());
        return false;
    }
    if (!initMetaStorage()) {
        LOG.error("Node {} initMetaStorage failed.", getNodeId());
        return false;
    }
    if (!initFSMCaller(new LogId(0, 0))) {
        LOG.error("Node {} initFSMCaller failed.", getNodeId());
        return false;
    }
    this.ballotBox = new BallotBox();
    final BallotBoxOptions ballotBoxOpts = new BallotBoxOptions();
    ballotBoxOpts.setWaiter(this.fsmCaller);
    ballotBoxOpts.setClosureQueue(this.closureQueue);
    if (!this.ballotBox.init(ballotBoxOpts)) {
        LOG.error("Node {} init ballotBox failed.", getNodeId());
        return false;
    }
    if (!initSnapshotStorage()) {
        LOG.error("Node {} initSnapshotStorage failed.", getNodeId());
        return false;
    }
    final Status st = this.logManager.checkConsistency();
    if (!st.isOk()) {
        LOG.error("Node {} is initialized with inconsistent log, status={}.", getNodeId(), st);
        return false;
    }
    this.conf = new ConfigurationEntry();
    this.conf.setId(new LogId());
    // if have log using conf in log, else using conf in options
    if (this.logManager.getLastLogIndex() > 0) {
        checkAndSetConfiguration(false);
    } else {
        this.conf.setConf(this.options.getInitialConf());
        // initially set to max(priority of all nodes)
        this.targetPriority = getMaxPriorityOfNodes(this.conf.getConf().getPeers());
    }
    if (!this.conf.isEmpty()) {
        Requires.requireTrue(this.conf.isValid(), "Invalid conf: %s", this.conf);
    } else {
        LOG.info("Init node {} with empty conf.", this.serverId);
    }
    // TODO RPC service and ReplicatorGroup is in cycle dependent, refactor it
    this.replicatorGroup = new ReplicatorGroupImpl();
    this.rpcService = new DefaultRaftClientService(this.replicatorGroup);
    final ReplicatorGroupOptions rgOpts = new ReplicatorGroupOptions();
    rgOpts.setHeartbeatTimeoutMs(heartbeatTimeout(this.options.getElectionTimeoutMs()));
    rgOpts.setElectionTimeoutMs(this.options.getElectionTimeoutMs());
    rgOpts.setLogManager(this.logManager);
    rgOpts.setBallotBox(this.ballotBox);
    rgOpts.setNode(this);
    rgOpts.setRaftRpcClientService(this.rpcService);
    rgOpts.setSnapshotStorage(this.snapshotExecutor != null ? this.snapshotExecutor.getSnapshotStorage() : null);
    rgOpts.setRaftOptions(this.raftOptions);
    rgOpts.setTimerManager(this.timerManager);
    // Adds metric registry to RPC service.
    this.options.setMetricRegistry(this.metrics.getMetricRegistry());
    if (!this.rpcService.init(this.options)) {
        LOG.error("Fail to init rpc service.");
        return false;
    }
    this.replicatorGroup.init(new NodeId(this.groupId, this.serverId), rgOpts);
    this.readOnlyService = new ReadOnlyServiceImpl();
    final ReadOnlyServiceOptions rosOpts = new ReadOnlyServiceOptions();
    rosOpts.setFsmCaller(this.fsmCaller);
    rosOpts.setNode(this);
    rosOpts.setRaftOptions(this.raftOptions);
    if (!this.readOnlyService.init(rosOpts)) {
        LOG.error("Fail to init readOnlyService.");
        return false;
    }
    // set state to follower
    this.state = State.STATE_FOLLOWER;
    if (LOG.isInfoEnabled()) {
        LOG.info("Node {} init, term={}, lastLogId={}, conf={}, oldConf={}.", getNodeId(), this.currTerm, this.logManager.getLastLogId(false), this.conf.getConf(), this.conf.getOldConf());
    }
    if (this.snapshotExecutor != null && this.options.getSnapshotIntervalSecs() > 0) {
        LOG.debug("Node {} start snapshot timer, term={}.", getNodeId(), this.currTerm);
        this.snapshotTimer.start();
    }
    if (!this.conf.isEmpty()) {
        stepDown(this.currTerm, false, new Status());
    }
    if (!NodeManager.getInstance().add(this)) {
        LOG.error("NodeManager add {} failed.", getNodeId());
        return false;
    }
    // Now the raft node is started , have to acquire the writeLock to avoid race
    // conditions
    this.writeLock.lock();
    if (this.conf.isStable() && this.conf.getConf().size() == 1 && this.conf.getConf().contains(this.serverId)) {
        // The group contains only this server which must be the LEADER, trigger
        // the timer immediately.
        electSelf();
    } else {
        this.writeLock.unlock();
    }
    return true;
}
Also used : BallotBoxOptions(com.alipay.sofa.jraft.option.BallotBoxOptions) ReadOnlyServiceOptions(com.alipay.sofa.jraft.option.ReadOnlyServiceOptions) RepeatedTimer(com.alipay.sofa.jraft.util.RepeatedTimer) ConfigurationManager(com.alipay.sofa.jraft.conf.ConfigurationManager) ConfigurationEntry(com.alipay.sofa.jraft.conf.ConfigurationEntry) Status(com.alipay.sofa.jraft.Status) BlockingWaitStrategy(com.lmax.disruptor.BlockingWaitStrategy) NamedThreadFactory(com.alipay.sofa.jraft.util.NamedThreadFactory) DisruptorMetricSet(com.alipay.sofa.jraft.util.DisruptorMetricSet) ReplicatorGroupOptions(com.alipay.sofa.jraft.option.ReplicatorGroupOptions) NodeId(com.alipay.sofa.jraft.entity.NodeId) DefaultRaftClientService(com.alipay.sofa.jraft.rpc.impl.core.DefaultRaftClientService) LogId(com.alipay.sofa.jraft.entity.LogId)

Example 12 with LogId

use of com.alipay.sofa.jraft.entity.LogId in project sofa-jraft by sofastack.

the class NodeImpl method handleRequestVoteRequest.

@Override
public Message handleRequestVoteRequest(final RequestVoteRequest request) {
    boolean doUnlock = true;
    this.writeLock.lock();
    try {
        if (!this.state.isActive()) {
            LOG.warn("Node {} is not in active state, currTerm={}.", getNodeId(), this.currTerm);
            return // 
            RpcFactoryHelper.responseFactory().newResponse(RequestVoteResponse.getDefaultInstance(), RaftError.EINVAL, "Node %s is not in active state, state %s.", getNodeId(), this.state.name());
        }
        final PeerId candidateId = new PeerId();
        if (!candidateId.parse(request.getServerId())) {
            LOG.warn("Node {} received RequestVoteRequest from {} serverId bad format.", getNodeId(), request.getServerId());
            return // 
            RpcFactoryHelper.responseFactory().newResponse(RequestVoteResponse.getDefaultInstance(), RaftError.EINVAL, "Parse candidateId failed: %s.", request.getServerId());
        }
        // noinspection ConstantConditions
        do {
            // check term
            if (request.getTerm() >= this.currTerm) {
                LOG.info("Node {} received RequestVoteRequest from {}, term={}, currTerm={}.", getNodeId(), request.getServerId(), request.getTerm(), this.currTerm);
                // increase current term, change state to follower
                if (request.getTerm() > this.currTerm) {
                    stepDown(request.getTerm(), false, new Status(RaftError.EHIGHERTERMRESPONSE, "Raft node receives higher term RequestVoteRequest."));
                }
            } else {
                // ignore older term
                LOG.info("Node {} ignore RequestVoteRequest from {}, term={}, currTerm={}.", getNodeId(), request.getServerId(), request.getTerm(), this.currTerm);
                break;
            }
            doUnlock = false;
            this.writeLock.unlock();
            final LogId lastLogId = this.logManager.getLastLogId(true);
            doUnlock = true;
            this.writeLock.lock();
            // vote need ABA check after unlock&writeLock
            if (request.getTerm() != this.currTerm) {
                LOG.warn("Node {} raise term {} when get lastLogId.", getNodeId(), this.currTerm);
                break;
            }
            final boolean logIsOk = new LogId(request.getLastLogIndex(), request.getLastLogTerm()).compareTo(lastLogId) >= 0;
            if (logIsOk && (this.votedId == null || this.votedId.isEmpty())) {
                stepDown(request.getTerm(), false, new Status(RaftError.EVOTEFORCANDIDATE, "Raft node votes for some candidate, step down to restart election_timer."));
                this.votedId = candidateId.copy();
                this.metaStorage.setVotedFor(candidateId);
            }
        } while (false);
        return // 
        RequestVoteResponse.newBuilder().setTerm(// 
        this.currTerm).setGranted(// 
        request.getTerm() == this.currTerm && candidateId.equals(this.votedId)).build();
    } finally {
        if (doUnlock) {
            this.writeLock.unlock();
        }
    }
}
Also used : Status(com.alipay.sofa.jraft.Status) LogId(com.alipay.sofa.jraft.entity.LogId) PeerId(com.alipay.sofa.jraft.entity.PeerId)

Example 13 with LogId

use of com.alipay.sofa.jraft.entity.LogId in project sofa-jraft by sofastack.

the class NodeImpl method logEntryFromMeta.

private LogEntry logEntryFromMeta(final long index, final ByteBuffer allData, final RaftOutter.EntryMeta entry) {
    if (entry.getType() != EnumOutter.EntryType.ENTRY_TYPE_UNKNOWN) {
        final LogEntry logEntry = new LogEntry();
        logEntry.setId(new LogId(index, entry.getTerm()));
        logEntry.setType(entry.getType());
        if (entry.hasChecksum()) {
            // since 1.2.6
            logEntry.setChecksum(entry.getChecksum());
        }
        final long dataLen = entry.getDataLen();
        if (dataLen > 0) {
            final byte[] bs = new byte[(int) dataLen];
            assert allData != null;
            allData.get(bs, 0, bs.length);
            logEntry.setData(ByteBuffer.wrap(bs));
        }
        if (entry.getPeersCount() > 0) {
            if (entry.getType() != EnumOutter.EntryType.ENTRY_TYPE_CONFIGURATION) {
                throw new IllegalStateException("Invalid log entry that contains peers but is not ENTRY_TYPE_CONFIGURATION type: " + entry.getType());
            }
            fillLogEntryPeers(entry, logEntry);
        } else if (entry.getType() == EnumOutter.EntryType.ENTRY_TYPE_CONFIGURATION) {
            throw new IllegalStateException("Invalid log entry that contains zero peers but is ENTRY_TYPE_CONFIGURATION type");
        }
        return logEntry;
    }
    return null;
}
Also used : LogId(com.alipay.sofa.jraft.entity.LogId) LogEntry(com.alipay.sofa.jraft.entity.LogEntry)

Example 14 with LogId

use of com.alipay.sofa.jraft.entity.LogId in project sofa-jraft by sofastack.

the class NodeImpl method handlePreVoteRequest.

@Override
public Message handlePreVoteRequest(final RequestVoteRequest request) {
    boolean doUnlock = true;
    this.writeLock.lock();
    try {
        if (!this.state.isActive()) {
            LOG.warn("Node {} is not in active state, currTerm={}.", getNodeId(), this.currTerm);
            return // 
            RpcFactoryHelper.responseFactory().newResponse(RequestVoteResponse.getDefaultInstance(), RaftError.EINVAL, "Node %s is not in active state, state %s.", getNodeId(), this.state.name());
        }
        final PeerId candidateId = new PeerId();
        if (!candidateId.parse(request.getServerId())) {
            LOG.warn("Node {} received PreVoteRequest from {} serverId bad format.", getNodeId(), request.getServerId());
            return // 
            RpcFactoryHelper.responseFactory().newResponse(RequestVoteResponse.getDefaultInstance(), RaftError.EINVAL, "Parse candidateId failed: %s.", request.getServerId());
        }
        boolean granted = false;
        // noinspection ConstantConditions
        do {
            if (!this.conf.contains(candidateId)) {
                LOG.warn("Node {} ignore PreVoteRequest from {} as it is not in conf <{}>.", getNodeId(), request.getServerId(), this.conf);
                break;
            }
            if (this.leaderId != null && !this.leaderId.isEmpty() && isCurrentLeaderValid()) {
                LOG.info("Node {} ignore PreVoteRequest from {}, term={}, currTerm={}, because the leader {}'s lease is still valid.", getNodeId(), request.getServerId(), request.getTerm(), this.currTerm, this.leaderId);
                break;
            }
            if (request.getTerm() < this.currTerm) {
                LOG.info("Node {} ignore PreVoteRequest from {}, term={}, currTerm={}.", getNodeId(), request.getServerId(), request.getTerm(), this.currTerm);
                // A follower replicator may not be started when this node become leader, so we must check it.
                checkReplicator(candidateId);
                break;
            }
            // A follower replicator may not be started when this node become leader, so we must check it.
            // check replicator state
            checkReplicator(candidateId);
            doUnlock = false;
            this.writeLock.unlock();
            final LogId lastLogId = this.logManager.getLastLogId(true);
            doUnlock = true;
            this.writeLock.lock();
            final LogId requestLastLogId = new LogId(request.getLastLogIndex(), request.getLastLogTerm());
            granted = requestLastLogId.compareTo(lastLogId) >= 0;
            LOG.info("Node {} received PreVoteRequest from {}, term={}, currTerm={}, granted={}, requestLastLogId={}, lastLogId={}.", getNodeId(), request.getServerId(), request.getTerm(), this.currTerm, granted, requestLastLogId, lastLogId);
        } while (false);
        return // 
        RequestVoteResponse.newBuilder().setTerm(// 
        this.currTerm).setGranted(// 
        granted).build();
    } finally {
        if (doUnlock) {
            this.writeLock.unlock();
        }
    }
}
Also used : LogId(com.alipay.sofa.jraft.entity.LogId) PeerId(com.alipay.sofa.jraft.entity.PeerId)

Example 15 with LogId

use of com.alipay.sofa.jraft.entity.LogId in project sofa-jraft by sofastack.

the class FSMCallerImpl method doCommitted.

private void doCommitted(final long committedIndex) {
    if (!this.error.getStatus().isOk()) {
        return;
    }
    final long lastAppliedIndex = this.lastAppliedIndex.get();
    // We can tolerate the disorder of committed_index
    if (lastAppliedIndex >= committedIndex) {
        return;
    }
    final long startMs = Utils.monotonicMs();
    try {
        final List<Closure> closures = new ArrayList<>();
        final List<TaskClosure> taskClosures = new ArrayList<>();
        final long firstClosureIndex = this.closureQueue.popClosureUntil(committedIndex, closures, taskClosures);
        // Calls TaskClosure#onCommitted if necessary
        onTaskCommitted(taskClosures);
        Requires.requireTrue(firstClosureIndex >= 0, "Invalid firstClosureIndex");
        final IteratorImpl iterImpl = new IteratorImpl(this.fsm, this.logManager, closures, firstClosureIndex, lastAppliedIndex, committedIndex, this.applyingIndex);
        while (iterImpl.isGood()) {
            final LogEntry logEntry = iterImpl.entry();
            if (logEntry.getType() != EnumOutter.EntryType.ENTRY_TYPE_DATA) {
                if (logEntry.getType() == EnumOutter.EntryType.ENTRY_TYPE_CONFIGURATION) {
                    if (logEntry.getOldPeers() != null && !logEntry.getOldPeers().isEmpty()) {
                        // Joint stage is not supposed to be noticeable by end users.
                        this.fsm.onConfigurationCommitted(new Configuration(iterImpl.entry().getPeers()));
                    }
                }
                if (iterImpl.done() != null) {
                    // For other entries, we have nothing to do besides flush the
                    // pending tasks and run this closure to notify the caller that the
                    // entries before this one were successfully committed and applied.
                    iterImpl.done().run(Status.OK());
                }
                iterImpl.next();
                continue;
            }
            // Apply data task to user state machine
            doApplyTasks(iterImpl);
        }
        if (iterImpl.hasError()) {
            setError(iterImpl.getError());
            iterImpl.runTheRestClosureWithError();
        }
        final long lastIndex = iterImpl.getIndex() - 1;
        final long lastTerm = this.logManager.getTerm(lastIndex);
        final LogId lastAppliedId = new LogId(lastIndex, lastTerm);
        this.lastAppliedIndex.set(lastIndex);
        this.lastAppliedTerm = lastTerm;
        this.logManager.setAppliedId(lastAppliedId);
        notifyLastAppliedIndexUpdated(lastIndex);
    } finally {
        this.nodeMetrics.recordLatency("fsm-commit", Utils.monotonicMs() - startMs);
    }
}
Also used : LoadSnapshotClosure(com.alipay.sofa.jraft.closure.LoadSnapshotClosure) Closure(com.alipay.sofa.jraft.Closure) SaveSnapshotClosure(com.alipay.sofa.jraft.closure.SaveSnapshotClosure) TaskClosure(com.alipay.sofa.jraft.closure.TaskClosure) Configuration(com.alipay.sofa.jraft.conf.Configuration) ArrayList(java.util.ArrayList) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) LogId(com.alipay.sofa.jraft.entity.LogId) LogEntry(com.alipay.sofa.jraft.entity.LogEntry) TaskClosure(com.alipay.sofa.jraft.closure.TaskClosure)

Aggregations

LogId (com.alipay.sofa.jraft.entity.LogId)44 LogEntry (com.alipay.sofa.jraft.entity.LogEntry)26 PeerId (com.alipay.sofa.jraft.entity.PeerId)18 Test (org.junit.Test)18 Status (com.alipay.sofa.jraft.Status)10 BaseStorageTest (com.alipay.sofa.jraft.storage.BaseStorageTest)9 ArrayList (java.util.ArrayList)8 ConfigurationEntry (com.alipay.sofa.jraft.conf.ConfigurationEntry)7 Configuration (com.alipay.sofa.jraft.conf.Configuration)5 ByteBuffer (java.nio.ByteBuffer)5 LogManager (com.alipay.sofa.jraft.storage.LogManager)4 CopyOnWriteArrayList (java.util.concurrent.CopyOnWriteArrayList)4 CountDownLatch (java.util.concurrent.CountDownLatch)4 ConfigurationManager (com.alipay.sofa.jraft.conf.ConfigurationManager)3 EntryType (com.alipay.sofa.jraft.entity.EnumOutter.EntryType)2 BaseLogEntryCodecFactoryTest (com.alipay.sofa.jraft.entity.codec.BaseLogEntryCodecFactoryTest)2 RaftException (com.alipay.sofa.jraft.error.RaftException)2 LogStorageOptions (com.alipay.sofa.jraft.option.LogStorageOptions)2 RaftOptions (com.alipay.sofa.jraft.option.RaftOptions)2 DisruptorMetricSet (com.alipay.sofa.jraft.util.DisruptorMetricSet)2