-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-7211: MM should handle TimeoutException in commitSync #5492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
0defde1
0228e18
b10c6ac
b0c33b7
e02bf6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,12 +33,13 @@ import org.apache.kafka.clients.producer.internals.ErrorLoggingCallback | |
| import org.apache.kafka.clients.producer.{KafkaProducer, ProducerConfig, ProducerRecord, RecordMetadata} | ||
| import org.apache.kafka.common.{KafkaException, TopicPartition} | ||
| import org.apache.kafka.common.serialization.{ByteArrayDeserializer, ByteArraySerializer} | ||
| import org.apache.kafka.common.utils.Utils | ||
| import org.apache.kafka.common.errors.WakeupException | ||
| import org.apache.kafka.common.utils.{Time, Utils} | ||
| import org.apache.kafka.common.errors.{TimeoutException, WakeupException} | ||
| import org.apache.kafka.common.record.RecordBatch | ||
|
|
||
| import scala.collection.JavaConverters._ | ||
| import scala.collection.mutable.HashMap | ||
| import scala.util.{Success, Try} | ||
| import scala.util.control.ControlThrowable | ||
|
|
||
| /** | ||
|
|
@@ -69,6 +70,8 @@ object MirrorMaker extends Logging with KafkaMetricsGroup { | |
| private var offsetCommitIntervalMs = 0 | ||
| private var abortOnSendFailure: Boolean = true | ||
| @volatile private var exitingOnSendFailure: Boolean = false | ||
| private var lastSuccessfulCommitTime = -1L | ||
| private val time = Time.SYSTEM | ||
|
|
||
| // If a message send failed after retries are exhausted. The offset of the messages will also be removed from | ||
| // the unacked offset list to avoid offset commit being stuck on that offset. In this case, the offset of that | ||
|
|
@@ -267,19 +270,34 @@ object MirrorMaker extends Logging with KafkaMetricsGroup { | |
| consumers.map(consumer => new ConsumerWrapper(consumer, customRebalanceListener, whitelist)) | ||
| } | ||
|
|
||
| def commitOffsets(consumerWrapper: ConsumerWrapper) { | ||
| def commitOffsets(consumerWrapper: ConsumerWrapper, retry: Int = Integer.MAX_VALUE): Unit = { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that |
||
| if (!exitingOnSendFailure) { | ||
| trace("Committing offsets.") | ||
| try { | ||
| consumerWrapper.commit() | ||
| lastSuccessfulCommitTime = time.milliseconds | ||
| } catch { | ||
| case e: WakeupException => | ||
| // we only call wakeup() once to close the consumer, | ||
| // so if we catch it in commit we can safely retry | ||
| // and re-throw to break the loop | ||
| consumerWrapper.commit() | ||
| commitOffsets(consumerWrapper, retry) | ||
| throw e | ||
|
|
||
| case _: TimeoutException if retry > 0 => | ||
| if (retry == Integer.MAX_VALUE) { // only try to remove offsets for nonexistent topics once | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if topic deletion happens after a few retries? It seems safer to to filter topic for every retry. It is probably OK because the time used to filter topic in MM should be much smaller than the time needed for backoff and the time needed to wait for offset commit to pass, right? |
||
| Try(consumerWrapper.consumer.listTopics) match { | ||
| case Success(visibleTopics) => | ||
| consumerWrapper.offsets.retain((tp, _) => visibleTopics.containsKey(tp.topic)) | ||
| case _ => | ||
| } | ||
| } | ||
| warn("Failed to commit offsets because the offset commit request processing can not be completed in time. " + | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to the concern of data loss, would it be useful to also keep track and log the time of last successful commit in the warning message, so that SRE can gauge how much time worth of data has been duplicated? |
||
| s"If you see this regularly, it could indicate that you need to increase the consumer's ${ConsumerConfig.DEFAULT_API_TIMEOUT_MS_CONFIG} " + | ||
| s"Last successful offset commit timestamp=${lastSuccessfulCommitTime}, retry count=${Integer.MAX_VALUE - retry}") | ||
| Thread.sleep(100) | ||
| commitOffsets(consumerWrapper, retry - 1) | ||
|
|
||
| case _: CommitFailedException => | ||
| warn("Failed to commit offsets because the consumer group has rebalanced and assigned partitions to " + | ||
| "another instance. If you see this regularly, it could indicate that you need to either increase " + | ||
|
|
@@ -422,14 +440,15 @@ object MirrorMaker extends Logging with KafkaMetricsGroup { | |
| } | ||
|
|
||
| // Visible for testing | ||
| private[tools] class ConsumerWrapper(consumer: Consumer[Array[Byte], Array[Byte]], | ||
| private[tools] class ConsumerWrapper(private[tools] val consumer: Consumer[Array[Byte], Array[Byte]], | ||
| customRebalanceListener: Option[ConsumerRebalanceListener], | ||
| whitelistOpt: Option[String]) { | ||
| val regex = whitelistOpt.getOrElse(throw new IllegalArgumentException("New consumer only supports whitelist.")) | ||
| var recordIter: java.util.Iterator[ConsumerRecord[Array[Byte], Array[Byte]]] = null | ||
|
|
||
| // We manually maintain the consumed offsets for historical reasons and it could be simplified | ||
| private val offsets = new HashMap[TopicPartition, Long]() | ||
| // Visible for testing | ||
| private[tools] val offsets = new HashMap[TopicPartition, Long]() | ||
|
|
||
| def init() { | ||
| debug("Initiating consumer") | ||
|
|
@@ -473,7 +492,7 @@ object MirrorMaker extends Logging with KafkaMetricsGroup { | |
| } | ||
|
|
||
| def commit() { | ||
| consumer.commitSync(offsets.map { case (tp, offset) => (tp, new OffsetAndMetadata(offset, ""))}.asJava) | ||
| consumer.commitSync(offsets.map { case (tp, offset) => (tp, new OffsetAndMetadata(offset)) }.asJava) | ||
| offsets.clear() | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not related to this PR. Can we update the comments above:
"There are N mirror maker threads each having one KafkaConsumer instance" ?