use of org.apache.ignite.raft.jraft.Status in project ignite-3 by apache.
the class ReadOnlyServiceImpl method join.
@Override
public void join() throws InterruptedException {
if (this.shutdownLatch != null) {
this.shutdownLatch.await();
}
this.readIndexDisruptor.unsubscribe(groupId);
resetPendingStatusError(new Status(RaftError.ESTOP, "Node is quit."));
}
use of org.apache.ignite.raft.jraft.Status in project ignite-3 by apache.
the class Replicator method sendHeartbeat.
public static void sendHeartbeat(final ThreadId id, final RpcResponseClosure<AppendEntriesResponse> closure, ExecutorService executor) {
final Replicator r = (Replicator) id.lock();
if (r == null) {
Utils.runClosureInThread(executor, closure, new Status(RaftError.EHOSTDOWN, "Peer %s is not connected", id));
return;
}
// id unlock in send empty entries.
r.sendEmptyEntries(true, closure);
}
use of org.apache.ignite.raft.jraft.Status in project ignite-3 by apache.
the class Replicator method sendEntries.
/**
* Send log entries to follower, returns true when success, otherwise false and unlock the id.
*
* @param nextSendingIndex next sending index
* @return send result.
*/
private boolean sendEntries(final long nextSendingIndex) {
final AppendEntriesRequestBuilder rb = raftOptions.getRaftMessagesFactory().appendEntriesRequest();
if (!fillCommonFields(rb, nextSendingIndex - 1, false)) {
// unlock id in installSnapshot
installSnapshot();
return false;
}
ByteBufferCollector dataBuf = null;
final int maxEntriesSize = this.raftOptions.getMaxEntriesSize();
final RecyclableByteBufferList byteBufList = RecyclableByteBufferList.newInstance();
try {
List<RaftOutter.EntryMeta> entries = new ArrayList<>();
for (int i = 0; i < maxEntriesSize; i++) {
final EntryMetaBuilder emb = raftOptions.getRaftMessagesFactory().entryMeta();
if (!prepareEntry(nextSendingIndex, i, emb, byteBufList)) {
break;
}
entries.add(emb.build());
}
rb.entriesList(entries);
if (entries.isEmpty()) {
if (nextSendingIndex < this.options.getLogManager().getFirstLogIndex()) {
installSnapshot();
return false;
}
// _id is unlock in _wait_more
waitMoreEntries(nextSendingIndex);
return false;
}
if (byteBufList.getCapacity() > 0) {
dataBuf = ByteBufferCollector.allocateByRecyclers(byteBufList.getCapacity());
for (final ByteBuffer b : byteBufList) {
dataBuf.put(b);
}
final ByteBuffer buf = dataBuf.getBuffer();
buf.flip();
rb.data(new ByteString(buf));
}
} finally {
RecycleUtil.recycle(byteBufList);
}
final AppendEntriesRequest request = rb.build();
if (LOG.isDebugEnabled()) {
LOG.debug("Node {} send AppendEntriesRequest to {} term {} lastCommittedIndex {} prevLogIndex {} prevLogTerm {} logIndex {} count {}", this.options.getNode().getNodeId(), this.options.getPeerId(), this.options.getTerm(), request.committedIndex(), request.prevLogIndex(), request.prevLogTerm(), nextSendingIndex, Utils.size(request.entriesList()));
}
this.statInfo.runningState = RunningState.APPENDING_ENTRIES;
this.statInfo.firstLogIndex = request.prevLogIndex() + 1;
this.statInfo.lastLogIndex = request.prevLogIndex() + Utils.size(request.entriesList());
final Recyclable recyclable = dataBuf;
final int v = this.version;
final long monotonicSendTimeMs = Utils.monotonicMs();
final int seq = getAndIncrementReqSeq();
Future<Message> rpcFuture = null;
try {
rpcFuture = this.rpcService.appendEntries(this.options.getPeerId().getEndpoint(), request, -1, new RpcResponseClosureAdapter<AppendEntriesResponse>() {
@Override
public void run(final Status status) {
if (status.isOk()) {
// TODO: recycle on send success, not response received IGNITE-14832.
// Also, this closure can be executed when rpcFuture was cancelled, but the request was not sent (meaning
// it's too early to recycle byte buffer)
RecycleUtil.recycle(recyclable);
}
onRpcReturned(Replicator.this.id, RequestType.AppendEntries, status, request, getResponse(), seq, v, monotonicSendTimeMs);
}
});
} catch (final Throwable t) {
RecycleUtil.recycle(recyclable);
ThrowUtil.throwException(t);
}
addInflight(RequestType.AppendEntries, nextSendingIndex, Utils.size(request.entriesList()), request.data() == null ? 0 : request.data().size(), seq, rpcFuture);
return true;
}
use of org.apache.ignite.raft.jraft.Status in project ignite-3 by apache.
the class Replicator method onTimeoutNowReturned.
@SuppressWarnings("unused")
static void onTimeoutNowReturned(final ThreadId id, final Status status, final TimeoutNowRequest request, final TimeoutNowResponse response, final boolean stopAfterFinish) {
final Replicator r = (Replicator) id.lock();
if (r == null) {
return;
}
final boolean isLogDebugEnabled = LOG.isDebugEnabled();
StringBuilder sb = null;
if (isLogDebugEnabled) {
sb = //
new StringBuilder("Node ").append(r.options.getGroupId()).append(":").append(//
r.options.getServerId()).append(//
" received TimeoutNowResponse from ").append(r.options.getPeerId());
}
if (!status.isOk()) {
if (isLogDebugEnabled) {
sb.append(" fail:").append(status);
LOG.debug(sb.toString());
}
notifyReplicatorStatusListener(r, ReplicatorEvent.ERROR, status);
if (stopAfterFinish) {
r.notifyOnCaughtUp(RaftError.ESTOP.getNumber(), true);
r.destroy();
} else {
id.unlock();
}
return;
}
if (isLogDebugEnabled) {
sb.append(response.success() ? " success" : " fail");
LOG.debug(sb.toString());
}
if (response.term() > r.options.getTerm()) {
final NodeImpl node = r.options.getNode();
r.notifyOnCaughtUp(RaftError.EPERM.getNumber(), true);
r.destroy();
node.increaseTermTo(response.term(), new Status(RaftError.EHIGHERTERMRESPONSE, "Leader receives higher term timeout_now_response from peer:%s", r.options.getPeerId()));
return;
}
if (stopAfterFinish) {
r.notifyOnCaughtUp(RaftError.ESTOP.getNumber(), true);
r.destroy();
} else {
id.unlock();
}
}
use of org.apache.ignite.raft.jraft.Status in project ignite-3 by apache.
the class Replicator method onAppendEntriesReturned.
private static boolean onAppendEntriesReturned(final ThreadId id, final Inflight inflight, final Status status, final AppendEntriesRequest request, final AppendEntriesResponse response, final long rpcSendTime, final long startTimeMs, final Replicator r) {
if (inflight.startIndex != request.prevLogIndex() + 1) {
LOG.warn("Replicator {} received invalid AppendEntriesResponse, in-flight startIndex={}, request prevLogIndex={}, reset the replicator state and probe again.", r, inflight.startIndex, request.prevLogIndex());
r.resetInflights();
r.state = State.Probe;
// unlock id in sendEmptyEntries
r.sendEmptyEntries(false);
return false;
}
// record metrics
if (request.entriesList() != null) {
r.nodeMetrics.recordLatency("replicate-entries", Utils.monotonicMs() - rpcSendTime);
r.nodeMetrics.recordSize("replicate-entries-count", request.entriesList().size());
r.nodeMetrics.recordSize("replicate-entries-bytes", request.data() != null ? request.data().size() : 0);
}
final boolean isLogDebugEnabled = LOG.isDebugEnabled();
StringBuilder sb = null;
if (isLogDebugEnabled) {
sb = //
new StringBuilder("Node ").append(//
r.options.getGroupId()).append(//
':').append(//
r.options.getServerId()).append(//
" received AppendEntriesResponse from ").append(//
r.options.getPeerId()).append(//
" prevLogIndex=").append(//
request.prevLogIndex()).append(//
" prevLogTerm=").append(//
request.prevLogTerm()).append(//
" count=").append(Utils.size(request.entriesList()));
}
if (!status.isOk()) {
// dummy_id is unlock in block
if (isLogDebugEnabled) {
//
sb.append(" fail, sleep, status=").append(status);
LOG.debug(sb.toString());
}
notifyReplicatorStatusListener(r, ReplicatorEvent.ERROR, status);
if (++r.consecutiveErrorTimes % 10 == 0) {
LOG.warn("Fail to issue RPC to {}, consecutiveErrorTimes={}, error={}", r.options.getPeerId(), r.consecutiveErrorTimes, status);
}
r.resetInflights();
r.state = State.Probe;
// unlock in in block
r.block(startTimeMs, status.getCode());
return false;
}
r.consecutiveErrorTimes = 0;
if (!response.success()) {
if (response.term() > r.options.getTerm()) {
if (isLogDebugEnabled) {
//
sb.append(" fail, greater term ").append(//
response.term()).append(//
" expect term ").append(r.options.getTerm());
LOG.debug(sb.toString());
}
final NodeImpl node = r.options.getNode();
r.notifyOnCaughtUp(RaftError.EPERM.getNumber(), true);
r.destroy();
node.increaseTermTo(response.term(), new Status(RaftError.EHIGHERTERMRESPONSE, "Leader receives higher term heartbeat_response from peer:%s", r.options.getPeerId()));
return false;
}
if (isLogDebugEnabled) {
sb.append(" fail, find nextIndex remote lastLogIndex ").append(response.lastLogIndex()).append(" local nextIndex ").append(r.nextIndex);
LOG.debug(sb.toString());
}
if (rpcSendTime > r.lastRpcSendTimestamp) {
r.lastRpcSendTimestamp = rpcSendTime;
}
// Fail, reset the state to try again from nextIndex.
r.resetInflights();
// prev_log_index and prev_log_term doesn't match
if (response.lastLogIndex() + 1 < r.nextIndex) {
LOG.debug("LastLogIndex at peer={} is {}", r.options.getPeerId(), response.lastLogIndex());
// The peer contains less logs than leader
r.nextIndex = response.lastLogIndex() + 1;
} else {
// decrease _last_log_at_peer by one to test the right index to keep
if (r.nextIndex > 1) {
LOG.debug("logIndex={} dismatch", r.nextIndex);
r.nextIndex--;
} else {
LOG.error("Peer={} declares that log at index=0 doesn't match, which is not supposed to happen", r.options.getPeerId());
}
}
// dummy_id is unlock in _send_heartbeat
r.sendEmptyEntries(false);
return false;
}
if (isLogDebugEnabled) {
sb.append(", success");
LOG.debug(sb.toString());
}
// success
if (response.term() != r.options.getTerm()) {
r.resetInflights();
r.state = State.Probe;
LOG.error("Fail, response term {} dismatch, expect term {}", response.term(), r.options.getTerm());
id.unlock();
return false;
}
if (rpcSendTime > r.lastRpcSendTimestamp) {
r.lastRpcSendTimestamp = rpcSendTime;
}
final int entriesSize = Utils.size(request.entriesList());
if (entriesSize > 0) {
if (r.options.getReplicatorType().isFollower()) {
// Only commit index when the response is from follower.
r.options.getBallotBox().commitAt(r.nextIndex, r.nextIndex + entriesSize - 1, r.options.getPeerId());
}
if (LOG.isDebugEnabled()) {
LOG.debug("Replicated logs in [{}, {}] to peer {}", r.nextIndex, r.nextIndex + entriesSize - 1, r.options.getPeerId());
}
}
r.state = State.Replicate;
r.blockTimer = null;
r.nextIndex += entriesSize;
r.hasSucceeded = true;
r.notifyOnCaughtUp(RaftError.SUCCESS.getNumber(), false);
// dummy_id is unlock in _send_entries
if (r.timeoutNowIndex > 0 && r.timeoutNowIndex < r.nextIndex) {
r.sendTimeoutNow(false, false);
}
return true;
}
Aggregations