@@ -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 ;
@@ -1594,6 +1598,10 @@ private void pauseConsumerIfNecessary() {
1594
1598
}
1595
1599
1596
1600
private void doPauseConsumerIfNecessary () {
1601
+ if (this .pausedForNack .size () > 0 ) {
1602
+ this .logger .debug ("Still paused for nack sleep" );
1603
+ return ;
1604
+ }
1597
1605
if (this .offsetsInThisBatch != null && this .offsetsInThisBatch .size () > 0 && !this .pausedForAsyncAcks ) {
1598
1606
this .pausedForAsyncAcks = true ;
1599
1607
this .logger .debug (() -> "Pausing for incomplete async acks: " + this .offsetsInThisBatch );
@@ -1607,7 +1615,15 @@ private void doPauseConsumerIfNecessary() {
1607
1615
}
1608
1616
1609
1617
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 ) {
1611
1627
synchronized (this ) {
1612
1628
doResumeConsumerIfNeccessary ();
1613
1629
}
@@ -1651,12 +1667,10 @@ private void pausePartitionsIfNecessary() {
1651
1667
}
1652
1668
1653
1669
private void resumePartitionsIfNecessary () {
1654
- Set <TopicPartition > pausedConsumerPartitions = this .consumer .paused ();
1655
- List <TopicPartition > partitionsToResume = this
1656
- .assignedPartitions
1670
+ List <TopicPartition > partitionsToResume = getAssignedPartitions ()
1657
1671
.stream ()
1658
1672
.filter (tp -> !isPartitionPauseRequested (tp )
1659
- && pausedConsumerPartitions .contains (tp ))
1673
+ && this . pausedPartitions .contains (tp ))
1660
1674
.collect (Collectors .toList ());
1661
1675
if (partitionsToResume .size () > 0 ) {
1662
1676
this .consumer .resume (partitionsToResume );
@@ -2203,7 +2217,7 @@ private void invokeBatchOnMessage(final ConsumerRecords<K, V> records, // NOSONA
2203
2217
processCommits ();
2204
2218
}
2205
2219
SeekUtils .doSeeks (toSeek , this .consumer , null , true , (rec , ex ) -> false , this .logger ); // NOSONAR
2206
- nackSleepAndReset ();
2220
+ pauseForNackSleep ();
2207
2221
}
2208
2222
}
2209
2223
@@ -2464,17 +2478,29 @@ private void handleNack(final ConsumerRecords<K, V> records, final ConsumerRecor
2464
2478
}
2465
2479
}
2466
2480
SeekUtils .doSeeks (list , this .consumer , null , true , (rec , ex ) -> false , this .logger ); // NOSONAR
2467
- nackSleepAndReset ();
2481
+ pauseForNackSleep ();
2468
2482
}
2469
2483
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
+ }
2476
2503
}
2477
- this .nackSleep = -1 ;
2478
2504
}
2479
2505
2480
2506
/**
@@ -3243,6 +3269,7 @@ public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
3243
3269
if (ListenerConsumer .this .assignedPartitions != null ) {
3244
3270
ListenerConsumer .this .assignedPartitions .removeAll (partitions );
3245
3271
}
3272
+ ListenerConsumer .this .pausedForNack .removeAll (partitions );
3246
3273
partitions .forEach (tp -> ListenerConsumer .this .lastCommits .remove (tp ));
3247
3274
synchronized (ListenerConsumer .this ) {
3248
3275
if (ListenerConsumer .this .offsetsInThisBatch != null ) {
@@ -3267,6 +3294,9 @@ public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
3267
3294
ListenerConsumer .this .logger .warn ("Paused consumer resumed by Kafka due to rebalance; "
3268
3295
+ "consumer paused again, so the initial poll() will never return any records" );
3269
3296
}
3297
+ if (ListenerConsumer .this .pausedForNack .size () > 0 ) {
3298
+ ListenerConsumer .this .consumer .pause (ListenerConsumer .this .pausedForNack );
3299
+ }
3270
3300
ListenerConsumer .this .assignedPartitions .addAll (partitions );
3271
3301
if (ListenerConsumer .this .commitCurrentOnAssignment
3272
3302
&& !collectAndCommitIfNecessary (partitions )) {
0 commit comments