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

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


The following commit(s) were added to refs/heads/master by this push:
     new f8bfcffc9ba Fix subscription stalls after consumer disconnect (#18760)
f8bfcffc9ba is described below

commit f8bfcffc9baf0e1b3c046bf8bf9a2a8b4b01a74b
Author: Caideyipi <[email protected]>
AuthorDate: Wed Sep 30 10:08:35 2026 +0800

    Fix subscription stalls after consumer disconnect (#18760)
---
 .../agent/SubscriptionBrokerAgent.java             |  8 ++++
 .../agent/SubscriptionReceiverAgent.java           | 12 +++++
 .../broker/ConsensusSubscriptionBroker.java        |  8 ++++
 .../subscription/broker/ISubscriptionBroker.java   |  3 ++
 .../db/subscription/broker/SubscriptionBroker.java |  7 +++
 .../broker/SubscriptionPrefetchingQueue.java       | 11 +++++
 .../consensus/ConsensusPrefetchingQueue.java       | 10 +++++
 .../receiver/SubscriptionReceiver.java             |  5 +++
 .../receiver/SubscriptionReceiverV1.java           | 15 +++++++
 .../agent/SubscriptionReceiverAgentTest.java       | 51 ++++++++++++++++++++++
 ...onsensusSubscriptionBrokerPayloadLimitTest.java | 14 ++++++
 .../SubscriptionBrokerAgentPayloadLimitTest.java   | 14 ++++++
 .../receiver/SubscriptionReceiverV1Test.java       | 22 +++++++++-
 13 files changed, 178 insertions(+), 2 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionBrokerAgent.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionBrokerAgent.java
index e65f1d88041..93f64aa0381 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionBrokerAgent.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionBrokerAgent.java
@@ -228,6 +228,14 @@ public class SubscriptionBrokerAgent {
     return false;
   }
 
+  public int requeueInFlightEvents(final ConsumerConfig consumerConfig) {
+    final String consumerGroupId = consumerConfig.getConsumerGroupId();
+    final String consumerId = consumerConfig.getConsumerId();
+    return getBrokers(consumerGroupId).stream()
+        .mapToInt(broker -> broker.requeueInFlightEvents(consumerId))
+        .sum();
+  }
+
   public int refreshInFlightEventLeases(
       final ConsumerConfig consumerConfig,
       final List<SubscriptionCommitContext> processorBufferedCommitContexts) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java
index dc42d4fbcae..a213eac6dfc 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java
@@ -161,6 +161,8 @@ public class SubscriptionReceiverAgent {
               if (isHandshake(req)) {
                 if (isSuccessful(requestResult.response)) {
                   if (currentReceiver != null && currentReceiver != receiver) {
+                    releaseInFlightEventsOnNewInstanceTakeover(
+                        currentReceiver, receiver.getConsumerInstanceId());
                     invalidateReplacedReceiver(currentReceiver, identity);
                   }
                   return receiver;
@@ -319,6 +321,8 @@ public class SubscriptionReceiverAgent {
           }
           if (receiver.getConsumerInstanceId() != null
               && !shouldKeepCurrentReceiver(currentReceiver, 
receiver.getConsumerInstanceId())) {
+            releaseInFlightEventsOnNewInstanceTakeover(
+                currentReceiver, receiver.getConsumerInstanceId());
             invalidateReplacedReceiver(currentReceiver, key);
             registered.set(true);
             return receiver;
@@ -361,6 +365,14 @@ public class SubscriptionReceiverAgent {
     receiver.invalidateConsumer();
   }
 
+  private static void releaseInFlightEventsOnNewInstanceTakeover(
+      final SubscriptionReceiver currentReceiver, final String 
incomingConsumerInstanceId) {
+    if (incomingConsumerInstanceId != null
+        && !Objects.equals(currentReceiver.getConsumerInstanceId(), 
incomingConsumerInstanceId)) {
+      currentReceiver.releaseInFlightEvents();
+    }
+  }
+
   private static ConsumerConnection getConsumerConnection(
       final TPipeSubscribeReq req, final SubscriptionReceiver receiver) {
     if (isHandshake(req) && req.isSetBody()) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBroker.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBroker.java
index 9765cdc4ea0..6b29f069524 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBroker.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBroker.java
@@ -316,6 +316,14 @@ public class ConsensusSubscriptionBroker implements 
ISubscriptionBroker {
     return Objects.nonNull(queue) && queue.requeue(consumerId, commitContext);
   }
 
+  @Override
+  public int requeueInFlightEvents(final String consumerId) {
+    return topicNameToConsensusPrefetchingQueues.values().stream()
+        .flatMap(List::stream)
+        .mapToInt(queue -> queue.requeueInFlightEvents(consumerId))
+        .sum();
+  }
+
   @Override
   public int refreshInFlightEventLeases(
       final String consumerId, final List<SubscriptionCommitContext> 
commitContexts) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ISubscriptionBroker.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ISubscriptionBroker.java
index 90eb947292e..567b93efada 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ISubscriptionBroker.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/ISubscriptionBroker.java
@@ -52,6 +52,9 @@ public interface ISubscriptionBroker {
   /** Returns an in-flight event to its prefetching queue without incrementing 
its nack count. */
   boolean requeue(String consumerId, SubscriptionCommitContext commitContext);
 
+  /** Returns all in-flight events held by a consumer without incrementing 
their nack counts. */
+  int requeueInFlightEvents(String consumerId);
+
   default List<SubscriptionCommitContext> selectAcceptedCommitContexts(
       final List<SubscriptionCommitContext> commitContexts) {
     if (Objects.isNull(commitContexts) || commitContexts.isEmpty()) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java
index 0353e3d74b1..3b1dec89dd1 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java
@@ -392,6 +392,13 @@ public class SubscriptionBroker implements 
ISubscriptionBroker {
         && prefetchingQueue.requeue(consumerId, commitContext);
   }
 
+  @Override
+  public int requeueInFlightEvents(final String consumerId) {
+    return topicNameToPrefetchingQueue.values().stream()
+        .mapToInt(queue -> queue.requeueInFlightEvents(consumerId))
+        .sum();
+  }
+
   @Override
   public int refreshInFlightEventLeases(
       final String consumerId, final List<SubscriptionCommitContext> 
commitContexts) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java
index 2f37ac6f64b..b760ee62f6f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java
@@ -850,6 +850,17 @@ public abstract class SubscriptionPrefetchingQueue {
     }
   }
 
+  public int requeueInFlightEvents(final String consumerId) {
+    int requeuedCount = 0;
+    for (final Pair<String, SubscriptionCommitContext> key :
+        ImmutableSet.copyOf(inFlightEvents.keySet())) {
+      if (Objects.equals(consumerId, key.left) && requeue(consumerId, 
key.right)) {
+        requeuedCount++;
+      }
+    }
+    return requeuedCount;
+  }
+
   /**
    * @return {@code true} if ack successfully
    */
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
index 79cb048afac..d415656986b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
@@ -2717,6 +2717,16 @@ public class ConsensusPrefetchingQueue {
     }
   }
 
+  public int requeueInFlightEvents(final String consumerId) {
+    int requeuedCount = 0;
+    for (final InFlightEventKey key : new 
ArrayList<>(inFlightEvents.keySet())) {
+      if (Objects.equals(consumerId, key.consumerId) && requeue(consumerId, 
key.commitContext)) {
+        requeuedCount++;
+      }
+    }
+    return requeuedCount;
+  }
+
   private boolean canAcceptCommitContext(
       final SubscriptionCommitContext commitContext, final String action, 
final boolean silent) {
     if (isClosed || closeRequested || pendingSeekRequest != null) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiver.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiver.java
index 1d2fbe4fc18..01c736b29dc 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiver.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiver.java
@@ -33,6 +33,11 @@ public interface SubscriptionReceiver {
 
   void handleExit();
 
+  /** Releases the in-flight events held by this receiver's current consumer. 
*/
+  default void releaseInFlightEvents() {
+    // no-op for receivers without subscription event ownership
+  }
+
   void handleTimeout();
 
   /**
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
index 57c768e4c7c..2c2b00eca12 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
@@ -232,6 +232,16 @@ public class SubscriptionReceiverV1 implements 
SubscriptionReceiver {
     }
   }
 
+  @Override
+  public void releaseInFlightEvents() {
+    synchronized (this) {
+      final ConsumerConfig consumerConfig = sharedConsumerConfig;
+      if (Objects.nonNull(consumerConfig) && !consumerInvalidated) {
+        releaseInFlightEvents(consumerConfig);
+      }
+    }
+  }
+
   @Override
   public void handleExit() {
     synchronized (this) {
@@ -246,6 +256,7 @@ public class SubscriptionReceiverV1 implements 
SubscriptionReceiver {
           // progress to continue consuming. When another connection has 
already taken ownership,
           // the receiver is invalidated and even this best-effort cleanup 
must be skipped.
           if (!consumerInvalidated) {
+            releaseInFlightEvents(consumerConfig);
             // When handling exit, unsubscribe from topics that have already 
been completed as much
             // as possible to release some resources (such as the underlying 
pipe) in a timely
             // manner.
@@ -493,6 +504,10 @@ public class SubscriptionReceiverV1 implements 
SubscriptionReceiver {
         .refreshInFlightEventLeases(consumerConfig, 
processorBufferedCommitContexts);
   }
 
+  protected int releaseInFlightEvents(final ConsumerConfig consumerConfig) {
+    return SubscriptionAgent.broker().requeueInFlightEvents(consumerConfig);
+  }
+
   private TPipeSubscribeResp handlePipeSubscribeSubscribe(final 
PipeSubscribeSubscribeReq req) {
     try {
       return handlePipeSubscribeSubscribeInternal(req);
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java
index 2d48518e5f2..21ef6ecb101 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java
@@ -331,6 +331,51 @@ public class SubscriptionReceiverAgentTest {
     Assert.assertFalse(receivers.get(1).invalidated);
   }
 
+  @Test
+  public void testNewConsumerInstanceTakeoverReleasesOldInFlightEvents() 
throws Exception {
+    final CopyOnWriteArrayList<FakeSubscriptionReceiver> receivers = new 
CopyOnWriteArrayList<>();
+    final SubscriptionReceiverAgent agent = createAgent(receivers, false /* 
closeOnTimeout */);
+
+    Assert.assertEquals(
+        TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+        agent
+            .handle(createHandshakeRequest("group", "consumer", "instance-1"), 
"root")
+            .getStatus()
+            .getCode());
+    Assert.assertEquals(
+        TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+        handleOnNewConnection(agent, createHandshakeRequest("group", 
"consumer", "instance-2"))
+            .getStatus()
+            .getCode());
+
+    Assert.assertEquals(2, receivers.size());
+    Assert.assertEquals(1, receivers.get(0).releaseCount.get());
+    Assert.assertTrue(receivers.get(0).invalidated);
+    Assert.assertEquals(0, receivers.get(1).releaseCount.get());
+  }
+
+  @Test
+  public void testSameConsumerInstanceReconnectPreservesInFlightEvents() 
throws Exception {
+    final CopyOnWriteArrayList<FakeSubscriptionReceiver> receivers = new 
CopyOnWriteArrayList<>();
+    final SubscriptionReceiverAgent agent = createAgent(receivers, false /* 
closeOnTimeout */);
+
+    Assert.assertEquals(
+        TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+        agent
+            .handle(createHandshakeRequest("group", "consumer", "instance-1"), 
"root")
+            .getStatus()
+            .getCode());
+    Assert.assertEquals(
+        TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+        handleOnNewConnection(agent, createHandshakeRequest("group", 
"consumer", "instance-1"))
+            .getStatus()
+            .getCode());
+
+    Assert.assertEquals(2, receivers.size());
+    Assert.assertEquals(0, receivers.get(0).releaseCount.get());
+    Assert.assertTrue(receivers.get(0).invalidated);
+  }
+
   @Test
   public void testLateHandshakeCannotTakeOverNewReceiver() throws Exception {
     final CopyOnWriteArrayList<FakeSubscriptionReceiver> receivers = new 
CopyOnWriteArrayList<>();
@@ -565,6 +610,7 @@ public class SubscriptionReceiverAgentTest {
     private final CountDownLatch releaseHandshake;
     private final AtomicInteger timeoutCount = new AtomicInteger();
     private final AtomicInteger exitCount = new AtomicInteger();
+    private final AtomicInteger releaseCount = new AtomicInteger();
     private volatile ConsumerConfig consumerConfig;
     private volatile boolean invalidated;
 
@@ -627,6 +673,11 @@ public class SubscriptionReceiverAgentTest {
       exitCount.incrementAndGet();
     }
 
+    @Override
+    public void releaseInFlightEvents() {
+      releaseCount.incrementAndGet();
+    }
+
     @Override
     public void handleTimeout() {
       timeoutCount.incrementAndGet();
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBrokerPayloadLimitTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBrokerPayloadLimitTest.java
index 1d2e7137535..d07ec0b557f 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBrokerPayloadLimitTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/ConsensusSubscriptionBrokerPayloadLimitTest.java
@@ -73,6 +73,20 @@ public class ConsensusSubscriptionBrokerPayloadLimitTest {
     verify(secondQueue).requeue(CONSUMER_ID, secondCommitContext);
   }
 
+  @Test
+  public void testRequeueInFlightEventsAcrossRegionQueues() throws Exception {
+    final ConsensusSubscriptionBroker broker = new 
ConsensusSubscriptionBroker(CONSUMER_GROUP_ID);
+    final ConsensusPrefetchingQueue firstQueue = 
mock(ConsensusPrefetchingQueue.class);
+    final ConsensusPrefetchingQueue secondQueue = 
mock(ConsensusPrefetchingQueue.class);
+    when(firstQueue.requeueInFlightEvents(CONSUMER_ID)).thenReturn(2);
+    when(secondQueue.requeueInFlightEvents(CONSUMER_ID)).thenReturn(3);
+    bindQueues(broker, Arrays.asList(firstQueue, secondQueue));
+
+    assertEquals(5, broker.requeueInFlightEvents(CONSUMER_ID));
+    verify(firstQueue).requeueInFlightEvents(CONSUMER_ID);
+    verify(secondQueue).requeueInFlightEvents(CONSUMER_ID);
+  }
+
   private static SubscriptionCommitContext newCommitContext(
       final int regionId, final int commitId) {
     return new SubscriptionCommitContext(
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/SubscriptionBrokerAgentPayloadLimitTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/SubscriptionBrokerAgentPayloadLimitTest.java
index 782f2338e9d..d0fdafc6e4d 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/SubscriptionBrokerAgentPayloadLimitTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/SubscriptionBrokerAgentPayloadLimitTest.java
@@ -78,6 +78,20 @@ public class SubscriptionBrokerAgentPayloadLimitTest {
     assertEquals(0L, secondEvent.getNackCount());
   }
 
+  @Test
+  public void testRequeueInFlightEventsAcrossBrokers() throws Exception {
+    final SubscriptionBrokerAgent agent = new SubscriptionBrokerAgent();
+    final ISubscriptionBroker firstBroker = mock(ISubscriptionBroker.class);
+    final ISubscriptionBroker secondBroker = mock(ISubscriptionBroker.class);
+    when(firstBroker.requeueInFlightEvents(CONSUMER_ID)).thenReturn(2);
+    when(secondBroker.requeueInFlightEvents(CONSUMER_ID)).thenReturn(3);
+    bindBrokers(agent, firstBroker, secondBroker);
+
+    assertEquals(5, agent.requeueInFlightEvents(createConsumerConfig()));
+    verify(firstBroker).requeueInFlightEvents(CONSUMER_ID);
+    verify(secondBroker).requeueInFlightEvents(CONSUMER_ID);
+  }
+
   private static SubscriptionEvent newEvent(final int commitId) {
     return new SubscriptionEvent(
         SubscriptionPollResponseType.TERMINATION.getType(),
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
index 53f42666c65..8b2bd022241 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
@@ -131,7 +131,15 @@ public class SubscriptionReceiverV1Test {
   @Test
   @SuppressWarnings("unchecked")
   public void testHandleExitKeepsSharedConsumerStateForTimeoutCleanup() throws 
Exception {
-    final SubscriptionReceiverV1 receiver = new SubscriptionReceiverV1();
+    final AtomicLong releaseCount = new AtomicLong();
+    final SubscriptionReceiverV1 receiver =
+        new SubscriptionReceiverV1() {
+          @Override
+          protected int releaseInFlightEvents(final ConsumerConfig 
consumerConfig) {
+            releaseCount.incrementAndGet();
+            return 1;
+          }
+        };
     final ConsumerConfig consumerConfig = createConsumerConfig(1_000L);
     setField(receiver, "sharedConsumerConfig", consumerConfig);
     final ThreadLocal<ConsumerConfig> consumerConfigThreadLocal =
@@ -144,12 +152,21 @@ public class SubscriptionReceiverV1Test {
     Assert.assertFalse((boolean) getField(receiver, "consumerInvalidated"));
     Assert.assertTrue(receiver.hasActiveConsumer());
     Assert.assertNull(consumerConfigThreadLocal.get());
+    Assert.assertEquals(1L, releaseCount.get());
   }
 
   @Test
   @SuppressWarnings("unchecked")
   public void testHandleExitClearsThreadLocalStateAfterInvalidation() throws 
Exception {
-    final SubscriptionReceiverV1 receiver = new SubscriptionReceiverV1();
+    final AtomicLong releaseCount = new AtomicLong();
+    final SubscriptionReceiverV1 receiver =
+        new SubscriptionReceiverV1() {
+          @Override
+          protected int releaseInFlightEvents(final ConsumerConfig 
consumerConfig) {
+            releaseCount.incrementAndGet();
+            return 1;
+          }
+        };
     final ConsumerConfig consumerConfig = createConsumerConfig(1_000L);
     setField(receiver, "sharedConsumerConfig", consumerConfig);
     final ThreadLocal<ConsumerConfig> consumerConfigThreadLocal =
@@ -162,6 +179,7 @@ public class SubscriptionReceiverV1Test {
     Assert.assertFalse(receiver.hasActiveConsumer());
     Assert.assertTrue((boolean) getField(receiver, "consumerInvalidated"));
     Assert.assertNull(consumerConfigThreadLocal.get());
+    Assert.assertEquals(0L, releaseCount.get());
   }
 
   @Test

Reply via email to