-
Notifications
You must be signed in to change notification settings - Fork 974
QuorumCoverage should only count unknown nodes #2303
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 all commits
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 |
|---|---|---|
|
|
@@ -373,29 +373,43 @@ public synchronized void addBookie(int bookieIndexHeardFrom, int rc) { | |
| public synchronized boolean checkCovered() { | ||
| // now check if there are any write quorums, with |ackQuorum| nodes available | ||
| for (int i = 0; i < ensembleSize; i++) { | ||
| int nodesNotCovered = 0; | ||
| int nodesOkay = 0; | ||
| int nodesUninitialized = 0; | ||
| /* Nodes which have either responded with an error other than NoSuch{Entry,Ledger}, | ||
|
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. I think I am missing something here So, I have a question. While doing a ledger recovery, bk client waits for response from (Qw - Qa) + 1 bookies So, can you please confirm the above assumption is correct or am I missing anything here?
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. Coverage is not Take your example above. You have b1=OK, b2=Unknown, b3=Unknown. But an entry could possibly be written to b2 & b3 and have attained ack quorum. So we need to check all write sets, of which there are |ensemble|, since we are roundrobining. Within an individual writeset, we only need to hear from |
||
| or have not responded at all. We cannot know if these nodes ever accepted a entry. */ | ||
| int nodesUnknown = 0; | ||
|
|
||
| for (int j = 0; j < writeQuorumSize; j++) { | ||
| int nodeIndex = (i + j) % ensembleSize; | ||
| if (covered[nodeIndex] == BKException.Code.OK) { | ||
| nodesOkay++; | ||
| } else if (covered[nodeIndex] != BKException.Code.NoSuchEntryException | ||
| && covered[nodeIndex] != BKException.Code.NoSuchLedgerExistsException) { | ||
| nodesNotCovered++; | ||
| if (covered[nodeIndex] == BKException.Code.UNINITIALIZED) { | ||
| nodesUninitialized++; | ||
| } | ||
| if (covered[nodeIndex] != BKException.Code.OK | ||
| && covered[nodeIndex] != BKException.Code.NoSuchEntryException | ||
| && covered[nodeIndex] != BKException.Code.NoSuchLedgerExistsException) { | ||
| nodesUnknown++; | ||
| } | ||
| } | ||
| // if we haven't seen any OK responses and there are still nodes not heard from, | ||
| // let's wait until | ||
| if (nodesNotCovered >= ackQuorumSize || (nodesOkay == 0 && nodesUninitialized > 0)) { | ||
|
|
||
| /* If nodesUnknown is greater than the ack quorum size, then | ||
| it is possible those two unknown nodes accepted an entry which | ||
| we do not know about */ | ||
| if (nodesUnknown >= ackQuorumSize) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| StringBuilder buffer = new StringBuilder(); | ||
| buffer.append("QuorumCoverage(e:").append(ensembleSize) | ||
| .append(",w:").append(writeQuorumSize) | ||
| .append(",a:").append(ackQuorumSize) | ||
| .append(") = ["); | ||
| int i = 0; | ||
| for (; i < covered.length - 1; i++) { | ||
| buffer.append(covered[i]).append(", "); | ||
| } | ||
| buffer.append(covered[i]).append("]"); | ||
| return buffer.toString(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /* | ||
| * | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| * | ||
| */ | ||
| package org.apache.bookkeeper.client; | ||
|
|
||
| import io.netty.buffer.UnpooledByteBufAllocator; | ||
| import com.google.common.collect.Lists; | ||
| import java.util.List; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import org.apache.bookkeeper.common.util.OrderedExecutor; | ||
| import org.apache.bookkeeper.net.BookieSocketAddress; | ||
| import org.apache.bookkeeper.proto.MockBookieClient; | ||
| import org.apache.bookkeeper.proto.DataFormats.LedgerMetadataFormat.DigestType; | ||
| import org.apache.bookkeeper.proto.checksum.DigestManager; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Test; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| public class ReadLastConfirmedOpTest { | ||
| private static final Logger log = LoggerFactory.getLogger(ReadLastConfirmedOpTest.class); | ||
| private final BookieSocketAddress bookie1 = new BookieSocketAddress("bookie1", 3181); | ||
| private final BookieSocketAddress bookie2 = new BookieSocketAddress("bookie2", 3181); | ||
|
|
||
| OrderedExecutor executor = null; | ||
|
|
||
| @Before | ||
| public void setup() throws Exception { | ||
| executor = OrderedExecutor.newBuilder() | ||
| .name("BookKeeperClientWorker") | ||
| .numThreads(1) | ||
| .build(); | ||
| } | ||
|
|
||
| @After | ||
| public void teardown() throws Exception { | ||
| if (executor != null) { | ||
| executor.shutdown(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Test for specific bug that was introduced with dcdd1e88 | ||
|
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. Can we describe the scenario ? Maybe referring dcdd1e8 is not useful |
||
| */ | ||
| @Test | ||
| public void testBookieFailsAfterLedgerMissingOnFirst() throws Exception { | ||
| long ledgerId = 0xf00b; | ||
| List<BookieSocketAddress> ensemble = Lists.newArrayList(bookie1, bookie2); | ||
| byte[] ledgerKey = new byte[0]; | ||
|
|
||
| MockBookieClient bookieClient = new MockBookieClient(executor); | ||
| DistributionSchedule schedule = new RoundRobinDistributionSchedule(2, 2, 2); | ||
| DigestManager digestManager = DigestManager.instantiate(ledgerId, ledgerKey, | ||
| DigestType.CRC32C, | ||
| UnpooledByteBufAllocator.DEFAULT, | ||
| true /* useV2 */); | ||
|
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. is is necessary to use V2 ?
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. We only use V2 internally, so that's what I tested with originally.
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. can we change it to v3 ? |
||
|
|
||
| CompletableFuture<Void> blocker = new CompletableFuture<>(); | ||
| bookieClient.setPreReadHook((bookie, _ledgerId, entryId) -> { | ||
| if (bookie.equals(bookie1)) { | ||
| return CompletableFuture.completedFuture(null); | ||
| } else { | ||
| return blocker; | ||
| } | ||
| }); | ||
| CompletableFuture<DigestManager.RecoveryData> promise = new CompletableFuture<>(); | ||
| ReadLastConfirmedOp op = new ReadLastConfirmedOp( | ||
| bookieClient, schedule, | ||
| digestManager, ledgerId, ensemble, | ||
| ledgerKey, | ||
| (rc, data) -> { | ||
| if (rc != BKException.Code.OK) { | ||
| promise.completeExceptionally( | ||
| BKException.create(rc)); | ||
| } else { | ||
| promise.complete(data); | ||
| } | ||
| }); | ||
| op.initiateWithFencing(); | ||
|
|
||
| while (op.getNumResponsesPending() > 1) { | ||
| Thread.sleep(100); | ||
| } | ||
| blocker.completeExceptionally( | ||
| new BKException.BKBookieHandleNotAvailableException()); | ||
| promise.get(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,16 +74,6 @@ public void testCoverageSets() { | |
| } | ||
| } | ||
| assertEquals("Should be no errors", 0, errors); | ||
|
|
||
| RoundRobinDistributionSchedule schedule = new RoundRobinDistributionSchedule( | ||
| 5, 3, 5); | ||
| DistributionSchedule.QuorumCoverageSet covSet = schedule.getCoverageSet(); | ||
| covSet.addBookie(0, BKException.Code.NoSuchLedgerExistsException); | ||
| covSet.addBookie(1, BKException.Code.NoSuchEntryException); | ||
| covSet.addBookie(2, BKException.Code.NoSuchLedgerExistsException); | ||
| covSet.addBookie(3, BKException.Code.UNINITIALIZED); | ||
| covSet.addBookie(4, BKException.Code.UNINITIALIZED); | ||
| assertFalse(covSet.checkCovered()); | ||
|
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. why dropping this test ?
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. Because it's completely incorrect.
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. ok |
||
| } | ||
|
|
||
| /** | ||
|
|
||
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.
@rdhabalia PTAL