-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-8816: Make offsets immutable to users of RecordCollector.offsets #7223
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
c540b38
c51b92c
b44b9af
2e0fd82
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 |
|---|---|---|
|
|
@@ -50,7 +50,10 @@ | |
| import java.util.Map; | ||
| import java.util.concurrent.Future; | ||
|
|
||
| import static org.hamcrest.MatcherAssert.assertThat; | ||
| import static org.hamcrest.Matchers.equalTo; | ||
| import static org.junit.Assert.assertEquals; | ||
| import static org.junit.Assert.assertThrows; | ||
| import static org.junit.Assert.assertTrue; | ||
| import static org.junit.Assert.fail; | ||
|
|
||
|
|
@@ -145,6 +148,31 @@ public void testStreamPartitioner() { | |
| assertEquals((Long) 0L, offsets.get(new TopicPartition("topic1", 2))); | ||
| } | ||
|
|
||
| @Test | ||
| public void shouldNotAllowOffsetsToBeUpdatedExternally() { | ||
| final String topic = "topic1"; | ||
| final TopicPartition topicPartition = new TopicPartition(topic, 0); | ||
|
|
||
| final RecordCollectorImpl collector = new RecordCollectorImpl( | ||
| "RecordCollectorTest-TestSpecificPartition", | ||
| new LogContext("RecordCollectorTest-TestSpecificPartition "), | ||
| new DefaultProductionExceptionHandler(), | ||
| new Metrics().sensor("skipped-records") | ||
| ); | ||
| collector.init(new MockProducer<>(cluster, true, new DefaultPartitioner(), byteArraySerializer, byteArraySerializer)); | ||
|
|
||
| collector.send(topic, "999", "0", null, 0, null, stringSerializer, stringSerializer); | ||
| collector.send(topic, "999", "0", null, 0, null, stringSerializer, stringSerializer); | ||
| collector.send(topic, "999", "0", null, 0, null, stringSerializer, stringSerializer); | ||
|
|
||
| final Map<TopicPartition, Long> offsets = collector.offsets(); | ||
|
|
||
| assertThat(offsets.get(topicPartition), equalTo(2L)); | ||
| assertThrows(UnsupportedOperationException.class, () -> offsets.put(new TopicPartition(topic, 0), 50L)); | ||
|
|
||
|
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. nit: I would remove this line
Contributor
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 would argue for keeping this because the change impacts the external behavior of the class. We're making a strong statement here: you will get an exception if you try to modify the contents of the returned map, you must copy this map if you want to make changes. This also distinguishes from the alternative approach we could have used in which we proactively copy the map for the user and where the user could have made a change to the map while still not impacting the underlying map. Given that this is externally facing and there is doc a couple levels up, I will fix that up. Happy to discuss further if you strongly disagree. |
||
| assertThat(collector.offsets().get(topicPartition), equalTo(2L)); | ||
| } | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| @Test(expected = StreamsException.class) | ||
| public void shouldThrowStreamsExceptionOnAnyExceptionButProducerFencedException() { | ||
|
|
||
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.
Would it be simpler to hand out a deep copy of the map directly?
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.
That would be totally fine for the current usage pattern, where we have a single query that ends up copying the map anyway. However, if we ever ended up with other queries then those queries would pay the cost of the copy whether they need it or not. It would be a bit surprising that a copy is happening without looking at the implementation. My bias would be towards defensive coding without surprise performance impact.
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.
Fair enough.