You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by lh...@apache.org on 2026/10/20 13:20:51 UTC

(pulsar) 13/19: [fix][broker] Remove the sticky key hashes of a sparse replay prefix when the mark-delete position moves (#26976)

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

lhotari pushed a commit to branch branch-4.2-cherry-picks-2026-10-20
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 7efe7a4637ea60b567b716e7725a0feb9b3b2e15
Author: Lari Hotari <lh...@users.noreply.github.com>
AuthorDate: Sun Oct 18 22:59:00 2026 +0300

    [fix][broker] Remove the sticky key hashes of a sparse replay prefix when the mark-delete position moves (#26976)
    
    (cherry picked from commit d137d35fba8ef57c47641fb4ed03a71edca9b72c)
---
 .../utils/ConcurrentBitmapSortedLongPairSet.java       |  3 ++-
 .../persistent/MessageRedeliveryControllerTest.java    | 18 ++++++++++++++++++
 .../utils/ConcurrentBitmapSortedLongPairSetTest.java   | 13 +++++++++++++
 3 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSet.java b/pulsar-broker/src/main/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSet.java
index 70437d07dbee..281dd445fff5 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSet.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSet.java
@@ -94,7 +94,8 @@ public class ConcurrentBitmapSortedLongPairSet {
                 } else {
                     RoaringBitmap bitSet = firstEntry.getValue();
                     if (bitSet != null) {
-                        bitsCleared |= bitSet.contains(0, item2);
+                        // any bit below item2, not every one: a sparse prefix has gaps
+                        bitsCleared |= bitSet.intersects(0, item2);
                         bitSet.remove(0, item2);
                         if (bitSet.isEmpty()) {
                             map.remove(firstEntry.getKey(), bitSet);
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/persistent/MessageRedeliveryControllerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/persistent/MessageRedeliveryControllerTest.java
index 03c7e6cf439e..2f9b22f98c98 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/persistent/MessageRedeliveryControllerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/persistent/MessageRedeliveryControllerTest.java
@@ -144,6 +144,24 @@ public class MessageRedeliveryControllerTest {
         }
     }
 
+    @Test(dataProvider = "allowOutOfOrderDelivery", timeOut = 10000)
+    public void testRemoveAllUpToASparsePrefixRemovesItsHashes(boolean allowOutOfOrderDelivery) {
+        MessageRedeliveryController controller = new MessageRedeliveryController(allowOutOfOrderDelivery);
+        // a gap between the positions removed and the one kept, in the same ledger
+        controller.add(1, 5, 100);
+        controller.add(1, 100, 200);
+
+        controller.removeAllUpTo(1, 10);
+
+        assertEquals(controller.size(), 1);
+        assertNull(controller.getHash(1, 5));
+        assertEquals(controller.getHash(1, 100), Long.valueOf(200));
+        assertFalse(controller.containsStickyKeyHash(100));
+        if (!allowOutOfOrderDelivery) {
+            assertTrue(controller.containsStickyKeyHash(200));
+        }
+    }
+
     @Test(timeOut = 10000)
     public void testOutOfOrderSentinelHashDoesNotInitializePositionHashMap() {
         MessageRedeliveryController controller = new MessageRedeliveryController(true);
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSetTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSetTest.java
index 29e5ae5e735e..923e669ccc9c 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSetTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/utils/ConcurrentBitmapSortedLongPairSetTest.java
@@ -128,6 +128,19 @@ public class ConcurrentBitmapSortedLongPairSetTest {
         assertTrue(set.isEmpty());
     }
 
+    @Test
+    public void testRemoveUpToReportsASparsePrefix() {
+        ConcurrentBitmapSortedLongPairSet set = new ConcurrentBitmapSortedLongPairSet();
+        set.add(1, 5);
+        set.add(1, 100);
+
+        // removes 1:5 although 1:0 to 1:4 and 1:6 to 1:10 were never there
+        assertTrue(set.removeUpTo(1, 11));
+        assertFalse(set.contains(1, 5));
+        assertTrue(set.contains(1, 100));
+        assertFalse(set.removeUpTo(1, 11));
+    }
+
     @Test
     public void testItems() {
         ConcurrentBitmapSortedLongPairSet set = new ConcurrentBitmapSortedLongPairSet();