@@ -712,6 +712,8 @@ private final class ListenerConsumer implements SchedulingAwareRunnable, Consume
712712
713713 private final Header infoHeader = new RecordHeader (KafkaHeaders .LISTENER_INFO , this .listenerinfo );
714714
715+ private final Set <TopicPartition > pausedForNack = new HashSet <>();
716+
715717 private Map <TopicPartition , OffsetMetadata > definedPartitions ;
716718
717719 private int count ;
@@ -728,6 +730,8 @@ private final class ListenerConsumer implements SchedulingAwareRunnable, Consume
728730
729731 private long nackSleep = -1 ;
730732
733+ private long nackWake ;
734+
731735 private int nackIndex ;
732736
733737 private Iterator <TopicPartition > batchIterator ;
@@ -1594,6 +1598,10 @@ private void pauseConsumerIfNecessary() {
15941598 }
15951599
15961600 private void doPauseConsumerIfNecessary () {
1601+ if (this .pausedForNack .size () > 0 ) {
1602+ this .logger .debug ("Still paused for nack sleep" );
1603+ return ;
1604+ }
15971605 if (this .offsetsInThisBatch != null && this .offsetsInThisBatch .size () > 0 && !this .pausedForAsyncAcks ) {
15981606 this .pausedForAsyncAcks = true ;
15991607 this .logger .debug (() -> "Pausing for incomplete async acks: " + this .offsetsInThisBatch );
@@ -1607,7 +1615,15 @@ private void doPauseConsumerIfNecessary() {
16071615 }
16081616
16091617 private void resumeConsumerIfNeccessary () {
1610- if (this .offsetsInThisBatch != null ) {
1618+ if (this .nackWake > 0 ) {
1619+ if (System .currentTimeMillis () > this .nackWake ) {
1620+ this .nackWake = 0 ;
1621+ this .consumer .resume (this .pausedForNack );
1622+ this .logger .debug (() -> "Resumed after nack sleep: " + this .pausedForNack );
1623+ this .pausedForNack .clear ();
1624+ }
1625+ }
1626+ else if (this .offsetsInThisBatch != null ) {
16111627 synchronized (this ) {
16121628 doResumeConsumerIfNeccessary ();
16131629 }
@@ -1651,12 +1667,10 @@ private void pausePartitionsIfNecessary() {
16511667 }
16521668
16531669 private void resumePartitionsIfNecessary () {
1654- Set <TopicPartition > pausedConsumerPartitions = this .consumer .paused ();
1655- List <TopicPartition > partitionsToResume = this
1656- .assignedPartitions
1670+ List <TopicPartition > partitionsToResume = getAssignedPartitions ()
16571671 .stream ()
16581672 .filter (tp -> !isPartitionPauseRequested (tp )
1659- && pausedConsumerPartitions .contains (tp ))
1673+ && this . pausedPartitions .contains (tp ))
16601674 .collect (Collectors .toList ());
16611675 if (partitionsToResume .size () > 0 ) {
16621676 this .consumer .resume (partitionsToResume );
@@ -2203,7 +2217,7 @@ private void invokeBatchOnMessage(final ConsumerRecords<K, V> records, // NOSONA
22032217 processCommits ();
22042218 }
22052219 SeekUtils .doSeeks (toSeek , this .consumer , null , true , (rec , ex ) -> false , this .logger ); // NOSONAR
2206- nackSleepAndReset ();
2220+ pauseForNackSleep ();
22072221 }
22082222 }
22092223
@@ -2464,17 +2478,29 @@ private void handleNack(final ConsumerRecords<K, V> records, final ConsumerRecor
24642478 }
24652479 }
24662480 SeekUtils .doSeeks (list , this .consumer , null , true , (rec , ex ) -> false , this .logger ); // NOSONAR
2467- nackSleepAndReset ();
2481+ pauseForNackSleep ();
24682482 }
24692483
2470- private void nackSleepAndReset () {
2471- try {
2472- ListenerUtils .stoppableSleep (KafkaMessageListenerContainer .this .thisOrParentContainer , this .nackSleep );
2473- }
2474- catch (@ SuppressWarnings (UNUSED ) InterruptedException e ) {
2475- Thread .currentThread ().interrupt ();
2484+ private void pauseForNackSleep () {
2485+ if (this .nackSleep > 0 ) {
2486+ this .nackWake = System .currentTimeMillis () + this .nackSleep ;
2487+ this .nackSleep = -1 ;
2488+ Set <TopicPartition > alreadyPaused = this .consumer .paused ();
2489+ this .pausedForNack .addAll (getAssignedPartitions ());
2490+ this .pausedForNack .removeAll (alreadyPaused );
2491+ this .logger .debug (() -> "Pausing for nack sleep: " + ListenerConsumer .this .pausedForNack );
2492+ try {
2493+ this .consumer .pause (this .pausedForNack );
2494+ }
2495+ catch (IllegalStateException ex ) {
2496+ // this should never happen; defensive, just in case...
2497+ this .logger .warn (() -> "Could not pause for nack, possible rebalance in process: "
2498+ + ex .getMessage ());
2499+ Set <TopicPartition > nowPaused = new HashSet <>(this .consumer .paused ());
2500+ nowPaused .removeAll (alreadyPaused );
2501+ this .consumer .resume (nowPaused );
2502+ }
24762503 }
2477- this .nackSleep = -1 ;
24782504 }
24792505
24802506 /**
@@ -3243,6 +3269,7 @@ public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
32433269 if (ListenerConsumer .this .assignedPartitions != null ) {
32443270 ListenerConsumer .this .assignedPartitions .removeAll (partitions );
32453271 }
3272+ ListenerConsumer .this .pausedForNack .removeAll (partitions );
32463273 partitions .forEach (tp -> ListenerConsumer .this .lastCommits .remove (tp ));
32473274 synchronized (ListenerConsumer .this ) {
32483275 if (ListenerConsumer .this .offsetsInThisBatch != null ) {
@@ -3267,6 +3294,9 @@ public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
32673294 ListenerConsumer .this .logger .warn ("Paused consumer resumed by Kafka due to rebalance; "
32683295 + "consumer paused again, so the initial poll() will never return any records" );
32693296 }
3297+ if (ListenerConsumer .this .pausedForNack .size () > 0 ) {
3298+ ListenerConsumer .this .consumer .pause (ListenerConsumer .this .pausedForNack );
3299+ }
32703300 ListenerConsumer .this .assignedPartitions .addAll (partitions );
32713301 if (ListenerConsumer .this .commitCurrentOnAssignment
32723302 && !collectAndCommitIfNecessary (partitions )) {
0 commit comments