-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-28489][SS] Fix a bug that KafkaOffsetRangeCalculator.getRanges may drop offsets #25237
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 all commits
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 |
|---|---|---|
|
|
@@ -61,19 +61,23 @@ private[kafka010] class KafkaOffsetRangeCalculator(val minPartitions: Option[Int | |
|
|
||
| // Splits offset ranges with relatively large amount of data to smaller ones. | ||
| val totalSize = offsetRanges.map(_.size).sum | ||
| val idealRangeSize = totalSize.toDouble / minPartitions.get | ||
|
|
||
| offsetRanges.flatMap { range => | ||
| // Split the current range into subranges as close to the ideal range size | ||
| val numSplitsInRange = math.round(range.size.toDouble / idealRangeSize).toInt | ||
|
|
||
| (0 until numSplitsInRange).map { i => | ||
| val splitStart = range.fromOffset + range.size * (i.toDouble / numSplitsInRange) | ||
| val splitEnd = range.fromOffset + range.size * ((i.toDouble + 1) / numSplitsInRange) | ||
| KafkaOffsetRange( | ||
| range.topicPartition, splitStart.toLong, splitEnd.toLong, preferredLoc = None) | ||
| val tp = range.topicPartition | ||
| val size = range.size | ||
| // number of partitions to divvy up this topic partition to | ||
| val parts = math.max(math.round(size.toDouble / totalSize * minPartitions.get), 1).toInt | ||
| var remaining = size | ||
| var startOffset = range.fromOffset | ||
| (0 until parts).map { part => | ||
| // Fine to do integer division. Last partition will consume all the round off errors | ||
| val thisPartition = remaining / (parts - part) | ||
|
Member
Author
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.
|
||
| remaining -= thisPartition | ||
| val endOffset = math.min(startOffset + thisPartition, range.untilOffset) | ||
| val offsetRange = KafkaOffsetRange(tp, startOffset, endOffset, None) | ||
| startOffset = endOffset | ||
| offsetRange | ||
| } | ||
| } | ||
| }.filter(_.size > 0) | ||
|
Contributor
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. I'm not sure it could be possible, but suppose it could be possible (as we have this, and we are doing integer division), then we still have chance to have less than minPartitions even the calculation on ratio-based distribution is correct. |
||
| } | ||
| } | ||
|
|
||
|
|
||
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.
This one ensures we never drop a TopicPartition.
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.
The ratio calculation looks good, but
roundseems to generate less partitions. Is there a reason to chooseroundinstead ofceiling?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.
Yeah I'm seeing the same. Suppose 4 offsetRanges divide 1 partition for each 0.25, then we lost 1. The number of lost partitions may vary.
In other words, if we use ceil, it may overflow the minimum partitions, and the number of exceeding partitions may vary. We don't guarantee for this calculator to return partitions closest to minimum partitions, so it seems OK.
If we really would like to make this strict, we could apply "allocation" - calculating ratio on each offsetRange, and allocate partitions to each offsetRange according to ratio (apply minimum of 1 for safeness), and allocate extra partitions to some offsetRanges if there're remaining partitions. Not sure we would like to deal with complexity.
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.
Yep, it's a hint. And when the number of partitions is less than
minPartition, we will try our best to split. Agreed that the option nameminPartitionis not accurate.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.
Then, could you update the document instead in a more accurate way?
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.
@dongjoon-hyun I think the doc for this method is accurate :
spark/external/kafka-0-10-sql/src/main/scala/org/apache/spark/sql/kafka010/KafkaOffsetRangeCalculator.scala
Line 38 in c4010a2
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.
I meant the structured streaming Kafka integration~
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.
A few days ago,
minPartitionsis added to the documentation for master/branch-2.4 via #25219 .