-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-8615: Change to track partition time breaks TimestampExtractor #7054
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
0ac3cd9
5c39664
b9f3c99
ac606c5
bcd654a
5120315
682ef4c
d134740
91d9d2f
bfa788f
5cd7f22
7c66617
22f6f09
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 |
|---|---|---|
|
|
@@ -41,8 +41,8 @@ | |
| * | ||
| * PartitionGroup also maintains a stream-time for the group as a whole. | ||
| * This is defined as the highest timestamp of any record yet polled from the PartitionGroup. | ||
| * The PartitionGroup's stream-time is also the stream-time of its task and is used as the | ||
| * stream-time for any computations that require it. | ||
| * Note however that any computation that depends on stream-time should track it on a per-operator basis to obtain an | ||
| * accurate view of the local time as seen by that processor. | ||
| * | ||
| * The PartitionGroups's stream-time is initially UNKNOWN (-1), and it set to a known value upon first poll. | ||
| * As a consequence of the definition, the PartitionGroup's stream-time is non-decreasing | ||
|
|
@@ -76,7 +76,7 @@ RecordQueue queue() { | |
| } | ||
|
|
||
| PartitionGroup(final Map<TopicPartition, RecordQueue> partitionQueues, final Sensor recordLatenessSensor) { | ||
| nonEmptyQueuesByTime = new PriorityQueue<>(partitionQueues.size(), Comparator.comparingLong(RecordQueue::timestamp)); | ||
| nonEmptyQueuesByTime = new PriorityQueue<>(partitionQueues.size(), Comparator.comparingLong(RecordQueue::headRecordTimestamp)); | ||
| this.partitionQueues = partitionQueues; | ||
| this.recordLatenessSensor = recordLatenessSensor; | ||
| totalBuffered = 0; | ||
|
|
@@ -109,7 +109,7 @@ record = queue.poll(); | |
| nonEmptyQueuesByTime.offer(queue); | ||
| } | ||
|
|
||
| // always update the stream time to the record's timestamp yet to be processed if it is larger | ||
| // always update the stream-time to the record's timestamp yet to be processed if it is larger | ||
| if (record.timestamp > streamTime) { | ||
| streamTime = record.timestamp; | ||
| recordLatenessSensor.record(0); | ||
|
|
@@ -140,8 +140,8 @@ int addRawRecords(final TopicPartition partition, final Iterable<ConsumerRecord< | |
| nonEmptyQueuesByTime.offer(recordQueue); | ||
|
|
||
| // if all partitions now are non-empty, set the flag | ||
| // we do not need to update the stream time here since this task will definitely be | ||
| // processed next, and hence the stream time will be updated when we retrieved records by then | ||
| // we do not need to update the stream-time here since this task will definitely be | ||
| // processed next, and hence the stream-time will be updated when we retrieved records by then | ||
| if (nonEmptyQueuesByTime.size() == this.partitionQueues.size()) { | ||
| allBuffered = true; | ||
| } | ||
|
|
@@ -157,10 +157,9 @@ public Set<TopicPartition> partitions() { | |
| } | ||
|
|
||
| /** | ||
| * Return the timestamp of this partition group as the smallest | ||
| * partition timestamp among all its partitions | ||
| * Return the stream-time of this partition group defined as the largest timestamp seen across all partitions | ||
| */ | ||
| public long timestamp() { | ||
| public long streamTime() { | ||
|
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. Should we rename this this
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. I'm in favor of that in theory -- but, then do we also rename
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. Let's just keep it as |
||
| return streamTime; | ||
| } | ||
|
|
||
|
|
@@ -192,6 +191,7 @@ public void close() { | |
|
|
||
| public void clear() { | ||
| nonEmptyQueuesByTime.clear(); | ||
| streamTime = RecordQueue.UNKNOWN; | ||
| for (final RecordQueue queue : partitionQueues.values()) { | ||
| queue.clear(); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,8 +31,8 @@ | |
|
|
||
| /** | ||
| * RecordQueue is a FIFO queue of {@link StampedRecord} (ConsumerRecord + timestamp). It also keeps track of the | ||
| * partition timestamp defined as the minimum timestamp of records in its queue; in addition, its partition | ||
| * timestamp is monotonically increasing such that once it is advanced, it will not be decremented. | ||
| * partition timestamp defined as the largest timestamp seen on the partition so far; this is passed to the | ||
| * timestamp extractor. | ||
| */ | ||
| public class RecordQueue { | ||
|
|
||
|
|
@@ -47,6 +47,7 @@ public class RecordQueue { | |
| private final ArrayDeque<ConsumerRecord<byte[], byte[]>> fifoQueue; | ||
|
|
||
|
ableegoldman marked this conversation as resolved.
|
||
| private StampedRecord headRecord = null; | ||
| private long partitionTime = RecordQueue.UNKNOWN; | ||
|
|
||
| private Sensor skipRecordsSensor; | ||
|
|
||
|
|
@@ -139,20 +140,30 @@ public boolean isEmpty() { | |
| } | ||
|
|
||
| /** | ||
| * Returns the tracked partition timestamp | ||
| * Returns the head record's timestamp | ||
| * | ||
| * @return timestamp | ||
| */ | ||
| public long timestamp() { | ||
| public long headRecordTimestamp() { | ||
| return headRecord == null ? UNKNOWN : headRecord.timestamp; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the tracked partition time | ||
| * | ||
| * @return partition time | ||
| */ | ||
| long partitionTime() { | ||
| return partitionTime; | ||
| } | ||
|
|
||
| /** | ||
| * Clear the fifo queue of its elements, also clear the time tracker's kept stamped elements | ||
| */ | ||
| public void clear() { | ||
| fifoQueue.clear(); | ||
| headRecord = null; | ||
| partitionTime = RecordQueue.UNKNOWN; | ||
| } | ||
|
|
||
| private void updateHead() { | ||
|
|
@@ -167,7 +178,7 @@ private void updateHead() { | |
|
|
||
| final long timestamp; | ||
| try { | ||
| timestamp = timestampExtractor.extract(deserialized, timestamp()); | ||
| timestamp = timestampExtractor.extract(deserialized, partitionTime); | ||
|
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. Can we also update method
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. Similar, can we piggy-back some cleanup to
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. Ack to all...except the last point. We do check both for null..?
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. Seems, we check both for But I think they cannot be
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. Sorry, misunderstood your question. Yes, either one could potentially be null if we don't yet have new records to process?
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. Good point.
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 think I agree the second null check should never happen. |
||
| } catch (final StreamsException internalFatalExtractorException) { | ||
| throw internalFatalExtractorException; | ||
| } catch (final Exception fatalUserException) { | ||
|
|
@@ -189,6 +200,8 @@ private void updateHead() { | |
| } | ||
|
|
||
| headRecord = new StampedRecord(deserialized, timestamp); | ||
|
|
||
| partitionTime = Math.max(partitionTime, timestamp); | ||
| } | ||
| } | ||
| } | ||
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.
Maybe we should change "stream-time" to "task-time" ? @vvcephei suggested it (and I like it) in an in-person discussion? If we agree, also the corresponding variable and method names should be updated. Thoughts?
Should it be "stream-time" or "stream time" ?
Uh oh!
There was an error while loading. Please reload this page.
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 support "task-time" -- would be good to distinguish from "stream time" should we ever want/need a "global" stream time. That said, we use stream time all over including things like PunctuationType. Will it be confusing to have
maybePuncuateStreamTime()punctuate on something called task-time? Though we already refer to it separately as "partition group timestamp", "stream partition time", AND "stream time" in that method..Regarding "stream-time" vs "stream time" -- we use the hyphen when referring to types of time semantics (eg event-time) so I'd favor "stream time" to maintain a distinction between "semantics types" and "time definitions" -- WDYT?
Uh oh!
There was an error while loading. Please reload this page.
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.
On the other hand, maybe phrases like "lowest task time" might be ambiguous (what does "lowest task" mean and why do we care about its time?) so I'm good with task-time.