@@ -712,6 +712,8 @@ private final class ListenerConsumer implements SchedulingAwareRunnable, Consume
712
712
713
713
private final Header infoHeader = new RecordHeader (KafkaHeaders .LISTENER_INFO , this .listenerinfo );
714
714
715
+ private final Set <TopicPartition > pausedForNack = new HashSet <>();
716
+
715
717
private Map <TopicPartition , OffsetMetadata > definedPartitions ;
716
718
717
719
private int count ;
@@ -728,6 +730,8 @@ private final class ListenerConsumer implements SchedulingAwareRunnable, Consume
728
730
729
731
private long nackSleep = -1 ;
730
732
733
+ private long nackWake ;
734
+
731
735
private int nackIndex ;
732
736
733
737
private Iterator <TopicPartition > batchIterator ;
@@ -1597,6 +1601,10 @@ private void pauseConsumerIfNecessary() {
1597
1601
}
1598
1602
1599
1603
private void doPauseConsumerIfNecessary () {
1604
+ if (this .pausedForNack .size () > 0 ) {
1605
+ this .logger .debug ("Still paused for nack sleep" );
1606
+ return ;
1607
+ }
1600
1608
if (this .offsetsInThisBatch != null && this .offsetsInThisBatch .size () > 0 && !this .pausedForAsyncAcks ) {
1601
1609
this .pausedForAsyncAcks = true ;
1602
1610
this .logger .debug (() -> "Pausing for incomplete async acks: " + this .offsetsInThisBatch );
@@ -1610,7 +1618,15 @@ private void doPauseConsumerIfNecessary() {
1610
1618
}
1611
1619
1612
1620
private void resumeConsumerIfNeccessary () {
1613
- if (this .offsetsInThisBatch != null ) {
1621
+ if (this .nackWake > 0 ) {
1622
+ if (System .currentTimeMillis () > this .nackWake ) {
1623
+ this .nackWake = 0 ;
1624
+ this .consumer .resume (this .pausedForNack );
1625
+ this .logger .debug (() -> "Resumed after nack sleep: " + this .pausedForNack );
1626
+ this .pausedForNack .clear ();
1627
+ }
1628
+ }
1629
+ else if (this .offsetsInThisBatch != null ) {
1614
1630
synchronized (this ) {
1615
1631
doResumeConsumerIfNeccessary ();
1616
1632
}
@@ -1654,12 +1670,10 @@ private void pausePartitionsIfNecessary() {
1654
1670
}
1655
1671
1656
1672
private void resumePartitionsIfNecessary () {
1657
- Set <TopicPartition > pausedConsumerPartitions = this .consumer .paused ();
1658
- List <TopicPartition > partitionsToResume = this
1659
- .assignedPartitions
1673
+ List <TopicPartition > partitionsToResume = getAssignedPartitions ()
1660
1674
.stream ()
1661
1675
.filter (tp -> !isPartitionPauseRequested (tp )
1662
- && pausedConsumerPartitions .contains (tp ))
1676
+ && this . pausedPartitions .contains (tp ))
1663
1677
.collect (Collectors .toList ());
1664
1678
if (partitionsToResume .size () > 0 ) {
1665
1679
this .consumer .resume (partitionsToResume );
@@ -2206,7 +2220,7 @@ private void invokeBatchOnMessage(final ConsumerRecords<K, V> records, // NOSONA
2206
2220
processCommits ();
2207
2221
}
2208
2222
SeekUtils .doSeeks (toSeek , this .consumer , null , true , (rec , ex ) -> false , this .logger ); // NOSONAR
2209
- nackSleepAndReset ();
2223
+ pauseForNackSleep ();
2210
2224
}
2211
2225
}
2212
2226
@@ -2467,17 +2481,29 @@ private void handleNack(final ConsumerRecords<K, V> records, final ConsumerRecor
2467
2481
}
2468
2482
}
2469
2483
SeekUtils .doSeeks (list , this .consumer , null , true , (rec , ex ) -> false , this .logger ); // NOSONAR
2470
- nackSleepAndReset ();
2484
+ pauseForNackSleep ();
2471
2485
}
2472
2486
2473
- private void nackSleepAndReset () {
2474
- try {
2475
- ListenerUtils .stoppableSleep (KafkaMessageListenerContainer .this .thisOrParentContainer , this .nackSleep );
2476
- }
2477
- catch (@ SuppressWarnings (UNUSED ) InterruptedException e ) {
2478
- Thread .currentThread ().interrupt ();
2487
+ private void pauseForNackSleep () {
2488
+ if (this .nackSleep > 0 ) {
2489
+ this .nackWake = System .currentTimeMillis () + this .nackSleep ;
2490
+ this .nackSleep = -1 ;
2491
+ Set <TopicPartition > alreadyPaused = this .consumer .paused ();
2492
+ this .pausedForNack .addAll (getAssignedPartitions ());
2493
+ this .pausedForNack .removeAll (alreadyPaused );
2494
+ this .logger .debug (() -> "Pausing for nack sleep: " + ListenerConsumer .this .pausedForNack );
2495
+ try {
2496
+ this .consumer .pause (this .pausedForNack );
2497
+ }
2498
+ catch (IllegalStateException ex ) {
2499
+ // this should never happen; defensive, just in case...
2500
+ this .logger .warn (() -> "Could not pause for nack, possible rebalance in process: "
2501
+ + ex .getMessage ());
2502
+ Set <TopicPartition > nowPaused = new HashSet <>(this .consumer .paused ());
2503
+ nowPaused .removeAll (alreadyPaused );
2504
+ this .consumer .resume (nowPaused );
2505
+ }
2479
2506
}
2480
- this .nackSleep = -1 ;
2481
2507
}
2482
2508
2483
2509
/**
@@ -3251,6 +3277,7 @@ public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
3251
3277
if (ListenerConsumer .this .assignedPartitions != null ) {
3252
3278
ListenerConsumer .this .assignedPartitions .removeAll (partitions );
3253
3279
}
3280
+ ListenerConsumer .this .pausedForNack .removeAll (partitions );
3254
3281
partitions .forEach (tp -> ListenerConsumer .this .lastCommits .remove (tp ));
3255
3282
synchronized (ListenerConsumer .this ) {
3256
3283
if (ListenerConsumer .this .offsetsInThisBatch != null ) {
@@ -3275,6 +3302,9 @@ public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
3275
3302
ListenerConsumer .this .logger .warn ("Paused consumer resumed by Kafka due to rebalance; "
3276
3303
+ "consumer paused again, so the initial poll() will never return any records" );
3277
3304
}
3305
+ if (ListenerConsumer .this .pausedForNack .size () > 0 ) {
3306
+ ListenerConsumer .this .consumer .pause (ListenerConsumer .this .pausedForNack );
3307
+ }
3278
3308
ListenerConsumer .this .assignedPartitions .addAll (partitions );
3279
3309
if (ListenerConsumer .this .commitCurrentOnAssignment
3280
3310
&& !collectAndCommitIfNecessary (partitions )) {
0 commit comments