-
Notifications
You must be signed in to change notification settings - Fork 444
RATIS-2511. Follower should throw ReadException if it is installing snapshot #1444
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
21cad2e
3b39244
2c1e333
966d28f
5889217
77570fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1092,12 +1092,11 @@ private CompletableFuture<RaftClientReply> staleReadAsync(RaftClientRequest requ | |
| } | ||
| return processQueryFuture(stateMachine.queryStale(request.getMessage(), minIndex), request); | ||
| } | ||
|
|
||
| ReadRequests getReadRequests() { | ||
| return getState().getReadRequests(); | ||
| } | ||
|
|
||
| private CompletableFuture<ReadIndexReplyProto> sendReadIndexAsync(RaftClientRequest clientRequest) { | ||
| final Throwable snapshotInstallation = snapshotInstallationHandler.getInProgressInstallSnapshotReadException(); | ||
| if (snapshotInstallation != null) { | ||
| return JavaUtils.completeExceptionally(snapshotInstallation); | ||
| } | ||
| final RaftPeerId leaderId = getInfo().getLeaderId(); | ||
| if (leaderId == null) { | ||
| return JavaUtils.completeExceptionally(new ReadIndexException(getMemberId() + ": Leader is unknown.")); | ||
|
|
@@ -1146,7 +1145,8 @@ private CompletableFuture<RaftClientReply> readAsync(RaftClientRequest request) | |
| } | ||
|
|
||
| return replyFuture | ||
| .thenCompose(readIndex -> getReadRequests().waitToAdvance(readIndex)) | ||
| .thenCompose(readIndex -> getState().getReadRequests().waitToAdvance(readIndex, | ||
| snapshotInstallationHandler::getInProgressInstallSnapshotReadException)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add a waitReadIndex method and check there. .thenCompose(this::waitReadIndex) private CompletableFuture<Long> waitReadIndex(long readIndex) {
final long installSnapshot = snapshotInstallationHandler.getInProgressInstallSnapshotIndex();
if (installSnapshot != RaftLog.INVALID_LOG_INDEX) {
return JavaUtils.completeExceptionally(newReadException("start waiting for", installSnapshot, false));
}
return getState().getReadRequests().waitToAdvance(readIndex);
}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the review. Updated, but there might be a small chance of interleaving here since the snapshot checking is not protected by ReadRequests object lock.
We can probably synchronized on RaftServerImpl lock, but I'm afraid this might increase follower read contention.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ivandika3 , That is a good point! Then, let's use your previous approach then; see https://issues.apache.org/jira/secure/attachment/13082234/1444_review2.patch |
||
| .thenCompose(readIndex -> queryStateMachine(request)) | ||
| .exceptionally(e -> readException2Reply(request, e)); | ||
| } else { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,21 +20,30 @@ | |
| import org.apache.ratis.conf.RaftProperties; | ||
| import org.apache.ratis.protocol.exceptions.ReadException; | ||
| import org.apache.ratis.server.RaftServerConfigKeys; | ||
| import org.apache.ratis.util.JavaUtils; | ||
| import org.apache.ratis.util.Preconditions; | ||
| import org.apache.ratis.util.TimeDuration; | ||
| import org.apache.ratis.util.TimeoutExecutor; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
| import java.util.NavigableMap; | ||
| import java.util.TreeMap; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.function.LongConsumer; | ||
| import java.util.function.Supplier; | ||
|
|
||
| /** For supporting linearizable read. */ | ||
| class ReadRequests { | ||
| private static final Logger LOG = LoggerFactory.getLogger(ReadRequests.class); | ||
|
|
||
| static ReadException newException(Object server, long installSnapshot) { | ||
| return new ReadException(server + ": Failed read as snapshot (" + installSnapshot | ||
| + ") installation is in progress"); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move this method to |
||
|
|
||
| static class ReadIndexQueue { | ||
| private final TimeoutExecutor scheduler = TimeoutExecutor.getInstance(); | ||
| /** The log index known to be applied. */ | ||
|
|
@@ -52,10 +61,14 @@ static class ReadIndexQueue { | |
| this.readTimeout = readTimeout; | ||
| } | ||
|
|
||
| CompletableFuture<Long> add(long readIndex) { | ||
| CompletableFuture<Long> add(long readIndex, Supplier<Throwable> failureSupplier) { | ||
| final CompletableFuture<Long> returned; | ||
| final boolean create; | ||
| synchronized (this) { | ||
| final Throwable failure = failureSupplier.get(); | ||
| if (failure != null) { | ||
| return JavaUtils.completeExceptionally(failure); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be checked in RaftServerImpl. |
||
| if (readIndex <= lastAppliedIndex) { | ||
| return CompletableFuture.completedFuture(lastAppliedIndex); | ||
| } | ||
|
|
@@ -88,6 +101,14 @@ private void handleTimeout(long readIndex) { | |
| removed.completeExceptionally(new ReadException("Read timeout " + readTimeout + " for index " + readIndex)); | ||
| } | ||
|
|
||
| void fail(Throwable cause) { | ||
| final Collection<CompletableFuture<Long>> futures; | ||
| synchronized (this) { | ||
| futures = new ArrayList<>(sorted.values()); | ||
| sorted.clear(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
//ReadIndexQueue
synchronized Collection<CompletableFuture<Long>> clear(Throwable cause) {
final Collection<CompletableFuture<Long>> futures = sorted.values();
sorted = new TreeMap<>();
return futures;
}//ReadRequests
void fail(Throwable cause) {
for(CompletableFuture<Long> f : readIndexQueue.clear(cause)) {
f.completeExceptionally(cause);
}
} |
||
| } | ||
| futures.forEach(f -> f.completeExceptionally(cause)); | ||
| } | ||
|
|
||
| /** Complete all the entries less than or equal to the given applied index. */ | ||
| synchronized void complete(long appliedIndex) { | ||
|
|
@@ -119,7 +140,11 @@ LongConsumer getAppliedIndexConsumer() { | |
| return readIndexQueue::complete; | ||
| } | ||
|
|
||
| CompletableFuture<Long> waitToAdvance(long readIndex) { | ||
| return readIndexQueue.add(readIndex); | ||
| CompletableFuture<Long> waitToAdvance(long readIndex, Supplier<Throwable> failureSupplier) { | ||
| return readIndexQueue.add(readIndex, failureSupplier); | ||
| } | ||
|
|
||
| void fail(Throwable cause) { | ||
| readIndexQueue.fail(cause); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's create the ReadException with a different message. It will be easier to debug later.