-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-13794: Fix comparator of inflightBatchesBySequence in TransactionManager #11991
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
Merged
hachikuji
merged 2 commits into
apache:trunk
from
ddrid:fix_comparator_in_transactionManager
Apr 5, 2022
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -674,6 +674,68 @@ public void testBatchCompletedAfterProducerReset() { | |
| assertNull(transactionManager.nextBatchBySequence(tp0)); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDuplicateSequenceAfterProducerReset() throws Exception { | ||
| initializeTransactionManager(Optional.empty()); | ||
| initializeIdempotentProducerId(producerId, epoch); | ||
|
|
||
| Metrics metrics = new Metrics(time); | ||
| final int requestTimeout = 10000; | ||
| final int deliveryTimeout = 15000; | ||
|
|
||
| RecordAccumulator accumulator = new RecordAccumulator(logContext, 16 * 1024, CompressionType.NONE, 0, 0L, | ||
| deliveryTimeout, metrics, "", time, apiVersions, transactionManager, | ||
| new BufferPool(1024 * 1024, 16 * 1024, metrics, time, "")); | ||
|
|
||
| Sender sender = new Sender(logContext, this.client, this.metadata, accumulator, false, | ||
| MAX_REQUEST_SIZE, ACKS_ALL, MAX_RETRIES, new SenderMetricsRegistry(metrics), this.time, requestTimeout, | ||
| 0, transactionManager, apiVersions); | ||
|
|
||
| assertEquals(0, transactionManager.sequenceNumber(tp0).intValue()); | ||
|
|
||
| Future<RecordMetadata> responseFuture1 = accumulator.append(tp0, time.milliseconds(), "1".getBytes(), "1".getBytes(), Record.EMPTY_HEADERS, | ||
| null, MAX_BLOCK_TIMEOUT, false, time.milliseconds()).future; | ||
| sender.runOnce(); | ||
| assertEquals(1, transactionManager.sequenceNumber(tp0).intValue()); | ||
|
|
||
| time.sleep(requestTimeout); | ||
| sender.runOnce(); | ||
| assertEquals(0, client.inFlightRequestCount()); | ||
| assertTrue(transactionManager.hasInflightBatches(tp0)); | ||
| assertEquals(1, transactionManager.sequenceNumber(tp0).intValue()); | ||
| sender.runOnce(); // retry | ||
| assertEquals(1, client.inFlightRequestCount()); | ||
| assertTrue(transactionManager.hasInflightBatches(tp0)); | ||
| assertEquals(1, transactionManager.sequenceNumber(tp0).intValue()); | ||
|
|
||
| time.sleep(5000); // delivery time out | ||
| sender.runOnce(); | ||
|
|
||
| // The retried request will remain inflight until the request timeout | ||
| // is reached even though the delivery timeout has expired and the | ||
| // future has completed exceptionally. | ||
| assertTrue(responseFuture1.isDone()); | ||
| TestUtils.assertFutureThrows(responseFuture1, TimeoutException.class); | ||
| assertFalse(transactionManager.hasInFlightRequest()); | ||
|
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. The behavior here puzzled me a little when I was trying to understand the test. Would a comment like this help? |
||
| assertEquals(1, client.inFlightRequestCount()); | ||
|
|
||
| sender.runOnce(); // bump the epoch | ||
| assertEquals(epoch + 1, transactionManager.producerIdAndEpoch().epoch); | ||
| assertEquals(0, transactionManager.sequenceNumber(tp0).intValue()); | ||
|
|
||
| Future<RecordMetadata> responseFuture2 = accumulator.append(tp0, time.milliseconds(), "2".getBytes(), "2".getBytes(), Record.EMPTY_HEADERS, | ||
| null, MAX_BLOCK_TIMEOUT, false, time.milliseconds()).future; | ||
| sender.runOnce(); | ||
| sender.runOnce(); | ||
| assertEquals(0, transactionManager.firstInFlightSequence(tp0)); | ||
| assertEquals(1, transactionManager.sequenceNumber(tp0).intValue()); | ||
|
|
||
| time.sleep(5000); // request time out again | ||
| sender.runOnce(); | ||
| assertTrue(transactionManager.hasInflightBatches(tp0)); // the latter batch failed and retried | ||
| assertFalse(responseFuture2.isDone()); | ||
| } | ||
|
|
||
| private ProducerBatch writeIdempotentBatchWithValue(TransactionManager manager, | ||
| TopicPartition tp, | ||
| String value) { | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Wouldn't this violate the requirements for the
comparemethod?Objects that are not equal need to have a stable order otherwise, binary search may not find the objects.
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.
Hi @artemlivshits, thanks for your comment. I don't think it violate the requirements for the
comparemethod since we are comparing two batches using an integer.As for the stable order, I think it doesn't affect the current code, but I can fix this in another pr if you regard it necessary. What do you think?
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.
Say we have 2 batches b1 and b2 that have the same base sequence, but are not equal. Then
compare(b1, b2) == 1andcompare(b2, b1) == 1, which violates the requirement of changing the sign when the argument order is changed.This property is used in binary search tree to order and search elements, if it's violated, then we may not find the element because we follow the wrong branch. Say we have some elements in logical order
a, b1, b2, x, ywhen we ordered them we didcompare(b2, b1), which returned 1 meaning that b2 is greater than b1. When we search for b1, we may start with b2 and usecompare(b1, b2), which would return 1 meaning that b1 is greater than b2, and we continue searching in thex, ypart and conclude that it's not there.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.
@artemlivshits Thanks for the explanation! I think it make sense. I will open another pr to fix it
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.
Nice catch! @artemlivshits !