5151import org .apache .kafka .clients .consumer .OffsetAndMetadata ;
5252import org .apache .kafka .clients .consumer .OffsetAndTimestamp ;
5353import org .apache .kafka .clients .consumer .OffsetCommitCallback ;
54+ import org .apache .kafka .clients .consumer .RetriableCommitFailedException ;
5455import org .apache .kafka .clients .producer .Producer ;
5556import org .apache .kafka .common .Metric ;
5657import org .apache .kafka .common .MetricName ;
@@ -1204,6 +1205,10 @@ private void wrapUp() {
12041205 * @param e the exception.
12051206 */
12061207 protected void handleConsumerException (Exception e ) {
1208+ if (e instanceof RetriableCommitFailedException ) {
1209+ this .logger .error (e , "Commit retries exhausted" );
1210+ return ;
1211+ }
12071212 try {
12081213 if (!this .isBatchListener && this .errorHandler != null ) {
12091214 this .errorHandler .handle (e , Collections .emptyList (), this .consumer ,
@@ -1283,13 +1288,25 @@ private void ackImmediate(ConsumerRecord<K, V> record) {
12831288 this .producer .sendOffsetsToTransaction (commits , this .consumerGroupId );
12841289 }
12851290 else if (this .syncCommits ) {
1286- this . consumer . commitSync (commits , this . syncCommitTimeout );
1291+ commitSync (commits );
12871292 }
12881293 else {
1289- this . consumer . commitAsync (commits , this . commitCallback );
1294+ commitAsync (commits , 0 );
12901295 }
12911296 }
12921297
1298+ private void commitAsync (Map <TopicPartition , OffsetAndMetadata > commits , int retries ) {
1299+ this .consumer .commitAsync (commits , (offsetsAttempted , exception ) -> {
1300+ if (exception instanceof RetriableCommitFailedException
1301+ && retries < this .containerProperties .getCommitRetries ()) {
1302+ commitAsync (commits , retries + 1 );
1303+ }
1304+ else {
1305+ this .commitCallback .onComplete (offsetsAttempted , exception );
1306+ }
1307+ });
1308+ }
1309+
12931310 private void invokeListener (final ConsumerRecords <K , V > records ) {
12941311 if (this .isBatchListener ) {
12951312 invokeBatchListener (records );
@@ -1851,10 +1868,10 @@ public void ackCurrent(final ConsumerRecord<K, V> record,
18511868 if (producer == null ) {
18521869 this .commitLogger .log (() -> "Committing: " + offsetsToCommit );
18531870 if (this .syncCommits ) {
1854- this . consumer . commitSync (offsetsToCommit , this . syncCommitTimeout );
1871+ commitSync (offsetsToCommit );
18551872 }
18561873 else {
1857- this . consumer . commitAsync (offsetsToCommit , this . commitCallback );
1874+ commitAsync (offsetsToCommit , 0 );
18581875 }
18591876 }
18601877 else {
@@ -2074,10 +2091,10 @@ private void commitIfNecessary() {
20742091 this .commitLogger .log (() -> "Committing: " + commits );
20752092 try {
20762093 if (this .syncCommits ) {
2077- this . consumer . commitSync (commits , this . syncCommitTimeout );
2094+ commitSync (commits );
20782095 }
20792096 else {
2080- this . consumer . commitAsync (commits , this . commitCallback );
2097+ commitAsync (commits , 0 );
20812098 }
20822099 }
20832100 catch (@ SuppressWarnings (UNUSED ) WakeupException e ) {
@@ -2087,6 +2104,22 @@ private void commitIfNecessary() {
20872104 }
20882105 }
20892106
2107+ private void commitSync (Map <TopicPartition , OffsetAndMetadata > commits ) {
2108+ doCommitSync (commits , 0 );
2109+ }
2110+
2111+ private void doCommitSync (Map <TopicPartition , OffsetAndMetadata > commits , int retries ) {
2112+ try {
2113+ this .consumer .commitSync (commits , this .syncCommitTimeout );
2114+ }
2115+ catch (RetriableCommitFailedException e ) {
2116+ if (retries >= this .containerProperties .getCommitRetries ()) {
2117+ throw e ;
2118+ }
2119+ doCommitSync (commits , retries + 1 );
2120+ }
2121+ }
2122+
20902123 private Map <TopicPartition , OffsetAndMetadata > buildCommits () {
20912124 Map <TopicPartition , OffsetAndMetadata > commits = new HashMap <>();
20922125 for (Entry <String , Map <Integer , Long >> entry : this .offsets .entrySet ()) {
@@ -2353,6 +2386,7 @@ private boolean collectAndCommitIfNecessary(Collection<TopicPartition> partition
23532386 return true ;
23542387 }
23552388
2389+ @ SuppressWarnings ("unused" )
23562390 private void commitCurrentOffsets (Map <TopicPartition , OffsetAndMetadata > offsetsToCommit ) {
23572391 ListenerConsumer .this .commitLogger .log (() -> "Committing on assignment: " + offsetsToCommit );
23582392 if (ListenerConsumer .this .transactionTemplate != null
@@ -2392,12 +2426,16 @@ protected void doInTransactionWithoutResult(TransactionStatus status) {
23922426 else {
23932427 ContainerProperties containerProps = KafkaMessageListenerContainer .this .getContainerProperties ();
23942428 if (containerProps .isSyncCommits ()) {
2395- ListenerConsumer .this .consumer .commitSync (offsetsToCommit ,
2396- containerProps .getSyncCommitTimeout ());
2429+ try {
2430+ ListenerConsumer .this .consumer .commitSync (offsetsToCommit ,
2431+ containerProps .getSyncCommitTimeout ());
2432+ }
2433+ catch (RetriableCommitFailedException e ) {
2434+ // ignore since this is on assignment anyway
2435+ }
23972436 }
23982437 else {
2399- ListenerConsumer .this .consumer .commitAsync (offsetsToCommit ,
2400- containerProps .getCommitCallback ());
2438+ commitAsync (offsetsToCommit , 0 );
24012439 }
24022440 }
24032441 }
0 commit comments