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

hangc0276 pushed a commit to branch branch-4.17
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/branch-4.17 by this push:
     new c9361a0e3c [fix] Ledger Replication worker should throttle on 
verrification reads (#4678)
c9361a0e3c is described below

commit c9361a0e3ce2ae67d0327b4851d949aa37d78fe1
Author: vraulji567 <[email protected]>
AuthorDate: Tue May 26 20:43:17 2026 -0400

    [fix] Ledger Replication worker should throttle on verrification reads 
(#4678)
---
 .../bookkeeper/replication/ReplicationWorker.java  |  2 +-
 .../bookkeeper/client/TestLedgerChecker.java       | 51 ++++++++++++++++++++++
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/ReplicationWorker.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/ReplicationWorker.java
index 05807455dc..cebd8a1b55 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/ReplicationWorker.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/ReplicationWorker.java
@@ -192,7 +192,7 @@ public class ReplicationWorker implements Runnable {
         this.underreplicationManager = 
bkc.getLedgerManagerFactory().newLedgerUnderreplicationManager();
         this.ledgerManager = bkc.getLedgerManagerFactory().newLedgerManager();
         this.admin = new BookKeeperAdmin(bkc, statsLogger, new 
ClientConfiguration(conf));
-        this.ledgerChecker = new LedgerChecker(bkc);
+        this.ledgerChecker = new LedgerChecker(bkc, 
conf.getInFlightReadEntryNumInLedgerChecker());
         this.workerThread = new BookieThread(this, "ReplicationWorker");
         this.openLedgerRereplicationGracePeriod = conf
                 .getOpenLedgerRereplicationGracePeriod();
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestLedgerChecker.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestLedgerChecker.java
index 222cd0aac4..545c9b8ac0 100644
--- 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestLedgerChecker.java
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestLedgerChecker.java
@@ -24,6 +24,9 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -34,6 +37,8 @@ import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.bookkeeper.net.BookieId;
+import org.apache.bookkeeper.proto.BookieClient;
+import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks;
 import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
 import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
 import org.junit.Test;
@@ -530,4 +535,50 @@ public class TestLedgerChecker extends 
BookKeeperClusterTestCase {
         killBookie(ensemble);
     }
 
+    @Test
+    public void testLedgerCheckerDoesNotExceedPermits() throws Exception {
+        int permits = 100;
+
+        // Atomic counter to track concurrent reads
+        AtomicInteger concurrentReads = new AtomicInteger(0);
+        AtomicInteger maxConcurrentReads = new AtomicInteger(0);
+
+        // Mock Bookie Client
+        BookieClient mockBookieClient = mock(BookieClient.class);
+        BookieWatcher mockBookieWatcher = mock(BookieWatcher.class);
+        when(mockBookieWatcher.isBookieUnavailable(any())).thenReturn(false);
+
+        doAnswer(invocation -> {
+            int now = concurrentReads.incrementAndGet();
+            maxConcurrentReads.updateAndGet(prev -> Math.max(prev, now));
+
+            // Simulate async completion
+            new Thread(() -> {
+                try{Thread.sleep(10);} catch (InterruptedException ie){}
+                int d = concurrentReads.decrementAndGet();
+                BookkeeperInternalCallbacks.ReadEntryCallback cb = 
invocation.getArgument(3);
+                cb.readEntryComplete(0, invocation.getArgument(1),
+                        invocation.getArgument(2), null, null);
+            }).start();
+            return null;
+        }).when(mockBookieClient).readEntry(any(BookieId.class), anyLong(), 
anyLong(),
+                any(), any(), anyInt());
+
+        // Create dummy LedgerHandle with enough fragments
+        LedgerHandle lh = bkc.createLedger(BookKeeper.DigestType.CRC32, 
TEST_LEDGER_PASSWORD);
+        startNewBookie();
+        for(int i = 0; i < 1000; i++) {
+            lh.addEntry(TEST_LEDGER_ENTRY_DATA);
+        }
+
+        LedgerChecker checker = new LedgerChecker(mockBookieClient, 
mockBookieWatcher, permits);
+        checker.checkLedger(lh, (rc, result) -> {}, 100);
+
+        // Wait for all async reads to finish
+        Thread.sleep(2000);
+
+        System.out.println("Max Concurrent reads observed = " + 
maxConcurrentReads.get());
+        assertTrue("Exceeded permit limit!", maxConcurrentReads.get() <= 
permits);
+    }
+
 }

Reply via email to