You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by zh...@apache.org on 2021/04/01 06:43:08 UTC

[bookkeeper] branch master updated: Defect-Fix Compaction status report into correct buckets. (#2645)

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

zhaijia 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 2722c9b  Defect-Fix Compaction status report into correct buckets. (#2645)
2722c9b is described below

commit 2722c9bfed3396f7fa0785c5e8e412fd412f03c0
Author: Kevin Wilson <kl...@comcast.net>
AuthorDate: Thu Apr 1 00:42:44 2021 -0600

    Defect-Fix Compaction status report into correct buckets. (#2645)
    
    The compaction status report is off by 1.
    
    When compaction is enabled, the 10% log is always empty. Setting the compaction threshold to .2 we were seeing the number of compacted entrylogs that were removed, match the sum of the 20% and 30% buckets. While the 10% bucket remained empty. Increasing the threshold to .3 we would see the sum of the first 4 buckets compacted (10%-40%). Also with the 10% bucket empty.
    
    Upon inspection of the code, the code is using the .ceil function to calculate the bucket index to be updated. This means that 10% bucket will never be used. However, there could be a rare 100% case in which the code runs into a index out of bounds exception when the entrylog is actually 100% used. The index calculation should be using floor and not the ceil call using the next index lower. So usage of .00 to .9 ends up in the 0 index instead of the current calculation which puts the  [...]
    
    This will leave the 100% index empty most of the time, as rarely will the entry logs be 100% used.
---
 .../bookkeeper/bookie/GarbageCollectorThread.java  | 17 ++++++++++--
 .../bookie/GarbageCollectorThreadTest.java         | 32 +++++++++++++++++++++-
 2 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java
index c242778..e2a75ac 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/GarbageCollectorThread.java
@@ -454,9 +454,7 @@ public class GarbageCollectorThread extends SafeRunnable {
         int[] entryLogUsageBuckets = new int[numBuckets];
 
         for (EntryLogMetadata meta : logsToCompact) {
-            int bucketIndex = Math.min(
-                    numBuckets - 1,
-                    (int) Math.ceil(meta.getUsage() * numBuckets));
+            int bucketIndex = calculateUsageIndex(numBuckets, meta.getUsage());
             entryLogUsageBuckets[bucketIndex]++;
 
             if (meta.getUsage() >= threshold) {
@@ -480,6 +478,19 @@ public class GarbageCollectorThread extends SafeRunnable {
     }
 
     /**
+     * Calculate the index for the batch based on the usage between 0 and 1.
+     *
+     * @param numBuckets Number of reporting buckets.
+     * @param usage 0.0 - 1.0 value representing the usage of the entry log.
+     * @return index based on the number of buckets The last bucket will have the 1.0 if added.
+     */
+    int calculateUsageIndex(int numBuckets, double usage) {
+        return Math.min(
+                numBuckets - 1,
+                (int) Math.floor(usage * numBuckets));
+    }
+
+    /**
      * Shutdown the garbage collector thread.
      *
      * @throws InterruptedException if there is an exception stopping gc thread.
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/GarbageCollectorThreadTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/GarbageCollectorThreadTest.java
index be6c81b..d15db12 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/GarbageCollectorThreadTest.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/GarbageCollectorThreadTest.java
@@ -32,18 +32,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.bookkeeper.conf.ServerConfiguration;
 import org.apache.bookkeeper.meta.LedgerManager;
 import org.apache.bookkeeper.stats.StatsLogger;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
 import org.powermock.reflect.Whitebox;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Unit test for {@link GarbageCollectorThread}.
  */
 public class GarbageCollectorThreadTest {
-
+    private static final Logger LOG = LoggerFactory.getLogger(GarbageCollectorThreadTest.class);
     @InjectMocks
     @Spy
     private GarbageCollectorThread mockGCThread;
@@ -78,4 +81,31 @@ public class GarbageCollectorThreadTest {
         mockGCThread.compactEntryLog(new EntryLogMetadata(9999));
         assertFalse(compacting.get());
     }
+
+    @Test
+    public void testCalculateUsageBucket() {
+        // Valid range for usage is [0.0 to 1.0]
+        final int numBuckets = 10;
+        int[] usageBuckets = new int[numBuckets];
+        for (int i = 0; i < numBuckets; i++) {
+            usageBuckets[i] = 0;
+        }
+
+        int items = 10000;
+        for (int item = 0; item <= items; item++) {
+            double usage = ((double) item / (double) items);
+            int index = mockGCThread.calculateUsageIndex(numBuckets, usage);
+            Assert.assertFalse("Boundary condition exceeded", index < 0 || index >= numBuckets);
+            LOG.debug("Mapped {} usage to {}}\n", usage, index);
+            usageBuckets[index]++;
+        }
+        LOG.info(
+                "Compaction: entry log usage buckets[10% 20% 30% 40% 50% 60% 70% 80% 90% 100%] = {}",
+                usageBuckets);
+        int sum = 0;
+        for (int i = 0; i < numBuckets; i++) {
+            sum += usageBuckets[i];
+        }
+        Assert.assertEquals("Incorrect number of items", items + 1, sum);
+    }
 }