You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by eo...@apache.org on 2020/06/12 15:06:42 UTC

[bookkeeper] branch master updated: Ledger having a Empty segment is causing ReplicasCheck scrutiny to fail

This is an automated email from the ASF dual-hosted git repository.

eolivelli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new b411dac  Ledger having a Empty segment is causing ReplicasCheck scrutiny to fail
b411dac is described below

commit b411dac67db385b033db875f5926465201ec3471
Author: Charan Reddy Guttapalem <cg...@salesforce.com>
AuthorDate: Fri Jun 12 08:06:31 2020 -0700

    Ledger having a Empty segment is causing ReplicasCheck scrutiny to fail
    
    
    
    Descriptions of the changes in this PR:
    
    - in Auditor.ReadLedgerMetadataCallbackForReplicasCheck, fixing logic for empty segments
    - added test for validating areEntriesOfLedgerStoredInTheBookie for empty middle segment
    - added testcase for ledger with more empty segments.
    
    
    
    Reviewers: Enrico Olivelli <eo...@gmail.com>
    
    This closes #2205 from reddycharan/fixreplicascheck
---
 .../org/apache/bookkeeper/replication/Auditor.java |  21 +++-
 .../bookkeeper/client/BookKeeperAdminTest.java     |   3 +-
 .../replication/AuditorReplicasCheckTest.java      | 122 +++++++++++++++++++++
 3 files changed, 140 insertions(+), 6 deletions(-)

diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java
index eac25b2..db6e331 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java
@@ -133,6 +133,7 @@ public class Auditor implements AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(Auditor.class);
     private static final int MAX_CONCURRENT_REPLICAS_CHECK_LEDGER_REQUESTS = 100;
     private static final int REPLICAS_CHECK_TIMEOUT_IN_SECS = 120;
+    private static final BitSet EMPTY_BITSET = new BitSet();
     private final ServerConfiguration conf;
     private final BookKeeper bkc;
     private final boolean ownBkc;
@@ -1489,7 +1490,8 @@ public class Auditor implements AutoCloseable {
                 return;
             }
 
-            if (metadata.getLastEntryId() == -1) {
+            final long lastEntryId = metadata.getLastEntryId();
+            if (lastEntryId == -1) {
                 LOG.debug("Ledger: {} is closed but it doesn't has any entries, so skipping the replicas check",
                         ledgerInRange);
                 mcbForThisLedgerRange.processResult(BKException.Code.OK, null, null);
@@ -1515,12 +1517,23 @@ public class Auditor implements AutoCloseable {
                 final Entry<Long, ? extends List<BookieSocketAddress>> segmentEnsemble = segments.get(segmentNum);
                 final List<BookieSocketAddress> ensembleOfSegment = segmentEnsemble.getValue();
                 final long startEntryIdOfSegment = segmentEnsemble.getKey();
-                final long lastEntryIdOfSegment = (segmentNum == (segments.size() - 1)) ? metadata.getLastEntryId()
+                final boolean lastSegment = (segmentNum == (segments.size() - 1));
+                final long lastEntryIdOfSegment = lastSegment ? lastEntryId
                         : segments.get(segmentNum + 1).getKey() - 1;
+                /*
+                 * Segment can be empty. If last segment is empty, then
+                 * startEntryIdOfSegment of it will be greater than lastEntryId
+                 * of the ledger. If the segment in middle is empty, then its
+                 * startEntry will be same as startEntry of the following
+                 * segment.
+                 */
+                final boolean emptySegment = lastSegment ? (startEntryIdOfSegment > lastEntryId)
+                        : (startEntryIdOfSegment == segments.get(segmentNum + 1).getKey());
                 for (int bookieIndex = 0; bookieIndex < ensembleOfSegment.size(); bookieIndex++) {
                     final BookieSocketAddress bookieInEnsemble = ensembleOfSegment.get(bookieIndex);
-                    final BitSet entriesStripedToThisBookie = distributionSchedule
-                            .getEntriesStripedToTheBookie(bookieIndex, startEntryIdOfSegment, lastEntryIdOfSegment);
+                    final BitSet entriesStripedToThisBookie = emptySegment ? EMPTY_BITSET
+                            : distributionSchedule.getEntriesStripedToTheBookie(bookieIndex, startEntryIdOfSegment,
+                                    lastEntryIdOfSegment);
                     if (entriesStripedToThisBookie.cardinality() == 0) {
                         /*
                          * if no entry is expected to contain in this bookie,
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java
index 1916880..15253fb 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java
@@ -592,7 +592,7 @@ public class BookKeeperAdminTest extends BookKeeperClusterTestCase {
     }
 
     @Test
-    public void testAreEntriesOfLedgerStoredInTheBookieForMultipleSegments() throws Exception {
+    public void testAreEntriesOfLedgerStoredInTheBookieForLastEmptySegment() throws Exception {
         int lastEntryId = 10;
         long ledgerId = 100L;
         BookieSocketAddress bookie0 = new BookieSocketAddress("bookie0:3181");
@@ -728,5 +728,4 @@ public class BookKeeperAdminTest extends BookKeeperClusterTestCase {
     public void testLegacyBookieServiceInfo() throws Exception {
         testBookieServiceInfo(false, true);
     }
-
 }
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorReplicasCheckTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorReplicasCheckTest.java
index 85b2015..fcbde5e 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorReplicasCheckTest.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorReplicasCheckTest.java
@@ -808,4 +808,126 @@ public class AuditorReplicasCheckTest extends BookKeeperClusterTestCase {
         runTestScenario(returnAvailabilityOfEntriesOfLedger, errorReturnValueForGetAvailabilityOfEntriesOfLedger, 0, 0,
                 numLedgersFoundHavingLessThanWQReplicasOfAnEntry);
     }
+
+    /*
+     * In this testscenario all the ledgers have empty segments.
+     */
+    @Test
+    public void testReplicasCheckForLedgersWithEmptySegments() throws Exception {
+        int numOfBookies = 5;
+        RegistrationManager regManager = driver.getRegistrationManager();
+        MultiKeyMap<String, AvailabilityOfEntriesOfLedger> returnAvailabilityOfEntriesOfLedger =
+                new MultiKeyMap<String, AvailabilityOfEntriesOfLedger>();
+        MultiKeyMap<String, Integer> errorReturnValueForGetAvailabilityOfEntriesOfLedger =
+                new MultiKeyMap<String, Integer>();
+        List<BookieSocketAddress> bookieAddresses = addAndRegisterBookies(regManager, numOfBookies);
+
+        LedgerManagerFactory mFactory = driver.getLedgerManagerFactory();
+        LedgerManager lm = mFactory.newLedgerManager();
+        DigestType digestType = DigestType.DUMMY;
+        byte[] password = new byte[0];
+        Collections.shuffle(bookieAddresses);
+
+        int numLedgersFoundHavingNoReplicaOfAnEntry = 0;
+        int numLedgersFoundHavingLessThanAQReplicasOfAnEntry = 0;
+        int numLedgersFoundHavingLessThanWQReplicasOfAnEntry = 0;
+
+        /*
+         * closed ledger.
+         *
+         * This closed Ledger has no entry. So it should not be counted towards
+         * numLedgersFoundHavingNoReplicaOfAnEntry/LessThanAQReplicasOfAnEntry
+         * /WQReplicasOfAnEntry.
+         */
+        Map<Long, List<BookieSocketAddress>> segmentEnsembles = new HashMap<Long, List<BookieSocketAddress>>();
+        int ensembleSize = 4;
+        int writeQuorumSize = 3;
+        int ackQuorumSize = 2;
+        long lastEntryId = -1L;
+        int length = 0;
+        segmentEnsembles.put(0L, bookieAddresses.subList(0, 4));
+        long ledgerId = 1L;
+        createClosedLedgerMetadata(lm, ledgerId, ensembleSize, writeQuorumSize, ackQuorumSize, segmentEnsembles,
+                lastEntryId, length, digestType, password);
+
+        /*
+         * closed ledger with multiple segments.
+         *
+         * This ledger has empty last segment, but all the entries have
+         * writeQuorumSize number of copies, So it should not be counted towards
+         * numLedgersFoundHavingNoReplicaOfAnEntry/LessThanAQReplicasOfAnEntry/
+         * WQReplicasOfAnEntry.
+         */
+        lastEntryId = 2;
+        segmentEnsembles.clear();
+        segmentEnsembles.put(0L, bookieAddresses.subList(0, 4));
+        segmentEnsembles.put((lastEntryId + 1), bookieAddresses.subList(1, 5));
+        ledgerId = 2L;
+        createClosedLedgerMetadata(lm, ledgerId, ensembleSize, writeQuorumSize, ackQuorumSize, segmentEnsembles,
+                lastEntryId, length, digestType, password);
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(0).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 2 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(1).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 1 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(2).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 1, 2 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(3).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 1, 2 }));
+
+        /*
+         * Closed ledger with multiple segments.
+         *
+         * Segment0, Segment1, Segment3, Segment5 and Segment6 are empty.
+         * Entries from entryid 3 are missing. So it should be counted towards
+         * numLedgersFoundHavingNoReplicaOfAnEntry.
+         */
+        lastEntryId = 5;
+        segmentEnsembles.clear();
+        segmentEnsembles.put(0L, bookieAddresses.subList(1, 5));
+        segmentEnsembles.put(0L, bookieAddresses.subList(0, 4));
+        segmentEnsembles.put(0L, bookieAddresses.subList(0, 4));
+        segmentEnsembles.put(4L, bookieAddresses.subList(1, 5));
+        segmentEnsembles.put(4L, bookieAddresses.subList(0, 4));
+        segmentEnsembles.put((lastEntryId + 1), bookieAddresses.subList(1, 5));
+        segmentEnsembles.put((lastEntryId + 1), bookieAddresses.subList(0, 4));
+        ledgerId = 3L;
+        createClosedLedgerMetadata(lm, ledgerId, ensembleSize, writeQuorumSize, ackQuorumSize, segmentEnsembles,
+                lastEntryId, length, digestType, password);
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(0).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 2 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(1).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 1 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(2).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 1, 2 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(3).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 1, 2 }));
+        numLedgersFoundHavingNoReplicaOfAnEntry++;
+
+        /*
+         * non-closed ledger with multiple segments
+         *
+         * since this is non-closed ledger, it should not be counted towards
+         * ledgersFoundHavingLessThanWQReplicasOfAnEntry
+         */
+        lastEntryId = 2;
+        segmentEnsembles.clear();
+        segmentEnsembles.put(0L, bookieAddresses.subList(0, 4));
+        segmentEnsembles.put(0L, bookieAddresses.subList(1, 5));
+        segmentEnsembles.put((lastEntryId + 1), bookieAddresses.subList(1, 5));
+        ledgerId = 4L;
+        createNonClosedLedgerMetadata(lm, ledgerId, ensembleSize, writeQuorumSize, ackQuorumSize, segmentEnsembles,
+                digestType, password);
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(0).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 2 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(1).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 1 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(2).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 0, 1, 2 }));
+        returnAvailabilityOfEntriesOfLedger.put(bookieAddresses.get(3).toString(), Long.toString(ledgerId),
+                new AvailabilityOfEntriesOfLedger(new long[] { 1, 2 }));
+
+        runTestScenario(returnAvailabilityOfEntriesOfLedger, errorReturnValueForGetAvailabilityOfEntriesOfLedger,
+                numLedgersFoundHavingNoReplicaOfAnEntry, numLedgersFoundHavingLessThanAQReplicasOfAnEntry,
+                numLedgersFoundHavingLessThanWQReplicasOfAnEntry);
+    }
 }