4949import org .apache .kafka .clients .consumer .OffsetAndMetadata ;
5050import org .apache .kafka .clients .consumer .OffsetAndTimestamp ;
5151import org .apache .kafka .clients .consumer .OffsetCommitCallback ;
52+ import org .apache .kafka .clients .consumer .RetriableCommitFailedException ;
5253import org .apache .kafka .clients .producer .Producer ;
5354import org .apache .kafka .common .Metric ;
5455import org .apache .kafka .common .MetricName ;
@@ -1201,6 +1202,10 @@ private void wrapUp() {
12011202 * @param e the exception.
12021203 */
12031204 protected void handleConsumerException (Exception e ) {
1205+ if (e instanceof RetriableCommitFailedException ) {
1206+ this .logger .error (e , "Commit retries exhausted" );
1207+ return ;
1208+ }
12041209 try {
12051210 if (!this .isBatchListener && this .errorHandler != null ) {
12061211 this .errorHandler .handle (e , Collections .emptyList (), this .consumer ,
@@ -1280,13 +1285,25 @@ private void ackImmediate(ConsumerRecord<K, V> record) {
12801285 this .producer .sendOffsetsToTransaction (commits , this .consumerGroupId );
12811286 }
12821287 else if (this .syncCommits ) {
1283- this . consumer . commitSync (commits , this . syncCommitTimeout );
1288+ commitSync (commits );
12841289 }
12851290 else {
1286- this . consumer . commitAsync (commits , this . commitCallback );
1291+ commitAsync (commits , 0 );
12871292 }
12881293 }
12891294
1295+ private void commitAsync (Map <TopicPartition , OffsetAndMetadata > commits , int retries ) {
1296+ this .consumer .commitAsync (commits , (offsetsAttempted , exception ) -> {
1297+ if (exception instanceof RetriableCommitFailedException
1298+ && retries < this .containerProperties .getCommitRetries ()) {
1299+ commitAsync (commits , retries + 1 );
1300+ }
1301+ else {
1302+ this .commitCallback .onComplete (offsetsAttempted , exception );
1303+ }
1304+ });
1305+ }
1306+
12901307 private void invokeListener (final ConsumerRecords <K , V > records ) {
12911308 if (this .isBatchListener ) {
12921309 invokeBatchListener (records );
@@ -1848,10 +1865,10 @@ public void ackCurrent(final ConsumerRecord<K, V> record,
18481865 if (producer == null ) {
18491866 this .commitLogger .log (() -> "Committing: " + offsetsToCommit );
18501867 if (this .syncCommits ) {
1851- this . consumer . commitSync (offsetsToCommit , this . syncCommitTimeout );
1868+ commitSync (offsetsToCommit );
18521869 }
18531870 else {
1854- this . consumer . commitAsync (offsetsToCommit , this . commitCallback );
1871+ commitAsync (offsetsToCommit , 0 );
18551872 }
18561873 }
18571874 else {
@@ -2071,10 +2088,10 @@ private void commitIfNecessary() {
20712088 this .commitLogger .log (() -> "Committing: " + commits );
20722089 try {
20732090 if (this .syncCommits ) {
2074- this . consumer . commitSync (commits , this . syncCommitTimeout );
2091+ commitSync (commits );
20752092 }
20762093 else {
2077- this . consumer . commitAsync (commits , this . commitCallback );
2094+ commitAsync (commits , 0 );
20782095 }
20792096 }
20802097 catch (@ SuppressWarnings (UNUSED ) WakeupException e ) {
@@ -2084,6 +2101,22 @@ private void commitIfNecessary() {
20842101 }
20852102 }
20862103
2104+ private void commitSync (Map <TopicPartition , OffsetAndMetadata > commits ) {
2105+ doCommitSync (commits , 0 );
2106+ }
2107+
2108+ private void doCommitSync (Map <TopicPartition , OffsetAndMetadata > commits , int retries ) {
2109+ try {
2110+ this .consumer .commitSync (commits , this .syncCommitTimeout );
2111+ }
2112+ catch (RetriableCommitFailedException e ) {
2113+ if (retries >= this .containerProperties .getCommitRetries ()) {
2114+ throw e ;
2115+ }
2116+ doCommitSync (commits , retries + 1 );
2117+ }
2118+ }
2119+
20872120 private Map <TopicPartition , OffsetAndMetadata > buildCommits () {
20882121 Map <TopicPartition , OffsetAndMetadata > commits = new HashMap <>();
20892122 for (Entry <String , Map <Integer , Long >> entry : this .offsets .entrySet ()) {
@@ -2348,6 +2381,7 @@ private boolean collectAndCommitIfNecessary(Collection<TopicPartition> partition
23482381 return true ;
23492382 }
23502383
2384+ @ SuppressWarnings ("unused" )
23512385 private void commitCurrentOffsets (Map <TopicPartition , OffsetAndMetadata > offsetsToCommit ) {
23522386 ListenerConsumer .this .commitLogger .log (() -> "Committing on assignment: " + offsetsToCommit );
23532387 if (ListenerConsumer .this .transactionTemplate != null
@@ -2387,12 +2421,16 @@ protected void doInTransactionWithoutResult(TransactionStatus status) {
23872421 else {
23882422 ContainerProperties containerProps = KafkaMessageListenerContainer .this .getContainerProperties ();
23892423 if (containerProps .isSyncCommits ()) {
2390- ListenerConsumer .this .consumer .commitSync (offsetsToCommit ,
2391- containerProps .getSyncCommitTimeout ());
2424+ try {
2425+ ListenerConsumer .this .consumer .commitSync (offsetsToCommit ,
2426+ containerProps .getSyncCommitTimeout ());
2427+ }
2428+ catch (RetriableCommitFailedException e ) {
2429+ // ignore since this is on assignment anyway
2430+ }
23922431 }
23932432 else {
2394- ListenerConsumer .this .consumer .commitAsync (offsetsToCommit ,
2395- containerProps .getCommitCallback ());
2433+ commitAsync (offsetsToCommit , 0 );
23962434 }
23972435 }
23982436 }
0 commit comments